在现代Web开发中,交互性和动画已经成为了设计吸引人的用户体验的不可或缺的一部分。Theatre.js是一个强大的JavaScript库,它使得创建高度交互性的Web动画变得容易。在本文中,我们将探讨Theatre.js的一些主要功能,并通过一个实际的示例来演示如何使用它来创建引人入胜的Web动画。
什么是Theatre.js?
Theatre.js是一个用于创建Web动画的JavaScript库,它旨在简化动画的制作过程并提供更多的交互性。它构建在Three.js之上,Three.js是一个用于创建3D图形的流行库。通过Theatre.js,您可以轻松地制作2D和3D动画,包括交互式场景、角色动画和特效。 以下是一些Theatre.js的主要功能:
- 剧场(Theatre): Theatre.js提供了一个剧场的概念,您可以在其中定义和组织您的动画场景。这使得动画的管理变得更加有条不紊。
- 演员(Actors): 在Theatre.js中,您可以创建演员,这些演员是动画的主要参与者。您可以为演员定义外观、动作和交互行为。
- 相机(Cameras): Theatre.js支持不同的相机类型,包括透视相机和正交相机,以满足不同场景的需求。
- 时间轴(Timeline): 您可以使用时间轴来控制动画的时间流逝,以便在不同时间点触发不同的动作。
- 事件处理: Theatre.js允许您轻松处理交互事件,如鼠标点击、键盘输入和触摸事件。
⠀现在,让我们通过一个官网的示例来深入了解如何使用Theatre.js来创建一个交互式的Web动画。
实例:创建一个可以运行的立方体
步骤1: 安装依赖
假设我们使用pnpm作为包管理工具,
bash
// r3f
pnpm add react three @react-three/fiber
// Theatre.js 最新版为0.7.0
pnpm add @theatre/core @theatre/studio @theatre/r3f
// threejs types
pnpm add -D @types/three
步骤2:创建一个main.tsx来写模型
ts
import { createRoot } from 'react-dom/client' // react18
import { Canvas } from '@react-three/fiber'
import { getProject } from '@theatre/core'
const demoSheet = getProject('Demo Project').sheet('Demo Sheet') // Demo Sheet 是独一无二的id,在播放动画时需要给出
const App = () => {
return (
<Canvas
camera={{
position: [5, 5, -5],
fov: 75,
}} // 一个默认监视着0,0,0 position 的相机
gl={{ preserveDrawingBuffer: true }} // 用于显示浮窗
>
<ambientLight intensity={Math.PI}/> // 平行光
<pointLight position={[1, 1, 1] intensity={Math.PI}} />
<mesh>
<boxGeometry args={[1, 1, 1]} />
<meshStandardMaterial color="orange" />
</mesh>
</Canvas>
)
}
createRoot(document.getElementById('root')!).render(<App />)
如果你使用过官网的例子你就会发现,在浏览器中渲染出的立方体表现和官方是不一样的,这里主要表现为颜色
和景深
的区别。这其中的原由是由于threejs 引擎由v0.154.0
--->v0.155.0
版本的升级导致的,原因如下: 总结一下就是,threejs
在154
版本向155
版本升级的时候,把 WebGLRenderer.useLegacyLights
的默认属性由true
改为了false
,并且打算在165
版本移除这个属性。而这个属性会影响光照强度。 想要解决这个问题可以有两种思路:
- 手动把原本的光照乘以
PI
值 - 手动修改
WebGLRenderer.useLegacyLights
的值为true
步骤3: 加入studio 调试界面
ts
import { createRoot } from 'react-dom/client' // react18
import React, { useEffect } from 'react'
import { Canvas } from '@react-three/fiber'
import studio from '@theatre/studio'
import extension from '@theatre/r3f/dist/extension'
...
const App = () => {
useEffect(() => {
if (process.env.NODE_ENV === "development") {
studio.initialize({ usePersistentStorage: false });
studio.extend(extension);
}
}, [])
return (
<Canvas
camera={{
position: [5, 5, -5],
fov: 75,
}} // 一个默认监视着0,0,0 position 的相机
gl={{ preserveDrawingBuffer: true }} // 用于显示浮窗
>
...
</Canvas>
)
}
使用studio.initialize
初始化 后,屏幕上显示如下将usePersistentStorage
的属性值设置为false
可以让该项目在调试模型下不被缓存,方便调试。
使用studio.extend(extension)
后,即可通过相机按钮开启调试控制台。
步骤4: 绑定事件,使控制台可以改变真实的three对象
ts
...
import {editable as e, SheetProvider, PerspectiveCamera } from '@theatre/r3f
function App () => {
return (
<Canvas>
<SheetProvider sheet={getProject('Demo Project').sheet('Demo Sheet')}>
<PerspectiveCamera theatreKey="Camera" makeDefault position={[5, 5, -5]} fov={75} lookAt={[0, 0, 0]} />
<ambientLight intensity={Math.PI}/>
<e.pointLight theatreKey="Light" position={[1, 1, 1]} intensity={Math.PI}/>
<e.mesh theatreKey="Cube">
<boxGeometry args={[1, 1, 1]} />
<meshStandardMaterial color="orange" />
</e.mesh>
</SheetProvider>
</Canvas>
)
}
...
我们使用sheetProvider
给three-react-fiber
组件提供上下文 使用editable
模块把three-react-fiber
组件转换为可监听组件 再使用PerspectiveCamera
替换canvas的默认相机,注意要使用lookAt 使相机注视着目标。
步骤5: 使用调试控制给元素打上关键帧
步骤6: 导出动画,并使用该文件播放
在设置完关键帧后,点击左上角Demo Project ,就可以在右侧点击导出作品按钮
ts
function App () => {
useEffect(() => {
demoSheet.project.ready.then(() => demoSheet.sequence.play({ iterationCount: Infinity}))
},[])
return (
<Canvas>
<SheetProvider sheet={getProject('Demo Project').sheet('Demo Sheet')}>
<PerspectiveCamera theatreKey="Camera" makeDefault position={[5, 5, -5]} fov={75} lookAt={[0, 0, 0]} />
<ambientLight intensity={Math.PI}/>
<e.pointLight theatreKey="Light" position={[1, 1, 1]} intensity={Math.PI}/>
<e.mesh theatreKey="Cube">
<boxGeometry args={[1, 1, 1]} />
<meshStandardMaterial color="orange" />
</e.mesh>
</SheetProvider>
</Canvas>
)
}
完整代码如下:
ts
import './index.css'
import { createRoot } from 'react-dom/client'
import React, { useEffect } from 'react'
import { Canvas } from '@react-three/fiber'
import studio from '@theatre/studio'
import extension from '@theatre/r3f/dist/extension'
import { SheetProvider, editable as e, PerspectiveCamera } from '@theatre/r3f'
import { getProject } from '@theatre/core'
import demoProjectState from './state.json'
const App = () => {
useEffect(() => {
if (process.env.NODE_ENV === "development") {
studio.initialize({ usePersistentStorage: false });
console.log(studio);
studio.extend(extension);
}
demoSheet.project.ready.then(() =>
demoSheet.sequence.play({ iterationCount: Infinity }),
);
}, []);
return (
<Canvas>
<Canvas gl={{ preserveDrawingBuffer: true }}>
<SheetProvider sheet={demoSheet}>
<PerspectiveCamera
theatreKey='Camera'
makeDefault
position={[5, 5, -5]}
lookAt={[0, 0, 0]}
fov={75}
/>
<ambientLight intensity={Math.PI} />
<e.pointLight
theatreKey='Light'
intensity={Math.PI}
position={[1, 1, 1]}
/>
<e.mesh theatreKey='Cube'>
<boxGeometry args={[1, 1, 1]} />
<meshStandardMaterial color='orange' />
</e.mesh>
</SheetProvider>
</Canvas>
</Canvas>
)
}
createRoot(document.getElementById('root')!).render(<App />)
让我们看下动画吧~