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

使用react-three加载GLTF文件时动画异常问题求助

解决React Three Fiber中GLTF模型动画无法自动播放的问题

问题根源

你给Canvas设置了frameloop="demand",这个模式下React Three Fiber(R3F)只会在用户交互(点击、滚动)或场景状态变化时才触发渲染帧更新。而动画依赖AnimationMixer通过useFrame持续获取delta时间来推进,没有持续的帧更新,动画自然卡住,只有滚动/点击时才会跳几帧。

修复方案

方案1:修改frameloop模式(推荐)

把Canvas的frameloop参数从"demand"改为"always",让R3F持续触发渲染帧,动画就能正常播放:

<Canvas
  frameloop="always" // 这里修改
  shadows
  dpr={[1, 2]}
  camera={{ position: [20, 3, 5], fov: 25 }}
  gl={{ preserveDrawingBuffer: true }}
>
  {/* 其他组件内容 */}
</Canvas>

方案2:保留demand模式但手动触发渲染

如果必须保留demand模式的性能优化,可以在useFrame里手动请求渲染,强制每一帧更新动画:

useFrame((state, delta) => {
  mixer.update(delta);
  state.gl.setAnimationLoop(() => state.render()); // 手动触发渲染
});

注意:这个方法会抵消demand模式的性能优势,优先推荐方案1。

额外优化点

  1. 用useRef保存mixer实例,避免组件重渲染时重复创建:
const mixer = useRef(new THREE.AnimationMixer(scene)).current;

原代码里直接用let mixer = new THREE.AnimationMixer(scene),每次组件渲染都会新建实例,可能导致动画异常。

  1. 用useEffect初始化动画,确保组件挂载后只执行一次:
useEffect(() => {
  const actions = animations.map(clip => mixer.clipAction(clip));
  actions.forEach(action => action.play());
  return () => actions.forEach(action => action.stop());
}, [animations, mixer]);

修改后的完整Robot组件代码

import React, { Suspense, useEffect, useState, useRef } from "react";
import { Canvas, useFrame } from "@react-three/fiber";
import { OrbitControls, Preload, useGLTF } from "@react-three/drei";
import CanvasLoader from "../Loader";
import * as THREE from "three";

const Robot = ({ isMobile }) => {
  const { scene, animations } = useGLTF("./robot/scene.gltf");
  const mixer = useRef(new THREE.AnimationMixer(scene)).current;

  useEffect(() => {
    const actions = animations.map(clip => mixer.clipAction(clip));
    actions.forEach(action => action.play());
    return () => actions.forEach(action => action.stop());
  }, [animations, mixer]);

  useFrame((state, delta) => {
    mixer.update(delta);
  });

  return (
    <mesh>
      <hemisphereLight intensity={0.15} groundColor="black" />
      <spotLight
        position={[-20, 50, 10]}
        angle={0.12}
        penumbra={1}
        intensity={1}
        castShadow
        shadow-mapSize={1024}
      />
      <pointLight intensity={1} />
      <primitive
        object={scene}
        dispose={null}
        scale={isMobile ? 2 : 1.6}
        position={isMobile ? [-10, -6, -2] : [-2, -4, 0]}
        rotation={isMobile ? [0, 1.5, 0] : [0, 1.3, 0]}
      />
    </mesh>
  );
};

const RobotCanvas = () => {
  const [isMobile, setIsMobile] = useState(false);

  useEffect(() => {
    const mediaQuery = window.matchMedia("(max-width: 450px)");
    setIsMobile(mediaQuery.matches);
    const handleMediaQueryChange = (event) => {
      setIsMobile(event.matches);
    };
    mediaQuery.addEventListener("change", handleMediaQueryChange);
    return () => mediaQuery.removeEventListener("change", handleMediaQueryChange);
  }, []);

  return (
    <Canvas
      frameloop="always"
      shadows
      dpr={[1, 2]}
      camera={{ position: [20, 3, 5], fov: 25 }}
      gl={{ preserveDrawingBuffer: true }}
    >
      <Suspense fallback={<CanvasLoader />}>
        <OrbitControls
          enableZoom={false}
          maxPolarAngle={Math.PI / 2}
          minPolarAngle={Math.PI / 2}
        />
        <Robot isMobile={isMobile} />
      </Suspense>
      <Preload all />
    </Canvas>
  );
};

export default RobotCanvas;

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 20:04:54