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

一、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个事件
 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、点击放大例子
 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]} />
          {/*  */}
        </Suspense>
      </Canvas>
    </div>
  );
}
十、代码地址

git地址
效果展示:
使用react-three-fiber加载glb格式3D文件,并播放3D模型自带动画_第1张图片
使用react-three-fiber加载glb格式3D文件,并播放3D模型自带动画_第2张图片

你可能感兴趣的:(html,react,js,hooks,react,threejs,three-fiber)