使用react-three-fiber加载glb格式3D文件,并播放3D模型自带动画

13 篇文章 2 订阅
6 篇文章 0 订阅
一、react-three-fiber简介

使用可重用、自包含的组件以声明方式构建您的场景,这些组件对状态做出反应、易于交互并且可以利用 React 的生态系统。 没有任何限制,一切工作在three.js这里将无一例外地工作。 生态完善。

二、安装
npm install three @react-three/fiber
三、基础语法
1、创建场景
import { Canvas } from '@react-three/fiber'

export default function App() {
  return (
    <div id="canvas-container">
      <Canvas />
    </div>
  )
}

Canvas 组件在幕后做了一些重要的设置工作:

  • 它设置了一个Scene和一个Camera,这是渲染所需的基本构建块
  • 它会自动处理调整大小
  • 它每一帧都渲染我们的场景

请注意,Canvas 将调整大小以适合父 div,因此您可以通过更改 css 中 #canvas-containerwidthheight 来控制它的大小。

2、添加Mesh组件(属于threejs中物体下的网格组件)

表示基于以三角形为polygon mesh(多边形网格)的物体的类。 同时也作为其他类的基类 ,一个Mesh是three.js 中的一个基本对象,它用于保存在3D 空间中表示对象所需的多边形和材质。我们将使用BoxGeometry组件为几何体和MeshPhongMaterial组件为材料创建一个新网格。

为了将这些对象实际添加到我们的场景中,我们将它们安装在Canvas组件中。

import { Canvas } from '@react-three/fiber'

export default function App() {
  return (
    <div id="canvas-container">
      <Canvas>
        <mesh>
          <boxGeometry />
          <meshPhongMaterial />
        </mesh>
      </Canvas>
    </div>
  )
}

上述代码会被编译为如下代码:

const scene = new THREE.Scene()
const camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000)

const renderer = new THREE.WebGLRenderer()
renderer.setSize(width, height)
document.querySelector('#canvas-container').appendChild(renderer.domElement)

const mesh = new THREE.Mesh()
mesh.geometry = new THREE.BoxGeometry()
mesh.material = new THREE.MeshPhongMaterial()

scene.add(mesh)

function animate() {
  requestAnimationFrame(animate)
  renderer.render(scene, camera)
}

animate()

当引入一个mesh组件时,Fiber 会创建一个新THREE.Mesh对象,几何和材料也是如此。然后,几何体和材料被附加到它们的父级。

3、构造函数传参

集合体构造函数可以传递三个参数:宽度、长度和深度。

const geometry = new THREE.BoxGeometry(2, 2, 2)

在fiber中可以使用如下方式代替上述写法:

<boxGeometry args={[2, 2, 2]} />

**请注意,每次更改这些时,都必须重新构造对象! **

4、 添加灯光

向场景中添加一些灯光

<Canvas>
  ...
  <ambientLight intensity={0.1} /> // 环境光会均匀的照亮场景中的所有物体。
  <directionalLight color="red" position={[0, 0, 5]} /> // 平行光是沿着特定方向发射的光。这种光的表现像是无限远,从它发出的光线都是平行的。
</Canvas>

参数传递和上述几何传递参数类似。

<directionalLight
  // we are setting the position
  position={[0, 0, 5]}
  // we are setting the color
  color="red"
/>

编译成

const light = new THREE.DirectionalLight()
light.position.set(0, 0, 5)
light.color.set('red')
四、事件
1、fiber中事件有13个事件
<mesh
  onClick={(e) => console.log('click')}
  onContextMenu={(e) => console.log('context menu')}
  onDoubleClick={(e) => console.log('double click')}
  onWheel={(e) => console.log('wheel spins')}
  onPointerUp={(e) => console.log('up')}
  onPointerDown={(e) => console.log('down')}
  onPointerOver={(e) => console.log('over')}
  onPointerOut={(e) => console.log('out')}
  onPointerEnter={(e) => console.log('enter')}
  onPointerLeave={(e) => console.log('leave')}
  onPointerMove={(e) => console.log('move')}
  onPointerMissed={() => console.log('missed')}
  onUpdate={(self) => console.log('props have been updated')}
/>
2、各个事件作用
事件名称作用
onClick点击网格做出响应
onContextMenu在纹理上右击鼠标事件
onDoubleClick在纹理上双击事件
onWheel在纹理上滚动鼠标滚轮触发的事件
onPointerUp鼠标抬起事件
onPointerDown鼠标按下事件
onPointerOver鼠标滑过
onPointerOut鼠标离开
onPointerEnter事件回调进入
onPointerLeave事件回调离开
onPointerMove鼠标移动
onPointerMissed事件消失回调
onUpdate视图更新回调
3、点击放大例子
<mesh scale={active ? 1.5 : 1} onClick={() => setActive(!active)} ref={myMesh}>
  <boxGeometry />
  <meshPhongMaterial color="royalblue" />
</mesh>
五、模型加载
1、引入资源

注意: 引入资源必须放在public文件夹下,否则都会引入失败

import React, { useRef } from 'react'
import { useGLTF } from '@react-three/drei'

export default function Model(props) {
  const group = useRef()
  const gltf = useGLTF('/images/xin.gltf'); // public下的images
  return (
    <group ref={group} {...props} dispose={null}>
      <primitive object={gltf.scene} />
    </group>
  )
}

useGLTF.preload('/images/xin.gltf'); // 预加载
2、使用资源
import { Canvas } from "@react-three/fiber";
import { Suspense } from "react";
import { Environment } from "@react-three/drei";

import Model from "./Model";
import './App.css';
import Loader from './Loader';

export default function App() {
  return (
    <div className="App">
      <Canvas>
        <ambientLight intensity={1} />
        <Suspense fallback={<Loader />}>
          <Model position={[0, -4, -10]} />
          <Environment preset="apartment" background /> // 背景图片
        </Suspense>
      </Canvas>
    </div>
  );
}
3、资源过大过渡动画
import { Html, useProgress } from '@react-three/drei'

function Loader() {
  const { progress } = useProgress()
  return <Html center>{progress} % loaded</Html>
}
六、基本动画
1、 useFrame

useFrame是一个 Fiber 挂钩,可让您在 Fiber 渲染循环的每一帧上执行代码。这可以有很多用途,但我们将专注于用它构建动画。

重要的是要记住Fiber 钩子只能在父级内部调用

2、使用useRef

每一帧直接改变我们的网格。首先,我们必须reference通过useRefReact 钩子获取它:

import React, { useRef } from "react";
import { useGLTF } from "@react-three/drei";
import { useFrame } from '@react-three/fiber';

export default function Model(props) {
  const group = useRef();
  const gltf = useGLTF("/images/kunchong.glb");
  useFrame(({clock}) => {
    group.current.rotation.y = Math.sin(clock.getElapsedTime());
  });
  return (
    <group ref={group} {...props} dispose={null}>
      <primitive object={gltf.scene} />
    </group>
  );
}
useGLTF.preload("/images/kunchong.glb");
七、react-spring动画,可以实现和css动画一样效果
1、安装
npm install three @react-spring/three
2、使用
import { useSpring, animated } from '@react-spring/three'
  • useSpring - 将值转换为动画值的钩子
  • animated-用于你的DOM代替或网状的部件,所以不是用meshanimated.mesh,如果你希望它受以下因素影响react-spring
import React, { useRef, useState } from "react";
import { useGLTF } from "@react-three/drei";
import { useFrame } from '@react-three/fiber';
import { useSpring, animated, config } from '@react-spring/three';

export default function Model(props) {
  const group = useRef();
  const gltf = useGLTF("/images/kunchong.glb");
  useFrame(({clock}) => {
    group.current.rotation.y = Math.sin(clock.getElapsedTime());
  });
  const [active, setActive]=useState(false);
  const { scale } = useSpring({ scale: active ? 1.5 : 1, config: config.wobbly });
  return (
    <animated.group ref={group} {...props} dispose={null} scale={scale} onClick={() => setActive(!active)}>
      <primitive object={gltf.scene} />
    </animated.group>
  );
}

useGLTF.preload("/images/kunchong.glb");

您会看到它不仅从一个值跳到另一个值,而是在两个值之间平滑地进行动画处理。 我想做的最后一步是为动画添加一些摇摆不定的效果,为此我们可以configreact-spring以下位置导入对象:

import { useSpring, animated, config } from 'react-spring/three';

最后,当我们调用钩子时,我们可以为 config 传递一个值,然后让我们传递wobbly配置:

const { scale } = useSpring({
  scale: active ? 1.5 : 1,
  config: config.wobbly,
});
八、加载windows的glb文件,并播放3D的动画
import React, { useState, useEffect, useCallback } from "react";
import { useGLTF, useAnimations } from "@react-three/drei";
import { useFrame } from "@react-three/fiber";
import { useSpring, animated, config } from "@react-spring/three";

export default function Model(props) {
  // const group = useRef();
  const { scene, animations } = useGLTF("/images/kunchong.glb");
  const { actions, names, ref } = useAnimations(animations);
  useFrame(({ clock }) => {
    ref.current.rotation.y = Math.sin(clock.getElapsedTime());
  });
  const [active, setActive] = useState(false);
  const { scale } = useSpring({
    scale: active ? 1.5 : 1,
    config: config.wobbly,
  });

  const [activeIndex, setActiveIndex]=useState(0);
  useEffect(() => {
    console.log(actions, "actions", animations, names);
    if (actions) {
      console.log(actions, "actions", animations, names);
      actions[names[activeIndex]].reset().fadeIn(0.5).play();
    }
    // In the clean-up phase, fade it out
    return () => actions[names[activeIndex]].fadeOut(0.5);
  }, [actions, names, activeIndex]);

  const handleChangeAnmition=useCallback(()=>{
    if(activeIndex<names.length-1){
      setActiveIndex(activeIndex+1);
    }else{
      setActiveIndex(0);
    }
  }, [activeIndex]);

  return (
    <animated.group
      ref={ref}
      {...props}
      dispose={null}
      scale={scale}
      onPointerOver={() => setActive(!active)}
      onClick={handleChangeAnmition}
    >
      <primitive object={scene} />
    </animated.group>
  );
}

useGLTF.preload("/images/kunchong.glb");
九、完整代码

Module.js

import React, { useState, useEffect, useCallback } from "react";
import { useGLTF, useAnimations } from "@react-three/drei";
import { useFrame } from "@react-three/fiber";
import { useSpring, animated, config } from "@react-spring/three";

export default function Model(props) {
  // const group = useRef();
  const { scene, animations } = useGLTF("/images/kunchong.glb");
  const { actions, names, ref } = useAnimations(animations);
  useFrame(({ clock }) => {
    ref.current.rotation.y = Math.sin(clock.getElapsedTime());
  });
  const [active, setActive] = useState(false);
  const { scale } = useSpring({
    scale: active ? 0.6 : 1,
    config: config.wobbly,
  });

  const [activeIndex, setActiveIndex]=useState(0);
  useEffect(() => {
    console.log(actions, "actions", animations, names);
    if (actions) {
      actions[names[activeIndex]].reset().fadeIn(0.5).play();
    }
    return () => actions[names[activeIndex]].fadeOut(0.5);
  }, [actions, names, activeIndex]);

  const handleChangeAnmition=useCallback(()=>{
    if(activeIndex<names.length-1){
      setActiveIndex(activeIndex+1);
    }else{
      setActiveIndex(0);
    }
  }, [activeIndex]);

  return (
    <animated.group
      ref={ref}
      {...props}
      dispose={null}
      scale={scale}
      onPointerOver={() => setActive(!active)}
      onClick={handleChangeAnmition}
    >
      <primitive object={scene} />
    </animated.group>
  );
}

useGLTF.preload("/images/kunchong.glb");

Loader.js

import { Html, useProgress } from "@react-three/drei";

export default function Loader() {
  const { progress } = useProgress();
  return (
    <Html center style={{ color: "blue" }}>
      {progress ? parseInt(progress) : 0} % loaded
    </Html>
  );
}

App.js

import { Canvas } from "@react-three/fiber";
import { Suspense } from "react";
import { Environment } from "@react-three/drei";

import Model from "./Model";
import './App.css';
import Loader from './Loader';

export default function App() {
  return (
    <div className="App">
      <Canvas>
        <ambientLight intensity={1} />
        <Suspense fallback={<Loader />}>
          <Model position={[0, -4, -12]} />
          {/* <Environment preset="apartment" background /> */}
        </Suspense>
      </Canvas>
    </div>
  );
}
十、代码地址

git地址
效果展示:
在这里插入图片描述
在这里插入图片描述

  • 9
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值