之前的一个数据可视化大屏 项目开发中,有一个要求是需要进行3d环绕旋转的效果,这个效果之前有个案例demo中看到过,是通过css的matrix3d方式实现的,css实现方式,但是这个demo的css太长了,如果按照这个来写的话,实在是困难,首先每一个的css都特别长,写10几个的话,那工作量就很大了,假如中间客户要新增一个或者减少一个又要增加不少工作量,于是想到使用threejs来实现。
演示效果:threejs 3d环绕旋转动画效果实例
动态效果
首先绘制轨道
根据可视化大屏ui设计图来看的话,有5条轨道,3条轨道上有内容,那么我们先绘制轨道,通过ArcCurve绘制弧形曲线,然后通过LineLoop连接起来绘制成一个闭合的轨道。
circle(r) {
// ArcCurve创建一个圆弧曲线
var arc = new THREE.ArcCurve(0, 0, r, 0, 2 * Math.PI, true);
// 获取点数越多,圆弧越光滑
var points = arc.getPoints(100); //返回一个vector2对象作为元素组成的数组
// console.log('points', points);
var geometry = new THREE.BufferGeometry();
//setFromPoints方法的本质:遍历points把vector2转变化vector3
geometry.setFromPoints(points);
var material = new THREE.LineBasicMaterial({
color: 0x0f4984,
linewidth: 1
});
// THREE.Line
var line = new THREE.LineLoop(geometry, material); //起始点闭合
// 圆弧线默认在XOY平面上,绕x轴旋转到XOZ平面上
line.rotateX(Math.PI / 2);
scene.add(line);
}
在轨道上添加物体
因为threejs还不够熟悉,不知道怎么直接在场景中直接添加旋转的css2d,所以我们这里创建网格模型,然后css2d获取网格模型的坐标。
创建网格模型
创建透明的网格模型,用于css2d获取模型的位置
var geometry = new THREE.SphereGeometry(1, 0, 10);
var material = new THREE.MeshBasicMaterial({
// 设置颜色贴图
color: 0x00ffff,
transparent: true,
opacity: 0,
}); //材质对象Material
var mesh = new THREE.Mesh(geometry, material); //网格模型对象Mesh
mesh.name = type.title
scene.add(mesh); //网格模型添加到场景中
创建css2d
创建css2d,获取模型的坐标,通过模型的坐标获取世界坐标,渲染出来。
var group = scene.getObjectByName(type.title)
var label = that.tag(group.title, type, 0)//把名称obj.name作为标签
var pos = new THREE.Vector3()
group.getWorldPosition(pos)//获取obj世界坐标、
label.position.copy(pos)//标签标注在obj世界坐标
scene.add(label)//标签插入model组对象中
css2d我们创建后,需要监听,当鼠标放在了css2d上就需要停止环绕动画,鼠标移开后css2d继续环绕,所以我们需要添加一个标识,用来判断是否需要进行滚动。
tag(name, data, index) {
var that = this;
// 创建div元素(作为标签)
var div = document.createElement('div')
div.innerHTML = `<a class="point" href="${data.url}" target="_blank">
<span>${data.title}</span>
</a>`
div.addEventListener('mouseover', function (e) {
that.isRoll[index] = false
})
div.addEventListener('mouseout', function (e) {
that.isRoll[index] = true
})
div.classList.add('tag')
//div元素包装为CSS2模型对象CSS2DObject
var label = new CSS2DObject(div)
div.style.pointerEvents = 'none'//避免HTML标签遮挡三维场景的鼠标事件
// 设置HTML元素标签在three.js世界坐标中位置
// label.position.set(x, y, z);
return label//返回CSS2模型标签
},
坐标更新
通过事先设置的速度参数我们来设置每次变化的角度来控制模型在轨道上的速度,通过然后通过sin cos来计算模型在x轴z轴的位置,css2d获取模型的位置获取世界坐标刷新自身的位置。就达到了旋转动画的效果了。
that.listMesh.forEach((type, index) => {
that.listAngle[index] -= that.isRoll[0] ? that.speed[0].num : 0; // 每次执行render角度新增加
var x = that.huanList[0].R * Math.sin(that.listAngle[index]); //地球x坐标计算
var z = that.huanList[0].R * Math.cos(that.listAngle[index]); //地球z坐标计算
// 设置地球xz坐标,实现公转动画
type.position.set(x, 0, z);
var pos = new THREE.Vector3()
type.getWorldPosition(pos)//获取obj世界坐标、
that.listLabel[index].position.copy(pos)//标签标注在obj世界坐标
});
监听窗口变化添加自适应
mounted() {
var that = this;
const viewElem = document.body;
setTimeout(() => {
this.getData()
}, 300)
// 监听窗口变化
const resizeObserver = new ResizeObserver(() => {
setTimeout(() => {
that.getresize();
}, 300)
});
resizeObserver.observe(viewElem);
},
getresize() {
var that = this;
this.width = this.$refs.canvasGLTF.offsetWidth
this.height = this.$refs.canvasGLTF.offsetHeight
that.renderer.setSize(that.width, that.height);
that.labelRenderer.setSize(this.width, this.height)
},
更新记录
2023年05月20日
添加窗口自适应
2024年05月29日
删除多余依赖
更多three实例
完整实例代码
项目基于vue3+vite javascript 方式编写,请确保有一定的代码基础,再购买
运行环境 nodejs 14 尽量运行环境一致避免出现奇怪的错误