今天主要学习的有关动画相关的技术,使用一个小球在场景中画一个爱心,同时颜色也会改变,也可以修改小球缩放或者旋转(这个案例中没有体现)。具体效果如下:
首先也是需要创建场景,相机、渲染器、控制器(这个案例中可有可无),不懂的可以查看day01的那篇。
这个场景添加了背景颜色和三维坐标,代码如下
scene.background = new THREE.Color(0x888888) // 三维坐标 const axesHelper = new THREE.AxesHelper(8) scene.add(axesHelper)
相机的位置在Z轴上,所以上面的视频中只能看到X轴和Y轴
1.设置灯光
scene.add(new THREE.AmbientLight(0xffffff, 0.2))
const dirLight = new THREE.DirectionalLight(0xfffff, 1)
dirLight.position.set(2, 2, 2)
scene.add(dirLight)
2.创建小球
const geometry = new THREE.SphereGeometry(0.5, 32, 16)
const material = new THREE.MeshBasicMaterial({ color: 0xff0000 })
cube = new THREE.Mesh(geometry, material)
scene.add(cube)
3.初始化动画轨道和动画剪辑
// 位置
const positionKF = new THREE.VectorKeyframeTrack(
'.position',
[0, 1, 2, 3, 4, 5, 6],
[0, 0, 0, -4, 4, 0, -2, 6, 0, 0, 5, 0, 2, 6, 0, 4, 4, 0, 0, 0, 0],
)
// 颜色
const colorKF = new THREE.ColorKeyframeTrack(
'.material.color',
[0, 1, 2, 3, 4, 5, 6],
[1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0],
)
// 动画剪辑,是可复用的关键帧轨道集
clip = new THREE.AnimationClip('Action', 6, [positionKF, colorKF])
4.启用动画, 不要忘记在animate函数中更新mixer
// 动画混合器
mixer = new THREE.AnimationMixer(cube)
clipAction = mixer.clipAction(clip)
clipAction.play()
function animate() {
requestAnimationFrame(animate)
// 获取帧时间,在最开始的时候有设置clock = new THREE.Clock()
const delta = clock.getDelta()
renderer.render(scene, camera)
// 更新
mixer.update(delta)
}
全部代码
<template>
<div id="threeId" ref="elementRef"></div>
</template>
<script setup lang="ts">
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
import { ref, onMounted } from 'vue'
let width, height, scene, camera, renderer, controls
let cube
let clip, mixer, clipAction
const elementRef = ref(null)
let clock = new THREE.Clock()
onMounted(() => {
const element = elementRef.value
width = element.offsetWidth
height = element.offsetHeight
initScene()
initCamera()
initRender()
initLight()
initMesh()
initAnimation()
enableAnimation()
initAxesHelper()
initControls()
animate()
})
function initScene() {
scene = new THREE.Scene()
scene.background = new THREE.Color(0x888888)
}
function initCamera() {
camera = new THREE.PerspectiveCamera(60, width / height, 1, 1000)
camera.position.z = 20
scene.add(camera)
}
function initRender() {
renderer = new THREE.WebGLRenderer({ antialias: true })
renderer.setPixelRatio(window.devicePixelRatio)
renderer.setSize(width, height)
elementRef.value.appendChild(renderer.domElement)
}
function initLight() {
scene.add(new THREE.AmbientLight(0xffffff, 0.2))
const dirLight = new THREE.DirectionalLight(0xfffff, 1)
dirLight.position.set(2, 2, 2)
scene.add(dirLight)
}
function initMesh() {
const geometry = new THREE.SphereGeometry(0.5, 32, 16)
const material = new THREE.MeshBasicMaterial({ color: 0xff0000 })
cube = new THREE.Mesh(geometry, material)
scene.add(cube)
}
function initAnimation() {
// VectorKeyframeTrack 向量类型的关键帧轨道:参数:名称,时间数组,与时间数组中的时间点对应的值数组, 使用的插值类型
// 位置
const positionKF = new THREE.VectorKeyframeTrack(
'.position',
[0, 1, 2, 3, 4, 5, 6],
[0, 0, 0, -4, 4, 0, -2, 6, 0, 0, 5, 0, 2, 6, 0, 4, 4, 0, 0, 0, 0],
)
// 颜色
const colorKF = new THREE.ColorKeyframeTrack(
'.material.color',
[0, 1, 2, 3, 4, 5, 6],
[1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0],
)
// 动画剪辑,是可复用的关键帧轨道集,他代表动画
clip = new THREE.AnimationClip('Action', 6, [positionKF, colorKF])
clip.tracks.forEach((track) => {
track.timeScale = 0.5 // 设置动画播放速度,这里设置为原来的一半
})
}
function enableAnimation() {
// 动画混合器
mixer = new THREE.AnimationMixer(cube)
clipAction = mixer.clipAction(clip)
clipAction.play()
}
function initAxesHelper() {
const axesHelper = new THREE.AxesHelper(8)
scene.add(axesHelper)
}
function initControls() {
controls = new OrbitControls(camera, renderer.domElement)
}
function animate() {
requestAnimationFrame(animate)
const delta = clock.getDelta()
renderer.render(scene, camera)
mixer.update(delta)
}
</script>
<style scoped>
#threeId {
width: 100%;
height: 100%;
}
</style>