使用requestAnimationFrame:基础动画
requestAnimationFrame()方法:
- - window.requestAnimationFrame(callback)告诉浏览器——你希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。
- - 该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行。
- - 当你准备更新动画时你应该调用此方法,这将使浏览器在下一次重绘之前调用你传入给该方法的动画函数(即你的回调函数)。
- - 回调函数执行次数通常是每秒60次,但在大多数遵循W3C建议的浏览器中,回调函数执行次数通常与浏览器屏幕刷新次数相匹配。
- - 为了提高性能和电池寿命,因此在大多数浏览器里,当requestAnimationFrame()运行在后台标签页或者隐藏的<iframe>里时,requestAnimationFrame()会被暂停调用以提升性能和电池寿命。
动画原理:
- - requestAnimationFrame:requestAnimationFrame()调用一个函数不是立即调用而是向浏览器发起一个执行某函数的请求, 什么时候会执行由浏览器决定,大约是每16.7ms调用一次。
- - 使用该方式实现动画的好处是:我们不需要告诉浏览器什么时候需要刷新屏幕。同时对CPU和GPU更友好。会让动画运行的更平缓。
- - 主要是修改对象的三个属性:位置(position)、旋转(rotation)和缩放(scale)来实现物体的旋转移动动画效果。
// 移动物体:实现在x轴的旋转效果
move() {
this.cube.rotation.x += 0.01
},
// 渲染下一针的时候就会调用 render
render() {
this.move()
this.renderer.render(this.scene, this.camera)
},
// 帧动画
animate(time) {
// 默认会传一个`time`,单位是ms
// 根据时间和速度计算移动距离
// 原本5是只渲染一次, 当相机通过轨道查看几何物体时,是按帧渲染的
let t = (time / 1000) % 5
this.cube.position.x = t * 1
// 运动就是在每一帧渲染的时候坐标轴的位置变化
// 小方块每帧在X轴上向前运动0.01,当运动到大于5时回到原点
if (this.cube.position.x >= 5) {
// 2. 移动距离
this.cube.position.x = 0
}
//使用渲染器,通过相机将场景渲染进来
this.render()
requestAnimationFrame(this.animate)
},
requestAnimationFrame()和 clock 的结合使用:
- - Clock 本质上就是对 Date 进行封装,提供了一些方法和属性,clock对象用于跟踪时间,用来解决时间相关的计算问题。;
- - 属性:
- - autoStart(Boolean):默认值是`true`,如果设置为`true`,则在第一次`update` 时开启时钟 Clock。
- - startTime(Float):存储时钟 Clock 最后一次调用`.start()`, `.getElapsedTime()`或`.getDelta()`方法的时间。
- - elapsedTime(Float):Float,保存时钟 Clock 运行的总时长。
- - running(Boolean):判断时钟 Clock 是否在运行。
- - 方法:
- - start ():启动时钟。同时将 `startTime` 和 `oldTime` 设置为当前时间。 设置 `elapsedTime` 为 0,并且设置 `running` 为 `true。`
- - stop ():停止时钟。同时将 `oldTime` 设置为当前时间。
- - getElapsedTime ():获取自时钟启动后的秒数,同时将`oldTime` 设置为当前时间;如果`autoStart` 设置为 `true` 且时钟并未运行,则该方法同时启动时钟。
- - getDelta ():获取自`oldTime` 设置后到当前的秒数。 同时将 `oldTime`设置为当前时间;如果`autoStart` 设置为 `true` 且时钟并未运行,则该方法同时启动时钟。
- - 属性:
// 设置时钟
this.clock = new THREE.Clock()
// 渲染下一针的时候就会调用 render
render() {
this.renderer.render(this.scene, this.camera)
},
// 帧动画
animate() {
// requestAnimationFrame 会默认传入进来time ,单位ms
// 浏览器刷新率是60帧/s,16帧/ms
// 获取时钟运行的总时长
// let totalTime = clock.getElapsedTime();
// 间隔时间,即oldtime到当前时间的秒数,同时将oldtime设置为当前时间
// oldtime :存储时钟最后一次调用start ,getElapsedTime或者getDelta方法 的时间
// let deltaTime = clock.getDelta();//两帧的时间差,这一帧到下一帧的时间差
// console.log('间隔时间',deltaTime)//0 此时为0 ,把clock.getElapsedTime()注释掉,则可以得到真正的间隔时间,大概是8ms,那么1000/8 大概是125帧/s
// 获取时钟运行的总时长
let clockTime = this.clock.getElapsedTime()
// 配合 Math.sin 和 Math.cos 会有更好的动画效果
this.smallBall.position.x = Math.sin(clockTime) * 3
this.smallBall.position.z = Math.cos(clockTime) * 3
this.smallBall.position.y = 2 + Math.sin(clockTime * 10)
this.render()
requestAnimationFrame(this.animate)
},
gsap动画库
gsap是一款以javascript实现方式实现的动画库,主要功能就是节省上面的运算时间, 需要你来设置在x, y, z轴上的变化。
gsap属性
- - x:100 => transform: translateX(100px) :在x轴水平移动100。
- - y:100 => transform: translateY(100px):在y轴水平移动100。
- - xPercent: -50 => transform: translateX(-50%):在x轴水平移动(元素宽度的百分比)。
- - yPercent: -50 => transform: translateY(-50%):在y轴水平移动(元素宽度的百分比)。
- - rotation: 360 => transform: rotate(360deg):旋转360°。
- - scale: 2 => transform: scale(2, 2):放大2倍。
- - transformOrigin: “0% 100%” => transform-origin: 0% 100%:平移中心,会围绕左下角旋转。
默认情况下,GSAP 将使用 px 和度数进行变换,但可以使用其他单位,例如 vw、弧度,甚至可以进行自己的 JS 计算或相对值!
GSAP特殊属性控制动画
- - duration:动画时长(秒)默认值:0.5。
- - delay:动画开始前的延迟时间(秒)。
- - repeat:动画应该重复多少次。
- - yoyo:如果为 true,每隔一次重复补间就会朝相反的方向运行。默认值:false。
- - stagger:每个目标动画开始的间隔时间(单位:秒)(如果提供了多个目标)。
- - ease: 在动画过程中控制变化的速度,比如动作的“个性”或感觉。默认值:“power1.out”。
- - onComplete:当动画完成时运行的函数。
gsap的基本使用
安装:
npm i --save gsap
引入使用:
// 导入动画库
import gsap from 'gsap'
添加动画:
-
gsap.to() – 这是最常见的补间类型。是设置当前元素或者变量的状态,到设置的状态的补间动画。所谓的补间动画,就是2个关键帧(即2种物体的状态)有了,框架自带计算出中间某个时刻的状态,从而填补2个状态间,动画的空白时刻,从而实现完整动画。
-
gsap.to有2个参数,第一个是目标元素或者变量。如果传入的是.box之类的css字符串选择器,GSAP 在后台使用
document.querySelectorAll()
选中页面的匹配的元素。当第一个目标是对象时,GSAP就会对其属性值进行修改来实现补间动画。
// 设置动画
let animate1 = gsap.to(
// 需要执行动画的参数对象
this.cube.position,
// 执行动画的目标参数
{
// 使盒子移动到 x 轴为 5 的位置
x: 10,
// 需要的时间,5秒
duration: 5,
// 动画执行方式
ease: 'power1.inOut',
// 循环次数,无限制循环是-1
repeat: -1,
// 往返运动
yoyo: true,
// 延迟两秒执行
delay: 2,
// 动画完成时执行的函数
onComplete: () => {
console.log('动画完成')
},
// 动画开始时执行的函数
onStart: () => {
console.log('动画开始')
},
}
)
添加双击暂停和开启动画:让双击画面,控制立方体动画暂停和恢复动画,前面创建的animate1这个动画实例,有isActive方法,可以用来获取当前动画是暂停还是播放状态,播放状态时isActive方法返回为true,暂停时为false,根据这个状态来调用pause方法来暂停动画和恢复动画。
window.addEventListener('dblclick', () => {
if (animate1.isActive()) {
// 暂停
animate1.pause()
} else {
// 恢复
animate1.resume()
}
})
gsap内建了requestAnimationFrame,因此不比手动更新动画,但是如果想看到立方体移动的效果,还是需要每帧重渲染场景。
Tween.js实现动画
tween.js在three.js中有很多使用场景,比如粒子动画,相机转场动画,物体运动。
基本用法:
tween.js可以平滑的修改元素的属性值。在配合动画函数实现动画效果
安装:
npm install @tweenjs/tween.js --save
引入:
import TWEEN from '@tweenjs/tween.js';
创建补间:
//创建tween对象并告诉它初始位置
const tween = new TWEEN.Tween(group.position)
指定过渡形式:
- - easing函数:
- - Linear:线性匀速运动效果
- - Quadratic:二次方的缓动
- - Cubic:三次方的缓动
- - Quartic:四次方的缓动
- - Quintic:五次方的缓动
- - Sinusoidal:正弦曲线的缓动
- - Exponential:指数曲线的缓动
- - Circular:圆形曲线的缓动
- - Elastic:指数衰减的正弦曲线缓动
- - Back:超过范围的三次方的缓动
- - Bounce:指数衰减的反弹缓动
- - easing类型:
- - In:easeIn,加速,先慢后快
- - Out:easeOut,减速,先快后慢
- - InOut:easeInOut,前半段加速,后半段减速
tween.easing(TWEEN.Easing.Sinusoidal.InOut);
依次执行多个动画:如果在程序中需要多个动画,并且有一定的先后顺序。那么就可以通过tween.chain()方法实现。
-
例如已经创建了tween1和tween2两个TWEEN对象。希望tween1结束后tween2开始。那么可以这样使用:tween1.chain(tween2);
-
如果你希望tween1结束后tween2开始,tween2结束后tween1开始,往复调用,那么可以这样写代码:tween1.chain(tween2);tween2.chain(tween1);
const tween = new TWEEN.Tween(group.position).to(
{ z: this.setting.maxZ },
5000);
tween.easing(TWEEN.Easing.Sinusoidal.InOut);
var tweenBack = new TWEEN.Tween(group.position).to(
{ z: this.setting.minZ },
5000);
tweenBack.easing(TWEEN.Easing.Sinusoidal.InOut);
tween.chain(tweenBack);
tweenBack.chain(tween);
补间动画功能:
- - to:指定动画的最终状态,控制补间的运动形式及方向 `new TWEEN.Tween().to()` , 当tween启动时,Tween.js将读取当前属性值并 应用相对值来找出新的最终值。
//通过tween对象的to()方法告诉它目标位置和所需时间
tween.to(
{ z: this.setting.maxZ },
// 指定动画时间 5s
5000
);
- repeat:动画执行次数,如果你想让一个动画执行指定次数。这时就可以使用`tween.repeat()`方法实现,它接收一个参数表示循环的次数,例如你希望某个动画执行10次,可以这样实现`tween.repeat(10)
tween.repeat(10)
- start:动画的开始的控制。上面已经提到了就是`tween.start()`,该函数可以接收一个参数,代表动画在参数指定的时间后开始执行。没有参数代表立即执行。
tween.start();
- stop:控制动画结束的函数就是`tween.stop()`,需要注意的是已经结束的动画和没有开始的动画对该函数不生效。
tween.stop()
-
yoyo:控制补间重复的模式 ,
new TWEEN.Tween().yoyo()
, 这个功能只有在使用 repeat 时才有效果 , 补间的 行,为将像悠悠球一样来回运动 , 而不是重新开始
tween.yoyo()
-
delay:控制补间开始前的延迟
new TWEEN.Tween().delay()
, 补间开始之前的延迟时间接受一个参数用于控制具 体时间
tween.delay(2)
-
pause:暂定补间动画
new TWEEN.Tween().pause()
, 暂停当前补间运动
tween.pause()
-
resume :恢复补间动画
new TWEEN.Tween().resume()
, 恢复这个已经被暂停的补间运动
tween.resume()
tween回调函数:
- - onStart :tween开始动画前的回调函数
- - onStop :tween结束动画后的回调函数
- - onUpdate :在tween每次被更新后的回调函数
- - onComplete :tween动画全部结束后的回调函数
由于Tween对象的每个函数调用都返回一个tween对象。所以也支持链式调用。以下代码与上一个例子实现同样的功能:
new TWEEN.Tween(group.position)
.to(
{
z: this.setting.maxZ
},
// 指定动画时间 5s
5000)
.easing(TWEEN.Easing.Linear.None)
.onComplete(function () {
console.log('测试');
console.log(_this.scene.children);
console.log(intersects[0].object);
});
.start()
调用Tween.update():为了实现平滑的动画效果,我们需要在一个循环动画中调用TWEEN.update方法,一般会把它放入在帧动画里循环,
// 帧动画
animate() {
this.render();
// 更新控制器
this.controls.update();
TWEEN.update();
this.renderer.render(this.scene, this.camera);
requestAnimationFrame(this.animate);
},