Three.js 进阶之旅:滚动控制模型动画和相机动画 ?

这篇具有很好参考价值的文章主要介绍了Three.js 进阶之旅:滚动控制模型动画和相机动画 ?。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Three.js 进阶之旅:滚动控制模型动画和相机动画 ?

声明:本文涉及图文和模型素材仅用于个人学习、研究和欣赏,请勿二次修改、非法传播、转载、出版、商用、及进行其他获利行为。

摘要

专栏上篇文章《Three.js 进阶之旅:页面*滑滚动-王国之泪》 讲解并实现了如何使用 R3F 进行页面图片*滑滚动,本文内容在上节的基础上,学习如何使用滚动控制 ScrollControls 来控制模型的的动画播放和相机动画,通过滚动鼠标滚轮或者上下移动触摸板,来控制模型的动画播放进度或者相机的方位视角,从而呈现出惊艳的视觉效果。这种有趣的效果大家在*时浏览一些网页的时候应该经常见到,如一些 3D产品 介绍页向下滑动鼠标滚轮时产品同时旋转并根据产品的不同视角加载不同文案、或者 3D数字地球 根据滚轮的移动距离转到某个国家或地区、还有一些 个人简历 页面或时间轴页面也经常用到这种效果。通过本文的阅读和案例页面的实现,你将学习到的知识包括:R3F 生态中的 ScrollControlsHtmluseScrolluseGLTFuseAnimations 等组件和方法的基本用法、在 R3F 中加载模型并播放模型骨骼动画、通过滚动控制模型动画播放进程和相机参数、页面元素的一些 CSS 动画及页面整体丝滑滚动动画实现等。

效果

本文案例的实现效果如下图所示,页面主体元素由一个三维模型 🐸🦢、及底部的 5HTML 页面构成,页面初始加载时模型是静止的,当我们使用鼠标或触控板或直接拖动页面滚动条时 🖱,相机镜头📷 从正面*处*滑过渡到模型侧面远处,模型开始自动播放自带骨骼动画,模型动作根据页面滚动距离和滚动时速率的大小而不同。当我们点击页面顶部菜单时,页面会*滑滚动到对应位置,模型也会播放动画。

Three.js 进阶之旅:滚动控制模型动画和相机动画 ?

当页面逆向 👆 滚动时,相机和模型动画也会逆向变化和播放。

Three.js 进阶之旅:滚动控制模型动画和相机动画 ?

文章使用 GIF 可能会造成丢帧或卡顿,可以亲自打开预览链接试试,大屏访问效果更佳。

  • 👁‍🗨 在线预览地址:https://dragonir.github.io/dancingDuck/

本专栏系列代码托管在 Github 仓库【threejs-odessey】,后续所有目录也都将在此仓库中更新

🔗 代码仓库地址:git@github.com:dragonir/threejs-odessey.git

原理

如果用原生 JavaScript 实现滚动动画效果,就需要监听滚动事件和计算滚动距离。本文还是和上篇文章《hree.js 进阶之旅:页面*滑滚动-王国之泪》一样,直接使用封装好的组件 ScrollControlsScroll 来实现,它们的详细用法和原理可前往上篇文章查看。本文中最终实现的页面需要加载模型并播放它自带的骨骼动画,因此用到了以下几个 @react-three/drei 中的组件和 hooks

Html

允许我们将 HTML 内容绑定到场景中的任意对象,它将自动投影到对象上。

<Html
  as='div'                       // 包裹元素,默认为 'div'
  wrapperClass                   // 包裹元素的类名,默认为 undefined
  prepend                        // 画布后面的元素,默认为 false
  center                         // 添加 -50%/-50% css变换,默认为 false
  fullscreen                     // 与左上角对齐并填满屏幕,默认为 false
  distanceFactor={10}            // 如果设置该值,子元素将按与相机的距离进行缩放,默认为 undefined
  zIndexRange={[100, 0]}         // Z阶范围,默认为 [16777271, 0]
  portal={domnodeRef}            // 对目标容器的引用,默认为 undefined
  transform                      // 若设置 true,将进行 3d 矩阵转换,默认为 false
  sprite                         // 渲染为 sprite,仅在转换模式下生效,默认为 false
  occlude                        // 遮挡模式,默认为 false,当设置为 blending 时将开启真正混合遮挡
  castShadow                     // 产生阴影
  receiveShadow                  // 接收阴影
  // 像 Mesh 一样设置材质
  material={<meshPhysicalMaterial side={DoubleSide} opacity={0.1} />}
  // 覆盖默认定位功能
  calculatePosition={(el: Object3D, camera: Camera, size: { width: number; height: number }) => number[]}
  occlude={[ref]}                // 可以为真或 Ref<Object3D>,当为 true 时遮挡整个场景,默认为 undefined
  onOcclude={(visible) => null}  // 可见性修改时的回调,默认为 undefined
  {...groupProps}                // 支持所有 THREE.Group 属性
  {...divProps}                  // 支持所有 HTML DIV 元素属性
>
  <h1>hello</h1>
  <p>world</p>
</Html>

Html 可以通过配置 occlude 属性隐藏在几何体后面。当 Html 组件隐藏时,它将在最内部的 div 上设置 opacity 属性,如果需要添加动画效果或者控制过渡效果,可以使用 onOcclude 自定义方法。

<Html
  occlude
  onOcclude={set}
  style={{
    transition: 'all 0.5s',
    opacity: hidden ? 0 : 1,
    transform: `scale(${hidden ? 0.5 : 1})`
  }}
/>

useGLTF

它是一个使用 useLoaderGLTFLoader 的方便钩子函数,它默认使用 draco 来加载已压缩的模型文件。

useGLTF(url)
useGLTF(url, '/draco-gltf')
useGLTF.preload(url)

useAnimations

AnimationMixer 的抽象钩子方法,可以像下面这样获取到模型自带的动画信息。

const { nodes, materials, animations } = useGLTF(url)
const { ref, mixer, names, actions, clips } = useAnimations(animations)
useEffect(() => {
  actions?.jump.play()
})

THREE.MathUtils.damp

使用 dt 以类似弹簧的方式从 xy *滑地插入一个数字,以保持与帧速率无关的运动。

.damp (x : Float, y: Float, lambda: Float, dt: Float ): Float
  • x:当前点。
  • y:目标点。
  • lambda:较高的 lambda 值可以使运动更加突然,较低的值可以使运动更加*缓。
  • dt:以秒为单位的增量时间。

实现

〇 资源引入

在文件顶部,我们按上述原理中所述,引入必需的组件和方法。

import * as THREE from 'three';
import { Suspense, useEffect } from 'react';
import { Canvas, useFrame } from '@react-three/fiber';
import { ScrollControls, Html, useScroll, useGLTF, useAnimations } from '@react-three/drei';

① 场景初始化

场景初始化非常简单,只需像下面这样添加 R3F<Canvas /> 组件,并初始化相机位置即可。

export default function Experience() {
  return (
    <>
      <Canvas camera={{ position: [0, 0, 0] }}></Canvas>
    </>
  );
}

Three.js 进阶之旅:滚动控制模型动画和相机动画 ?

② 加载模型

我们先定义一个 ShubaDuck 类用来表示模型元素,然后使用 useGLTF 加载模型 🐸🦢 并使用模型文件的 scene 进行渲染。然后在 Canvas 中添加模型元素,并通过 scaleposition 等属性调整模型在页面上的显示大小和位置。在页面渲染前,我们也可以使用 useGLTF.preload 对模型进行预加载,以提高页面使用体验。

function ShubaDuck({ ...props }) {
  const { scene, animations } = useGLTF('./models/duck.glb')
  return <primitive object={scene} {...props} />
}
useGLTF.preload('./models/duck.glb');

<Canvas camera={{ position: [0, 0, 0] }}>
  <ShubaDuck scale={8} position={[0, -7, 0]} />
</Canvas>

以下为原始模型文件,下载完模型后可以我们可以在 Blender 中查看模型结构和动画信息,删除修改不需要的元素,最后压缩导出。

Three.js 进阶之旅:滚动控制模型动画和相机动画 ?

页面中完成模型加载并渲染。

Three.js 进阶之旅:滚动控制模型动画和相机动画 ?

🔗 模型文件来源:https://sketchfab.com/3d-models/shuba-duck-54a6276ce06c4cc88fd497c8f1b8eb66

③ 播放模型骨骼动画

我们从模型中拿到内置的骨骼动画 animations,然后使用 useAnimations 获取到所有的动作,可以根据动作的名称进行动画播放比如本例中是 LironShuba。可以在 useEffect 中对动作使用 play() 方法进行播放,本例中的 reset()fadeIn() 方法都是可选的,它们的作用分别是开始前重置动作和模型入场动画类型。

function ShubaDuck({ ...props }) {
  const { scene, animations } = useGLTF('./models/duck.glb')
  const { actions } = useAnimations(animations, scene)
  // 播放模型动画
  useEffect(() => void (actions['LironShuba'].reset().fadeIn(0.5).play()), [actions])
  // ...
}

Three.js 进阶之旅:滚动控制模型动画和相机动画 ?

④ 滚动控制模型动画

现在我们来添加通过鼠标滚轮滚动来控制模型动画播放进度的功能,即当我们向下滚动页面时,模型动画正向播放,否则逆向播放,滚动速度越快,模型动画播放速度也越快。我们先在 useEffect 中将模型动画初始状态设置为 pause 暂停,然后在 useFrame 页面重绘动画钩子函数中拿到动画动作和滚动百分比 scroll.offset,设置动作播放时间 action.time,使其从初始值*滑过渡到目标值,目标值的确定可以通过整个动画播放周期时间以及页面滚动距离的长度去计算,第三个参数 lambda 也可以根据自己的页面进行调整。

function ShubaDuck({ ...props }) {
  // ...
  useEffect(() => void (actions['LironShuba'].play().paused = true), [actions])
  useFrame((state, delta) => {
    const action = actions['LironShuba']
    const offset = scroll.offset
    action.time = THREE.MathUtils.damp(action.time, (action.getClip().duration / 2) * offset, 100, delta)
    state.camera.lookAt(0, 0, 0)
  })
  // ...
}

在页面中,我们需要使用 <ScrollControls /> 组将将模型组件 <ShubaDuck /> 包裹起来。

<Canvas camera={{ position: [0, 0, 0] }}>
  <Suspense fallback={null}>
    <ScrollControls pages={4}>
      <ShubaDuck scale={10} position={[0, -10, 0]} />
    </ScrollControls>
  </Suspense>
</Canvas>

此时使用滚动控制模型动画功能就全部完成了,页面初始加载时模型是静止的,当我们滚动页面时,模型根据滚动速度和滚动距离就会自动播放对应的动画了。

Three.js 进阶之旅:滚动控制模型动画和相机动画 ?

💡 scroll.offset 是一个处于 [0, 1] 之间的数,表示滚动的百分比,当页面未滚动时值为 0,完全滚动到尽头时值为 1.

⑤ 滚动控制相机

useFrame 钩子函数中,我们同样可以在页面滚动时动态修改相机 📷 的位置,这样在视觉上也能形成比如模型旋转、放大缩小、显示隐藏等动画效果。本案例中使用了如下的设置,当页面从上往下滚动时,相机从模型正面变换到模型侧面更远的地方,在视觉上形成模型转身并变小的效果

useFrame((state, delta) => {
  // ...
  state.camera.position.set(Math.sin(-offset) * 50, 1, Math.cos((offset * Math.PI) / 5) * 15)
})

Three.js 进阶之旅:滚动控制模型动画和相机动画 ?

⑥ 页面装饰

模型部分已经全部完成了,此时我们可以使用原理中介绍的 Html 组件,将其添加到页面中,并直接用 HTMLCSS 添加一些好看的页面,与模型主题呼应。像下面这样,本文中添加了 5 个页面,每个页面都添加了不同的样式。有时间的话,我们也可以使用 gsap 等动画库,给 HTML 元素也添加一些滚动时的动画效果

<Canvas camera={{ position: [0, 0, 0] }}>
  <Suspense fallback={null}>
    <ScrollControls pages={4}>
      <Html wrapperClass='articles' occlude>
        <article className='page page1'></article>
        <article className='page page2'></article>
        <article className='page page3'></article>
        <article className='page page4'></article>
        <article className='page page5'></article>
      </Html>
      <ShubaDuck scale={10} position={[0, -10, 0]} />
    </ScrollControls>
  </Suspense>
</Canvas>

第一页

第一页有较多的页面元素,其中底部白色文字使用了一种 woff2 格式的开源卡通字体,旋转的鹅 🦢 是通过如下 CSS 动画实现的。

@keyframes rotateY
  from
    transform: perspective(400px) rotateY(0deg)
  to
    transform: perspective(400px) rotateY(360deg)

Three.js 进阶之旅:滚动控制模型动画和相机动画 ?

第二页

第二页是 3 个色块 🟩 通过旋转后形成的图案,给它们添加了明暗变化的动画效果。

Three.js 进阶之旅:滚动控制模型动画和相机动画 ?

其他页

剩下的页面使用了一些简单的文案或图片元素,等有时间再优化小吧 😂。其实还有很多细节样式和功能,如鼠标的样式是一个 🟡、向下滚动的提示语、顶部半透明的导航菜单等,具体实现可查看源码。

Three.js 进阶之旅:滚动控制模型动画和相机动画 ?
Three.js 进阶之旅:滚动控制模型动画和相机动画 ?
Three.js 进阶之旅:滚动控制模型动画和相机动画 ?

⑦ 点击菜单栏页面滑动动画

最后,我们来给页面顶部的菜单添加一下点击操作 🖱 。点击页面顶部导航栏菜单滑动到对应页面功能实现使用了 element.scrollIntoView 方法,可以像下面这样实现并绑定到菜单的点击事件中,此时点击菜单页面滚动时,模型动画也会同时播放。

const handleMenuClick = (className) => {
  const page = document.querySelector(className);
  page.scrollIntoView({ behavior: 'smooth', block: 'center', inline: 'nearest' });
}

<span className='menu' onClick={handleMenuClick.bind(this, '.page1')}></span>

Three.js 进阶之旅:滚动控制模型动画和相机动画 ?

🔗 源码地址: https://github.com/dragonir/threejs-odessey

总结

本文中主要包含的知识点包括:

  • R3F 生态中的 ScrollControlsHtmluseScrolluseGLTFuseAnimations 等组件和方法的基本用法。
  • 学会在 R3F 中加载模型并播放模型骨骼动画。
  • 通过滚动控制模型动画播放进程和相机参数。
  • 在滚动页面中将模型和 HTML 元素结合起来。
  • 页面元素的一些 CSS 动画及页面整体丝滑滚动动画实现等。

想了解其他前端知识或其他未在本文中详细描述的Web 3D开发技术相关知识,可阅读我往期的文章。如果有疑问可以在评论中留言,如果觉得文章对你有帮助,不要忘了一键三连哦 👍

附录

  • [1]. 🌴 Three.js 打造缤纷夏日3D梦中情岛
  • [2]. 🔥 Three.js 实现炫酷的赛博朋克风格3D数字地球大屏
  • [3]. 🐼 Three.js 实现2022冬奥主题3D趣味页面,含冰墩墩
  • [4]. 🦊 Three.js 实现3D开放世界小游戏:阿狸的多元宇宙
  • [5]. 🏡 Three.js 进阶之旅:全景漫游-高阶版在线看房
  • ...
  • 【Three.js 进阶之旅】系列专栏访问 👈
  • 更多往期【3D】专栏访问 👈
  • 更多往期【前端】专栏访问 👈

参考

  • [1]. threejs.org
  • [2]. drei.pmnd.rs

本文作者:dragonir 本文地址:https://www.cnblogs.com/dragonir/p/17430114.html文章来源地址https://www.toymoban.com/news/detail-458278.html

到了这里,关于Three.js 进阶之旅:滚动控制模型动画和相机动画 ?的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包赞助服务器费用

相关文章

  • Three.js之相机、渲染器、光源、动画、性能监测

    第一个3D案例—透视投影相机 第一个3D案例—渲染器 … Canvas画布布局和全屏 透视投影相机PerspectiveCamera WebGL渲染器WebGLRenderer 辅助观察坐标系AxesHelper 漫反射网格材质MeshLambertMaterial 点光源PointLight 点光源辅助观察PointLightHelper 环境光AmbientLight 平行光DirectionalLight 平行光辅助观

    2024年02月13日
    浏览(17)
  • Three.js 进阶之旅:全景漫游-高阶版在线看房 ?

    Three.js 进阶之旅:全景漫游-高阶版在线看房 ?

    声明:本文涉及图文和模型素材仅用于个人学习、研究和欣赏,请勿二次修改、非法传播、转载、出版、商用、及进行其他获利行为。 专栏上篇文章《Three.js 进阶之旅:全景漫游-初阶移动相机版》中通过创建多个球体全景场景并移动相机和控制器的方式实现了多个场景之间

    2023年04月10日
    浏览(15)
  • three.js 缓动算法.easing(渐入相机动画)

    three.js 缓动算法.easing(渐入相机动画)

    效果:淡入,靠近物体 代码:

    2024年01月19日
    浏览(16)
  • Three.js基础入门介绍——Three.js学习三【借助控制器操作相机】

    Three.js基础入门介绍——Three.js学习三【借助控制器操作相机】

    在Three.js基础入门介绍——Three.js学习二【极简入门】中介绍了如何搭建Three.js开发环境并实现一个包含旋转立方体的场景示例,以此为前提,本篇将引进一个控制器的概念并使用”轨道控制器”(OrbitControls)来达到从不同方向展示场景内容的效果。 Three.js的控制器 three.js的核

    2024年02月04日
    浏览(48)
  • Three.js——十五、Box3、相机动画、lookAt()视线方向、管道漫游案例、OrbitControls旋转缩放限制、以及相机控件MapControls

    Three.js——十五、Box3、相机动画、lookAt()视线方向、管道漫游案例、OrbitControls旋转缩放限制、以及相机控件MapControls

    正投影相机和透视相机的区别 如果都以高处俯视去看整个场景,正投影相机就类似于 2d 的可视化的效果,透视相机就类似于人眼观察效果 调整 left, right, top, bottom 范围大小 如果你想整体预览全部立方体,就需要调整相机的渲染范围,比如设置上下左右的范围。 使用场景:正

    2024年02月04日
    浏览(29)
  • Three.js 模型加载及加载简单动画

    Three.js 模型加载及加载简单动画

    时间过的好快啊~再一次感叹,忙忙碌碌一年又过去了,新年第一帖,新的一年也要加油呀! 简单介绍下Three.js吧,Three.js是基于原生WebGL封装运行的三维引擎,在所有WebGL引擎中,Three.js是国内文资料最多、使用最广泛的三维引擎。因为使用简单,入门比较容易。 Three.js的具体

    2024年02月12日
    浏览(9)
  • Three.js教程:相机控件轨道控制器OrbitControls

    推荐:将 NSDT场景编辑器 加入你的3D工具链 其他系列工具: NSDT简石数字孪生 平时开发调试代码,或者展示模型的时候,可以通过相机控件OrbitControls实现旋转缩放预览效果。 OrbitControls使用 你可以打开课件案例源码测试下效果。 旋转:拖动鼠标左键 缩放:滚动鼠标中键 平移

    2024年02月09日
    浏览(40)
  • Three.js加载FBX模型并解析骨骼动画

    Three.js加载FBX模型并解析骨骼动画

    通过Threejs先加载一个.FBX格式的三维模型文件,然后解析该文件中的骨骼动画信息。  FBX 加载器 FBXLoader.js 加载fbx模型文件 加载模型文件,加载完成后,如果模型显示位置不符合要求,可以通过Threejs程序进行平移、缩放等操作。 查看FBX模型帧动画数据 stl、obj都是静态模型,

    2024年02月07日
    浏览(20)
  • Three.js使用OrbitControls(控制器)后修改相机旋转方向无效问题

    Three.js使用OrbitControls(控制器)后修改相机旋转方向无效问题

            当我们在项目使用 OrbitControls(控制器)来控制相机进行旋转的时候不可避免就会遇到一个问题,修改相机的 lookAt 和 rotation 无效~ 通过 lookAt、和rotation对相机的旋转进行操作后发现相机的位置和角度并没有发生变化,原因是当前相机已经被 OrbitControls 控制器托管

    2024年04月10日
    浏览(46)
  • VUE2+THREE.JS 按照行动轨迹移动人物模型并相机视角跟随人物

    VUE2+THREE.JS 按照行动轨迹移动人物模型并相机视角跟随人物

    人物按照上一篇博客所设定的关键点位置,匀速移动 全局定义的参数:

    2024年02月20日
    浏览(44)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包