threejs学习day6:动画

Posted 简数

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了threejs学习day6:动画相关的知识,希望对你有一定的参考价值。

在本章之前,所有画面都是静止的,本章将介绍如果使用Three.js进行动态画面的渲染。此外,将会介绍一个Three.js作者写的另外一个库,用来观测每秒帧数(FPS)。

动画原理


在这里,我们将动态画面简称为动画(animation)。正如动画片的原理一样,动画的本质是利用了人眼的视觉暂留特性,快速地变换画面,从而产生物体在运动的假象。而对于Three.js程序而言,动画的实现也是通过在每秒中多次重绘画面实现的。

为了衡量画面切换速度,引入了每秒帧数FPS(Frames Per Second)的概念,是指每秒画面重绘的次数。FPS越大,则动画效果越平滑,当FPS小于20时,一般就能明显感受到画面的卡滞现象。

那么FPS是不是越大越好呢?其实也未必。当FPS足够大(比如达到60),再增加帧数人眼也不会感受到明显的变化,反而相应地就要消耗更多资源(比如电影的胶片就需要更长了,或是电脑刷新画面需要消耗计算资源等等)。因此,选择一个适中的FPS即可。

NTSC标准的电视FPS是30,PAL标准的电视FPS是25,电影的FPS标准为24。而对于Three.js动画而言,一般FPS在30到60之间都是可取的。

setInterval方法


如果要设置特定的FPS(虽然严格来说,即使使用这种方法,javascript也不能保证帧数精确性),可以使用JavaScript DOM定义的方法:

setInterval(func, msec)

其中,func是每过msec毫秒执行的函数,如果将func定义为重绘画面的函数,就能实现动画效果。setInterval函数返回一个id,如果需要停止重绘,需要使用clearInterval方法,并传入该id,具体的做法为:

首先,在init函数中定义每20毫秒执行draw函数的setInterval,返回值记录在全局变量id中:

id = setInterval(draw, 20);

draw函数中,我们首先设定在每帧中的变化(毕竟,如果每帧都是相同的,即使重绘再多次,还是不会有动画的效果),这里我们让场景中的长方体绕y轴转动。然后,执行渲染:

function draw() {
    mesh.rotation.y = (mesh.rotation.y + 0.01) % (Math.PI * 2);
    renderer.render(scene, camera);
}

这样,每20毫秒就会调用一次draw函数,改变长方体的旋转值,然后进行重绘。最终得到的效果就是FPS为50的旋转长方体。

我们在html中添加一个按钮,按下后停止动画:

<button id="stopBtn" onclick="stop()">Stop</button>

对应的stop函数为:

function stop() {
    if (id !== null) {
        clearInterval(id);
        id = null;
    }
}

requestAnimationFrame方法


大多数时候,我们并不在意多久重绘一次,这时候就适合用requestAnimationFrame方法了。它告诉浏览器在合适的时候调用指定函数,通常可能达到60FPS。

requestAnimationFrame同样有对应的cancelAnimationFrame取消动画:

function stop() {
    if (id !== null) {
        cancelAnimationFrame(id);
        id = null;
    }
}

setInterval不同的是,由于requestAnimationFrame只请求一帧画面,因此,除了在init函数中需要调用,在被其调用的函数中需要再次调用requestAnimationFrame

function draw() {
    mesh.rotation.y = (mesh.rotation.y + 0.01) % (Math.PI * 2);
    renderer.render(scene, camera);
    id = requestAnimationFrame(draw);
}

因为requestAnimationFrame较为“年轻”,因而一些老的浏览器使用的是试验期的名字:mozRequestAnimationFramewebkitRequestAnimationFramemsRequestAnimationFrame,为了支持这些浏览器,我们最好在调用之前,先判断是否定义了requestAnimationFrame以及上述函数:

var requestAnimationFrame = window.requestAnimationFrame 
        || window.mozRequestAnimationFrame
        || window.webkitRequestAnimationFrame
        || window.msRequestAnimationFrame;
window.requestAnimationFrame = requestAnimationFrame;

如何取舍


setInterval方法与requestAnimationFrame方法的区别较为微妙。一方面,最明显的差别表现在setInterval可以手动设定FPS,而requestAnimationFrame则会自动设定FPS;但另一方面,即使是setInterval也不能保证按照给定的FPS执行,在浏览器处理繁忙时,很可能低于设定值。当浏览器达不到设定的调用周期时,requestAnimationFrame采用跳过某些帧的方式来表现动画,虽然会有卡滞的效果但是整体速度不会拖慢,而setInterval会因此使整个程序放慢运行,但是每一帧都会绘制出来;

总而言之,requestAnimationFrame适用于对于时间较为敏感的环境(但是动画逻辑更加复杂),而setInterval则可在保证程序的运算不至于导致延迟的情况下提供更加简洁的逻辑(无需自行处理时间)。

使用stat.js记录FPS

stat.js是Three.js的作者Mr. Doob的另一个有用的JavaScript库。很多情况下,我们希望知道实时的FPS信息,从而更好地监测动画效果。这时候,stat.js就能提供一个很好的帮助,它占据屏幕中的一小块位置(如左上角),效果为:,单击后显示每帧渲染时间:。

首先,我们需要下载stat.js文件,下载后,将其放在项目文件夹下,然后在HTML中引用:

<script type="text/javascript" ></script>

在页面初始化的时候,对其初始化并将其添加至屏幕一角。这里,我们以右上角为例:

var stat = null;

function init() {
    stat = new Stats();
    stat.domElement.style.position = 'absolute';
    stat.domElement.style.right = '0px';
    stat.domElement.style.top = '0px';
    document.body.appendChild(stat.domElement);

    // Three.js init ...
}

然后,在上一节介绍的动画重绘函数draw中调用stat.begin();stat.end();分别表示一帧的开始与结束:

function draw() {
    stat.begin();

    mesh.rotation.y = (mesh.rotation.y + 0.01) % (Math.PI * 2);
    renderer.render(scene, camera);

    stat.end();
}

最终就能得到FPS效果了。


以上是关于threejs学习day6:动画的主要内容,如果未能解决你的问题,请参考以下文章

angular8 + threejs 实现太阳系3D动画

threejs+blender页面滚动伴随着三维动画

Threejs基础代码段Tweenjs补间动画

Threejs动画初探

一大波ThreeJS学习笔记来啦

用组件的方式开发threejs多个模型动画网页