效果
说明:实现模型加载完成后,对其进行爆炸分解,然后延迟还原。
思路:
1.获取到obj模型后,初始化爆炸数据保存到每个mesh的userdata上;
代码:
// 初始化爆炸数据保存到每个mesh的userdata上
initExplodeModel (modelObject) {
let _this = this;
if (!modelObject) return;
// 计算模型中心
const explodeBox = new THREE.Box3();
explodeBox.setFromObject(modelObject);
const explodeCenter = this.getWorldCenterPosition(explodeBox);
const meshBox = new THREE.Box3();
// 遍历整个模型,保存数据到userData上,以便爆炸函数使用
modelObject.traverse(function (value) {
if (value.isMark || value.isMarkChild || value.isLine || value.isSprite) return;
if (value.isMesh) {
meshBox.setFromObject(value);
const meshCenter = _this.getWorldCenterPosition(meshBox);
// 爆炸方向
value.userData.worldDir = new THREE.Vector3()
.subVectors(meshCenter, explodeCenter)
.normalize();
// 爆炸距离 mesh中心点到爆炸中心点的距离
value.userData.worldDistance = new THREE.Vector3().subVectors(meshCenter, explodeCenter);
// 原始坐标
value.userData.originPosition = value.getWorldPosition(new THREE.Vector3());
// mesh中心点
value.userData.meshCenter = meshCenter.clone();
value.userData.explodeCenter = explodeCenter.clone();
}
});
},
2.获取模型的中心坐标(很重要,不然分解出来很奇怪)
// 获取模型中心点
getWorldCenterPosition (box, scalar = 0.5) {
return new THREE.Vector3().addVectors(box.max, box.min).multiplyScalar(scalar);
},
3.设置爆炸值与方向
// 设置爆炸值,scalar=1为初始值
explodeModel (model, scalar) {
model.traverse(function (value) {
// @ts-ignore
if (!value.isMesh || !value.userData.originPosition) return;
const distance = value.userData.worldDir
.clone()
.multiplyScalar(value.userData.worldDistance.length() * scalar);
const offset = new THREE.Vector3().subVectors(
value.userData.meshCenter,
value.userData.originPosition
);
const center = value.userData.explodeCenter;
const newPos = new THREE.Vector3().copy(center).add(distance).sub(offset);
const localPosition = value.parent?.worldToLocal(newPos.clone());
localPosition && value.position.copy(localPosition);
});
},
1.点击调用:单单实现爆炸效果
this.explodeModel(scene, 1);
默认1为初始值,就是模型未爆炸的样子,数值越大,爆炸分解的越狠
2.模型加载完成后旋转并过渡爆炸,延迟两秒后再逐步还原,停止旋转;
//调用
this.sceneRange(1)
//爆炸动画
sceneRange (num) {
//开启模型自动旋转
this.controls.autoRotate = true;
//禁止对模型的操作
this.controls.enabled = false;
let time = setInterval(() => {
if (num >= 1.7) {
setTimeout(() => {
let time2 = setInterval(() => {
if (num <= 1) {
window.clearInterval(time2)
// 关闭旋转,可操作
this.controls.autoRotate = false;
this.controls.enabled = true;
this.explodeModel(scene, 1);
// 初始化相机视角
// this.initPoint();
return
}
num -= 0.005;
this.explodeModel(scene, num)
}, 10)
}, 1500)
window.clearInterval(time)
return
}
num += 0.01;
this.explodeModel(scene, num)
}, 1)
},
模型开启自旋转:
// 控制器操作
initOrbitControls () {
// let allControls = new OrbitControls(camera, HTMLDOMElement);
let allControls = new OrbitControls(camera, renderer.domElement);
allControls.enableDamping = true;
allControls.enableZoom = true;// 是否可以缩放 默认是true
allControls.autoRotate = true;//是否旋转
allControls.autoRotateSpeed = 7;//开启旋转时的旋转速度
allControls.enablePan = true;// 是否开启右键拖拽
allControls.enableKeys = true;
allControls.keyPanSpeed = 7;//使用键盘控制模型位置的相机平移像素
allControls.dampingFactor = 0.5;// 动态阻尼系数 就是鼠标拖拽旋转灵敏度,阻尼越小越灵敏
allControls.keys = {
LEFT: 37,
UP: 38,
RIGHT: 39,
BOTTOM: 40
}
this.controls = allControls;
},
这一步很重要,要开启控制器方法
总结:在处理爆炸的定时器方法中,还没想到更好的处理方式,欢迎补充。还有这个封装的爆炸方法,不只是obj模型可用,基本上都能通用。