示例效果

 这是3D可视化教程系列的文章,如果第一次阅读请先阅读《3D可视化教程导读》,这里展示简单的模型拆解效果,可访问a-dismantling 展示网址

 华为的网站上也有这种拆解示例,比我做的例子好看多了(毕竟我的模型是网上找的,该模型的物体分组有点问题,而我又懒得自己做一个,但并不影响大家了解学习。),原理基本上是一样的。华为服务器拆解示例

源码及3D项目文件

  源码及工程项目都放到github上。
  源码:threejs-example

原理

 拆解模型的原理就在于,让各个模型向不同方向移动。
 移动时当然是一个过程,不是“瞬间移动到另一个位置”一个瞬间事件,而是“慢慢地从这个位置移动到另一个位置上”由多个瞬间事件组成的渐变过程。使用tween.js库能很方便实现这种渐变过程,比如从0到1,它会自动拆成”0,0.12,0.22,0.33 …. 0.99,1”,多个瞬间事件,在回调里改变对应的位置即可:

1
2
3
4
5
6
7
8
9
10
// 移动函数
function move(obj,position) {
new TWEEN.Tween(obj.position)
.to(position, 1000)
.onUpdate(function (val) {
obj.position.set(val.x || 0, val.y || 0, val.z || 0);
})
.start();
}


 在模型制作上,必须是包含若干个子模型,这样才能分别控制不同的子模型做不同的事。
最小单元模型
 可以看到,这个机箱的3D模型是包含了子模型,使用scene.getObjectByName("Object_7")就能得到Object_7这个模型,再设置其位置属性就可以实现位置变化obj.position.set(x,y,z)。最后让各个模块向各个方向移动即可做到拆解效果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

// 拆解按钮
document.getElementById("dismantling").onclick=()=>{
move(scene.getObjectByName("Object_7"),{x:-5});
move(scene.getObjectByName("Object_18"),{x:-5});

move(scene.getObjectByName("Object_10"),{x:5});
move(scene.getObjectByName("Object_11"),{x:5});
move(scene.getObjectByName("Object_17"),{x:5});

move(scene.getObjectByName("Object_27"),{z:5});
move(scene.getObjectByName("Object_29"),{z:5});

move(scene.getObjectByName("Object_14"),{z:-5});
move(scene.getObjectByName("Object_16"),{z:-5});

move(scene.getObjectByName("Object_28"),{y:2});
};

 拆解效果至此就完成了,十分简单。

其它

 整个项目还用到其它功能,在three example上都能找到例子,不再细说,点右下角可以看到源码:

  • 用于实现移动视角的orbitControl
  • 用于识别选中物体的raycast
  • 用于描边物体的outline
  • 显示物体名称的原理是:如果检测到选中物体,就修改HTML对应的样式与内容:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    		<style>
    #name-box{
    position:absolute;
    color: white;
    background-color: rgba(15, 15, 200, 0.774);
    font-size: 32px;
    font-weight: 600;
    pointer-events: none;
    padding: 15px;
    border-radius: 10px;
    }
    </style>

    <div id="name-box">test</div>
    1
    2
    3
    4
    5
    6
    const nameBox = document.getElementById("name-box");
    // 名称提示
    nameBox.innerHTML = intersection[0].object.name.replace("Object_","零件");
    nameBox.style.display = "block";
    nameBox.style.top = mousePosition.y; // 跟随鼠标的位置
    nameBox.style.left = mousePosition.x + 30;

附录