You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Three.js中实现LineSegments模型的两状态转换动画

在Three.js中实现LineSegments两种状态的过渡动画

核心思路

LineSegments的形态由其BufferGeometry的position属性决定,动画本质是在两个状态的顶点坐标之间逐帧插值,更新geometry的position数据并标记需要重新渲染。

方法一:手动插值(无需额外库)

适合不想引入第三方库的场景,手动控制动画进度与顶点计算:

  1. 准备两个状态的顶点数组(需保证顶点数量完全一致)
  2. 初始化LineSegments时加载初始状态顶点
  3. 在render循环中根据时间计算动画进度,插值生成当前帧顶点数据
  4. 更新geometry的position属性并标记needsUpdate

示例代码:

// 定义两种状态的顶点数据(示例:2条线段,共4个顶点)
const startPositions = new Float32Array([
  -1, 0, 0,
   1, 0, 0,
  -1, 1, 0,
   1, 1, 0
]);
const endPositions = new Float32Array([
  -2, -1, 0,
   2, -1, 0,
  -2, 2, 0,
   2, 2, 0
]);

// 创建LineSegments实例
const geometry = new THREE.BufferGeometry();
geometry.setAttribute('position', new THREE.BufferAttribute(startPositions, 3));
const material = new THREE.LineBasicMaterial({ color: 0xff0000 });
const lineSegments = new THREE.LineSegments(geometry, material);
scene.add(lineSegments);

// 动画控制变量
let animationProgress = 0;
const duration = 2000; // 动画时长(毫秒)
let startTime = Date.now();
let isPlaying = true;

// 渲染循环中更新动画
function render() {
  requestAnimationFrame(render);

  if (isPlaying) {
    const elapsedTime = Date.now() - startTime;
    animationProgress = Math.min(elapsedTime / duration, 1);
    
    // 插值计算当前顶点位置
    const currentPositions = new Float32Array(startPositions.length);
    for (let i = 0; i < startPositions.length; i++) {
      currentPositions[i] = THREE.MathUtils.lerp(startPositions[i], endPositions[i], animationProgress);
    }
    
    // 更新顶点数据并标记需要刷新
    lineSegments.geometry.attributes.position.set(currentPositions);
    lineSegments.geometry.attributes.position.needsUpdate = true;

    // 动画结束后反向播放
    if (animationProgress === 1) {
      setTimeout(() => {
        startTime = Date.now();
        [startPositions, endPositions] = [endPositions, startPositions];
        animationProgress = 0;
      }, 500);
    }
  }

  renderer.render(scene, camera);
}
render();

方法二:使用Tween.js简化动画管理

适合需要缓动效果、循环控制等复杂动画逻辑的场景,借助Tween.js统一管理动画生命周期:

  1. 引入Tween.js(可通过npm或CDN获取)
  2. 定义状态顶点数据并初始化LineSegments
  3. 创建Tween实例对进度变量做动画,在回调中更新顶点数据
  4. 在渲染循环中调用Tween.update()维持动画运行

示例代码:

// 引入Tween.js(假设已通过npm安装)
import { Tween, Easing } from '@tweenjs/tween.js';

// 定义状态顶点数据
const startPositions = new Float32Array([
  -1, 0, 0,
   1, 0, 0,
  -1, 1, 0,
   1, 1, 0
]);
const endPositions = new Float32Array([
  -2, -1, 0,
   2, -1, 0,
  -2, 2, 0,
   2, 2, 0
]);

// 创建LineSegments实例
const geometry = new THREE.BufferGeometry();
geometry.setAttribute('position', new THREE.BufferAttribute(startPositions, 3));
const material = new THREE.LineBasicMaterial({ color: 0xff0000 });
const lineSegments = new THREE.LineSegments(geometry, material);
scene.add(lineSegments);

// 动画进度变量
const animationState = { progress: 0 };

// 创建循环动画
function createAnimation() {
  new Tween(animationState)
    .to({ progress: 1 }, 2000)
    .easing(Easing.Quadratic.InOut) // 缓动效果
    .onUpdate(() => {
      const currentPositions = new Float32Array(startPositions.length);
      for (let i = 0; i < startPositions.length; i++) {
        currentPositions[i] = THREE.MathUtils.lerp(startPositions[i], endPositions[i], animationState.progress);
      }
      lineSegments.geometry.attributes.position.set(currentPositions);
      lineSegments.geometry.attributes.position.needsUpdate = true;
    })
    .onComplete(() => {
      setTimeout(() => {
        [startPositions, endPositions] = [endPositions, startPositions];
        animationState.progress = 0;
        createAnimation();
      }, 500);
    })
    .start();
}

// 渲染循环
function render() {
  requestAnimationFrame(render);
  Tween.update(); // 必须调用以更新Tween状态
  renderer.render(scene, camera);
}
createAnimation();
render();

注意事项

  • 两个状态的顶点数量必须完全一致,否则插值会导致形态错乱
  • 若顶点数量极大,手动循环插值性能不足,可考虑使用ShaderMaterial在GPU层面实现插值(复杂度更高但性能更优)
  • 动画过程中必须设置needsUpdate = true,否则Three.js不会更新顶点数据
  • 如需暂停/继续动画,只需控制isPlaying变量(手动插值法)或调用Tween的pause()/resume()方法

内容的提问来源于stack exchange,提问作者Neil

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 05:20:43