基于Three.js实现圆柱端点沿自定义路径动画的NEWUPDATE函数需求
实现Three.js圆柱端点沿自定义路径运动的高效方案
问题概述
现有基于Three.js的旋转圆柱实现,通过UPDATE函数将单位圆柱变换为指定两个端点的圆柱。但当需要让圆柱一端沿模长不固定的复杂自定义路径运动时,原旋转变换思路不再适用,需实现NEWUPDATE函数,要求不销毁重建圆柱以保证性能,调用逻辑如下:
const Path = [ new THREE.Vector3(1, 0, 0), new THREE.Vector3(1, 2, 3), // 更多自定义路径点 ]; let k = 0; function animate() { requestAnimFrame(animate); NEWUPDATE(geomCylinder, Path[k]); k++; render(); }
原参考代码
核心动画与UPDATE函数
const O = new THREE.Vector3(0, 0, 0); const alpha = Math.PI / 180; const Malpha = new THREE.Vector3(Math.sin(alpha), Math.cos(alpha), 0); function animate() { requestAnimFrame(animate); UPDATE(geomCylinder, O, J); UPDATE(geomCylinder, O, Malpha); render(); }
const radius = 0.07; // 单位圆柱:底面中心分别位于(0,0,0)和(0,1,0) const geomUnitCylinder = new THREE.CylinderGeometry(radius, radius, 1, 64, 1, true); geomUnitCylinder.translate(0, 0.5, 0); const J = new THREE.Vector3(0, 1, 0); // 将单位圆柱变换为端点在P、Q的圆柱 function UPDATE(geomCylinder, P, Q) { const vec = Q.clone().sub(P); const height = vec.length(); vec.normalize(); const quaternion = new THREE.Quaternion().setFromUnitVectors(J, vec); geomCylinder.scale(1, height, 1); geomCylinder.applyQuaternion(quaternion); geomCylinder.translate(P.x, P.y, P.z); }
完整场景代码
JavaScript
const radius = 0.07; // 单位圆柱:底面中心分别位于(0,0,0)和(0,1,0) const geomUnitCylinder = new THREE.CylinderGeometry( radius, radius, 1, 64, 1, true ); geomUnitCylinder.translate(0, 0.5, 0); const J = new THREE.Vector3(0, 1, 0); // 将单位圆柱变换为端点在P、Q的圆柱 function UPDATE(geomCylinder, P, Q) { const vec = Q.clone().sub(P); const height = vec.length(); vec.normalize(); const quaternion = new THREE.Quaternion().setFromUnitVectors(J, vec); geomCylinder.scale(1, height, 1); geomCylinder.applyQuaternion(quaternion); geomCylinder.translate(P.x, P.y, P.z); } // Three.js 场景初始化 const aspect = window.innerWidth / window.innerHeight; const camera = new THREE.PerspectiveCamera(50, aspect, 1, 10000); const scene = new THREE.Scene(); scene.background = new THREE.Color(0x111111); const renderer = new THREE.WebGLRenderer({ antialias: true }); renderer.setPixelRatio(window.devicePixelRatio); renderer.setSize(window.innerWidth, window.innerHeight); document.getElementById("container").appendChild(renderer.domElement); camera.position.z = 5; scene.add(camera); function render() { renderer.render(scene, camera); } const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 }); const geomCylinder = geomUnitCylinder.clone(); const mesh = new THREE.Mesh(geomCylinder, material); const object = new THREE.Object3D(); object.add(mesh); scene.add(object); // 自定义requestAnimFrame实现 window.requestAnimFrame = (() => { return (callback) => { window.setTimeout(callback, 1000 / 1); }; })(); // 原动画逻辑 const O = new THREE.Vector3(0, 0, 0); const alpha = Math.PI / 180; const Malpha = new THREE.Vector3(Math.sin(alpha), Math.cos(alpha), 0); function animate() { requestAnimFrame(animate); UPDATE(geomCylinder, O, J); UPDATE(geomCylinder, O, Malpha); render(); } animate();
HTML
<!-- 引入Three.js 0.151.2版本库文件 --> <script src="three.min.js"></script> <div id="container"></div>
解决方案:NEWUPDATE函数实现
核心思路
每次更新前将几何体重置为初始单位圆柱状态,避免多次变换叠加导致的变形;然后根据当前路径点动态计算圆柱的高度、朝向,并完成变换。以下实现默认圆柱一端固定在原点(0,0,0),若需固定其他点,只需修改对应平移参数即可。
NEWUPDATE函数代码
function NEWUPDATE(geomCylinder, targetPoint) { // 重置几何体到初始单位圆柱状态 geomCylinder.copy(geomUnitCylinder); // 计算原点到目标点的向量 const vec = targetPoint.clone(); const height = vec.length(); // 归一化向量,计算旋转四元数(将单位圆柱的Y轴朝向目标方向) vec.normalize(); const quaternion = new THREE.Quaternion().setFromUnitVectors(J, vec); // 应用缩放、旋转、平移变换 geomCylinder.scale(1, height, 1); geomCylinder.applyQuaternion(quaternion); // 若固定点不是原点,替换为固定点坐标即可 geomCylinder.translate(0, 0, 0); }
适配后的动画逻辑
// 定义自定义路径 const Path = [ new THREE.Vector3(1, 0, 0), new THREE.Vector3(1, 2, 3), new THREE.Vector3(-2, 1, 1), new THREE.Vector3(0, -1, 2), // 可添加更多路径点 ]; let k = 0; function animate() { requestAnimFrame(animate); // 循环遍历路径点,避免索引越界 if (k >= Path.length) k = 0; NEWUPDATE(geomCylinder, Path[k]); k++; render(); } animate();
关键优化说明
- 状态重置:通过
geomCylinder.copy(geomUnitCylinder)直接复用预定义的单位圆柱,无需重建几何体,保证性能的同时避免累积变换误差。 - 动态适配:每次根据路径点重新计算圆柱的高度和旋转方向,完美适配任意模长的路径点。
- 路径循环:添加循环逻辑,实现路径的重复播放。
内容的提问来源于stack exchange,提问作者Stéphane Laurent
相关产品推荐
相关产品推荐

