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

Three.js导入带开合动画的雨伞模型后动画异常求助

雨伞3D模型动画在React Three Fiber中异常的解决指南

问题背景

  • 购入的雨伞3D模型自带开合动画,在Blender中预览完全正常
  • 导入React Three Fiber项目后,模型显示两个动画均不符合预期:一个仅实现雨伞旋转,另一个仅让推杆上下移动,无法联动伞面及支撑杆
  • 已确认项目代码无问题,推测问题出在Blender的模型绑定或导出设置,但缺乏Blender使用经验

用户提供的项目代码:

import * as React from 'react';
import { Canvas, useFrame } from '@react-three/fiber';
import { Suspense, useState, useRef, useEffect } from 'react';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
import { OrbitControls, useGLTF } from '@react-three/drei';
import * as THREE from 'three';

const Model = ({ playAnimation }) => {
const gltf = useGLTF(require("../svg/Part3.glb"));
const mixer = useRef();

// Increase the scale to make the model larger
gltf.scene.scale.set(200, 200, 200);

// Set up animation mixer
if (!mixer.current) {
    mixer.current = new THREE.AnimationMixer(gltf.scene);
    gltf.animations.forEach((animation) => {
        const action = mixer.current.clipAction(animation);
        action.play();
    });
    console.log(gltf.animations)
}

// Update the animation mixer on each frame
useFrame((state, delta) => mixer.current.update(delta));

// Play animation when playAnimation is true
useEffect(() => {
    if (playAnimation) {
        mixer.current.timeScale = 1;
    } else {
        mixer.current.timeScale = 0;
    }
}, [playAnimation]);

return <primitive object={gltf.scene} />;
};

const Umbrella = () => {
const [playAnimation, setPlayAnimation] = useState(false);

const handleButtonClick = () => {
    setPlayAnimation(!playAnimation);
};

return (
    <div>
    <Canvas camera={{ position: [0, 0, 1000], fov: 75, near: 1, far: 5000 }} style={{  backgroundColor:       

    "black", position: "relative", width: "100%", height: "90vh" }}>
            <directionalLight position={[2000, 2000, 2000]} intensity={1} color="white" />
            <pointLight position={[10, 10, 10]} intensity={0.6} />
            <Suspense fallback={null}>
                <Model playAnimation={playAnimation} />
            </Suspense>
            <OrbitControls />
        </Canvas>
        <button onClick={handleButtonClick}>{playAnimation ? 'Pause Animation' : 'Play Animation'}  

  </button>
    </div>
);
};

export default Umbrella;

解决步骤

1. 验证Blender中的骨骼绑定有效性

打开模型文件,按以下步骤操作:

  • 切换到姿态模式(快捷键Ctrl+Tab选择姿态模式)
  • 选中推杆对应的骨骼,拖动它,观察伞面和支撑杆是否跟随运动
    • 如果跟随正常:说明绑定没问题,问题出在动画导出环节
    • 如果不跟随:需要重新绑定骨骼
      • 切换回物体模式,先选中伞面/支撑杆模型,再选中骨骼对象
      • 按Ctrl+P,选择自动权重完成模型与骨骼的绑定

2. 烘焙动画到骨骼(解决GLTF导出兼容性)

Blender中部分非骨骼驱动的动画导出后无法被Three.js正确识别,需要将动画烘焙到骨骼:

  • 选中骨骼对象,切换到图形编辑器,顶部菜单选择动作编辑器
  • 在动作编辑器中选中目标开合动画,点击顶部菜单编辑 > 烘焙动作
  • 在弹出的烘焙设置中,勾选仅骨骼、清除关键帧,点击确定完成烘焙

3. 按标准设置导出GLB格式

导出时确保以下设置正确,避免兼容性问题:

  • 选中模型和骨骼对象,点击文件 > 导出 > glTF 2.0
  • 在导出面板中:
    • 勾选动画选项,确保动画数据被导出
    • 确认骨骼选项已勾选
    • 格式选择GLB(二进制格式,Three.js支持更稳定)
    • 取消勾选不必要的选项(如相机、灯光,除非项目需要)

4. 项目中动画的额外验证(可选)

虽然你确认代码无问题,但可以做以下检查:

  • 控制台打印gltf.animations,查看动画的名称和轨道目标是否为骨骼(而非物体本身)
  • 尝试单独播放目标开合动画,避免同时播放多个冲突动画:
    // 替换原有的动画遍历代码
    if (!mixer.current) {
      mixer.current = new THREE.AnimationMixer(gltf.scene);
      // 筛选出开合相关的动画(根据实际动画名称调整判断条件)
      const targetAnim = gltf.animations.find(anim => anim.name.includes("open") || anim.name.includes("close"));
      if (targetAnim) {
        const action = mixer.current.clipAction(targetAnim);
        action.play();
      }
      console.log(gltf.animations);
    }
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 15:28:18