在react-三纤维中的useFrame()中取消多个模型之间的动画同步

问题描述 投票:0回答:1

我正在使用 next.js、TypeScript 和 React- Three-Fiber 开发我的 Web 开发人员组合。 在一页中,我想做一个受此启发的漂亮背景:https://codepen.io/algoregalore/pen/KJdBYP

我想制作多个模型(采用.glb格式)在画布中的随机位置以动画淡入淡出的方式出现,等待几秒钟然后慢慢淡出以便重新出现在另一个位置,反之亦然。 最主要的是每个模型将独立于其他模型播放其动画,不同步

这是 TypeScript 中的主要代码:

import {Suspense, useRef, useEffect, RefObject} from 'react';
import {Canvas, useFrame} from '@react-three/fiber';
import {Preload} from '@react-three/drei';
import {Group, Material, Mesh} from 'three';
import {SmartphoneModel, LaptopModel, CookieModel, PawnModel } from './models';
import {getNewPositionsArray, getRandomIntInclusive} from "@/utils/function";
import { ModelProps } from '@/constants/interface';

const componentModels = {
  smartphone: SmartphoneModel,
  laptop: LaptopModel,
  cookie: CookieModel,
  pawn: PawnModel
};

// this component is a wrapper to add new prop and control the animation of the model passed as prop
const ModelFadeWrapper = ({model, position, props} : {model : string, position? : [number, number, number], props : ModelProps}) => {
  const ref = useRef<Group>(null); // we create a ref to handle the model rotation
  const animationStarted = useRef<boolean>(false); // we create a ref that represent if the animation start
  const delayAnimationStart = getRandomIntInclusive(0,4) * 1000; // a random before the start of the animation
  console.log(delayAnimationStart);
  // we create a new var as a component model in order to pass new prop to it like position, ref and the other props.
  const SpecificModel = componentModels[model as keyof typeof componentModels];

  useEffect(()=>{
    if (ref.current?.children != null) {
      (ref.current.children as Mesh[]).forEach((mesh)=>{
        const material = mesh.material as Material;
        material.opacity = 0;
      });
      ref.current.visible = false;
      setTimeout(()=>{
        if (ref.current != null) {
          ref.current.visible = true;
          animationStarted.current = true;
          console.log(model);
        }
      },delayAnimationStart);
    }
  },[]);

  useFrame((state, delta) => {
    if (ref.current?.children != null && animationStarted) {
      ref.current.rotation.y += 0.01; 
    }
  });

  return (
    <SpecificModel modelRef={ref} position={position} {...props}/>
  );
};


// this component represent the background canvas of the about page
const AboutBGCanvas = () => {
  let modelList:{name:string,key:string,props:ModelProps}[] = [
    {name:"pawn", key:"Pawn1",props:{scale:0.1}},
    {name:"pawn", key:"Pawn2", props:{scale:0.1}},
    {name:"laptop", key:"Laptop1",props:{ rotation:[0.8,0,0], scale:0.8}},
    {name:"laptop", key:"Laptop2",props:{ rotation:[0.8,0,0], scale:0.8}},
    {name:"smartphone", key:"Smartphone1",props:{ scale:0.005}},
    {name:"smartphone", key:"Smartphone2",props:{ scale:0.005}},
    {name:"cookie", key:"Cookie1",props:{ scale:1}}
  ]; // this array contain model component
  let positionModelList:[number,number,number][] = getNewPositionsArray(7,{x:[-3,3],y:[-3,3]},1.6); // this array represent all the positions of the respective models in the canvas
  modelList.forEach((modelObj,index)=>{ // for each model object of modelList we add the position value to the props object
    modelObj.props.position = positionModelList[index];
  });

  return (
    <div className='w-full h-auto absolute inset-0 z-[-1]'>
      <Canvas>
        <directionalLight position={[10, 10, 5]} intensity={2} />
        <directionalLight position={[-10, -10, -5]} intensity={1} />
        <Suspense fallback={null}>
          {
            modelList.map((model, index)=>
              <ModelFadeWrapper key={model.key} model={model.name} position={positionModelList[index]} props={model.props}/>
            )
          }
        </Suspense>
        <Preload all/>
      </Canvas>
    </div>
  );
};

export default AboutBGCanvas;

我设法在画布中显示 .gbl 模型,并访问模型每个网格的旋转和材质属性。 (所以我可以使用不透明度)。 我在 modelList 中创建一个对象列表,稍后将使用它来创建模型,并生成一个扩展向量位置 (x,y,z) 列表来设置画布中所有模型的位置。 然后在 ModelFadeWrapper 中,我传递模型中的 ref 和 props 对象(位置、旋转),并使用 ref 修改模型的不透明度和旋转。 目前,我仅通过更改 useFrame() 内所有模型的 y 旋转进行测试。

useFrame((state, delta) => {
    if (ref.current?.children != null && animationStarted) {
       ref.current.rotation.y += 0.01; 
    }
});

但是所有模型都会同时播放动画,即使我设置了 setTimeout 来使所有模型动画的开始不同步。 旋转与所有模型同步。 我该如何设置每个模型的轮换而不是同时播放? 预先感谢您的回复。

three.js react-three-fiber
1个回答
0
投票

您正在尝试实现“逐帧”动画,IMO 对于您的用例来说,实现和维护相当复杂。

您可以使用“Tween”引擎切换为“命令式动画”(例如gsap)。 这样,您可以方便地将各个补间组织到时间线中。

示例在这里.

© www.soinside.com 2019 - 2024. All rights reserved.