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

如何在Three.js中处理GLTF/FBX单AnimationClip网格动画及切换淡入淡出

你不需要将单流动画拆分为多个AnimationClip,Three.js的AnimationAction支持对同一Clip创建多个独立播放实例,结合时间范围限制和Action自带的交叉淡入淡出接口,即可实现需求。

实现方案

1. 初始化基础组件

首先加载FBX资源后,获取单流动画的完整Clip,提前将帧号区间转换为时间区间(单位为秒),转换规则为时间 = 帧号 / FBX导出帧率,注意帧率要和FBX导出时的配置保持一致,避免时间计算偏差。
示例代码如下:

import * as THREE from 'three'
import { FBXLoader } from 'three/addons/loaders/FBXLoader.js'

const fbxLoader = new FBXLoader()
const clock = new THREE.Clock()
let mixer, currentAction, nextAction, currentSeg
const FPS = 30 // 替换为你的FBX导出帧率
// 自定义动画片段配置,按需扩展
const animationSegments = {
  idle: { start: 0 / FPS, end: 10 / FPS },
  walk: { start: 11 / FPS, end: 22 / FPS },
  run: { start: 23 / FPS, end: 35 / FPS }
}

// 加载FBX资源
fbxLoader.load('your-model.fbx', (fbx) => {
  const fullAnimationClip = fbx.animations[0]
  // 初始化动画混合器
  mixer = new THREE.AnimationMixer(fbx)
  // 为同一个Clip创建两个独立的Action实例,用于交叉淡入
  currentAction = mixer.clipAction(fullAnimationClip)
  nextAction = mixer.clipAction(fullAnimationClip)
  // 初始播放Idle动画
  currentSeg = animationSegments.idle
  playSegment(currentSeg, currentAction)
  // 将模型加入场景
  scene.add(fbx)
})

2. 单片段循环播放控制

封装片段播放方法,同时在帧更新逻辑中限制Action的播放时间始终在对应片段的区间内,实现循环播放:

function playSegment(seg, action) {
  action.reset()
  action.time = seg.start
  action.setLoop(THREE.LoopRepeat, Infinity)
  action.timeScale = 1
  action.play()
}

// 帧更新逻辑
function animate() {
  requestAnimationFrame(animate)
  const delta = clock.getDelta()
  if (mixer) mixer.update(delta)

  // 限制当前激活Action的播放区间
  if (currentAction?.isRunning()) {
    if (currentAction.time > currentSeg.end) {
      currentAction.time = currentSeg.start + (currentAction.time - currentSeg.end)
    }
  }
  // 限制切换中目标Action的播放区间
  if (nextAction?.isRunning()) {
    const targetSeg = animationSegments[currentTargetSeg]
    if (nextAction.time > targetSeg.end) {
      nextAction.time = targetSeg.start + (nextAction.time - targetSeg.end)
    }
  }
  renderer.render(scene, camera)
}
animate()

3. 片段交叉淡入淡出切换

调用AnimationAction自带的crossFadeTo方法实现权重过渡,切换前提前将目标Action定位到对应片段的起始时间即可:

let currentTargetSeg = ''
function switchAnimation(segName, fadeDuration = 0.3) {
  const targetSeg = animationSegments[segName]
  // 相同片段无需切换
  if (currentSeg === targetSeg || !targetSeg) return

  currentTargetSeg = segName
  // 初始化目标Action的播放位置
  playSegment(targetSeg, nextAction)
  // 执行交叉淡入淡出,第三个参数设置为true自动停止原Action
  currentAction.crossFadeTo(nextAction, fadeDuration, true)

  // 过渡完成后交换两个Action的引用,重置状态
  setTimeout(() => {
    const temp = currentAction
    currentAction = nextAction
    nextAction = temp
    currentSeg = targetSeg
  }, fadeDuration * 1000)
}

// 调用示例:切换到Walk动画,过渡时长0.3秒
switchAnimation('walk', 0.3)
注意事项
  • 如果单流动画包含变形目标动画、属性动画等非骨骼动画内容,上述逻辑无需修改即可直接适配
  • 淡入淡出时长建议不超过两个切换片段中最短时长的1/2,避免过渡过程中出现动作跳变
  • 如需支持倒放、变速效果,直接修改对应Action的timeScale属性即可,时间区间限制逻辑会自动适配
  • 如果需要精确控制过渡曲线,可以手动修改两个Action的weight属性,无需使用封装好的crossFadeTo方法

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.05 20:15:01