Three.js基础探寻十——动画

  本篇将介绍如果使用Three.js进行动态画面的渲染。此外,将会介绍一个Three.js作者写的另外一个库stat.js,用来观测每秒帧数(FPS)。

1.实现动画效果

  1.1 动画原理

  对于Three.js程序而言,动画的实现是通过在每秒中多次重绘画面实现的。

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

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

  对于Three.js动画而言,一般FPS在30到60之间都是可取的。

  1.2 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;

    }

}        

  源码:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>3.js测试10.1</title>
    </head>
    <body onload="init()">
        <canvas id="mainCanvas" width="400px" height="300px" ></canvas>
        <button id="stopBtn" onclick="stop()">Stop</button>
    </body>
    <script type="text/javascript" src="js/three.min.js"></script>
    <script type="text/javascript">
        var scene = null;
        var camera = null;
        var renderer = null;

        var mesh = null;
        var id = null;

        function init() {
            renderer = new THREE.WebGLRenderer({
                canvas: document.getElementById(‘mainCanvas‘)
            });
            renderer.setClearColor(0x000000);
            scene = new THREE.Scene();

            camera = new THREE.OrthographicCamera(-5, 5, 3.75, -3.75, 0.1, 100);
            camera.position.set(5, 5, 20);
            camera.lookAt(new THREE.Vector3(0, 0, 0));
            scene.add(camera);

            mesh = new THREE.Mesh(new THREE.CubeGeometry(1, 2, 3),
                new THREE.MeshLambertMaterial({
                    color: 0xffff00
            }));
            scene.add(mesh);

            var light = new THREE.DirectionalLight(0xffffff);
            light.position.set(20, 10, 5);
            scene.add(light);

            id = setInterval(draw, 20);
        }

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

        function stop() {
            if (id !== null) {
                clearInterval(id);
                id = null;
            }
        }
    </script>
</html>

  你会看到一个傻缺在一直转。

  1.3 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较为“年轻”,因而一些老的浏览器使用的是试验期的名字:mozRequestAnimationFrame、webkitRequestAnimationFrame、msRequestAnimationFrame,为了支持这些浏览器,我们最好在调用之前,先判断是否定义了requestAnimationFrame以及上述函数:

var requestAnimationFrame = window.requestAnimationFrame

|| window.mozRequestAnimationFrame

|| window.webkitRequestAnimationFrame

|| window.msRequestAnimationFrame;

window.requestAnimationFrame = requestAnimationFrame;

  源码:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>3.js测试10.2</title>
    </head>
    <body onload="init()">
        <canvas id="mainCanvas" width="400px" height="300px" ></canvas>
        <button id="stopBtn" onclick="stop()">Stop</button>
    </body>
    <script type="text/javascript" src="js/three.min.js"></script>
    <script type="text/javascript">
        var requestAnimationFrame = window.requestAnimationFrame
                || window.mozRequestAnimationFrame
                || window.webkitRequestAnimationFrame
                || window.msRequestAnimationFrame;
        window.requestAnimationFrame = requestAnimationFrame;

        var scene = null;
        var camera = null;
        var renderer = null;

        var mesh = null;
        var id = null;

        function init() {
            renderer = new THREE.WebGLRenderer({
                canvas: document.getElementById(‘mainCanvas‘)
            });
            renderer.setClearColor(0x000000);
            scene = new THREE.Scene();

            camera = new THREE.OrthographicCamera(-5, 5, 3.75, -3.75, 0.1, 100);
            camera.position.set(5, 5, 20);
            camera.lookAt(new THREE.Vector3(0, 0, 0));
            scene.add(camera);

            mesh = new THREE.Mesh(new THREE.CubeGeometry(1, 2, 3),
                new THREE.MeshLambertMaterial({
                    color: 0xffff00
            }));
            scene.add(mesh);

            var light = new THREE.DirectionalLight(0xffffff);
            light.position.set(20, 10, 5);
            scene.add(light);

            id = requestAnimationFrame(draw);
        }

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

        function stop() {
            if (id !== null) {
                cancelAnimationFrame(id);
                id = null;
            }
        }
    </script>
</html>

  和上面的差不多,点stop会停止。

  1.4 两种方法的比较

  setInterval方法与requestAnimationFrame方法的区别较为微妙。一方面,最明显的差别表现在setInterval可以手动设定FPS,而requestAnimationFrame则会自动设定FPS;但另一方面,即使是setInterval也不能保证按照给定的FPS执行,在浏览器处理繁忙时,很可能低于设定值。当浏览器达不到设定的调用周期时,requestAnimationFrame采用跳过某些帧的方式来表现动画,虽然会有卡滞的效果但是整体速度不会拖慢,而setInterval会因此使整个程序放慢运行,但是每一帧都会绘制出来;总而言之,requestAnimationFrame适用于对于时间较为敏感的环境(但是动画逻辑更加复杂),而setInterval则可在保证程序的运算不至于导致延迟的情况下提供更加简洁的逻辑(无需自行处理时间)。

2.使用stat.js记录FPS

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

,单击后显示每帧渲染时间:

  首先,我们需要下载stat.js文件,可以在https://github.com/mrdoob/stats.js/blob/master/build/stats.min.js找到。下载后,将其放在项目文件夹下,然后在HTML中引用:

<script type="text/javascript" src="js/stat.js"></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效果了。

  源码:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>3.js测试10.4</title>
    </head>
    <body onload="init()">
        <canvas id="mainCanvas" width="400px" height="300px" ></canvas>
        <button id="stopBtn" onclick="stop()">Stop</button>
    </body>
    <script type="text/javascript" src="js/three.js"></script>
    <script type="text/javascript" src="js/stats.min.js"></script>
    <script type="text/javascript">
        var requestAnimationFrame = window.requestAnimationFrame
                || window.mozRequestAnimationFrame
                || window.webkitRequestAnimationFrame
                || window.msRequestAnimationFrame;
        window.requestAnimationFrame = requestAnimationFrame;

        var scene = null;
        var camera = null;
        var renderer = null;

        var mesh = null;
        var id = null;

        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);

            renderer = new THREE.WebGLRenderer({
                canvas: document.getElementById(‘mainCanvas‘)
            });
            renderer.setClearColor(0x000000);
            scene = new THREE.Scene();

            camera = new THREE.OrthographicCamera(-5, 5, 3.75, -3.75, 0.1, 100);
            camera.position.set(5, 5, 20);
            camera.lookAt(new THREE.Vector3(0, 0, 0));
            scene.add(camera);

            mesh = new THREE.Mesh(new THREE.CubeGeometry(1, 2, 3),
                new THREE.MeshLambertMaterial({
                    color: 0xffff00
            }));
            scene.add(mesh);

            var light = new THREE.DirectionalLight(0xffffff);
            light.position.set(20, 10, 5);
            scene.add(light);

            id = requestAnimationFrame(draw);
        }

        function draw() {
            stat.begin();

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

            stat.end();
        }

        function stop() {
            if (id !== null) {
                cancelAnimationFrame(id);
                id = null;
            }
        }
    </script>
</html>

整理自张雯莉《Three.js入门指南》

时间: 2024-11-02 09:18:40

Three.js基础探寻十——动画的相关文章

Three.js基础探寻九——网格

在学习了几何形状和材质之后,我们就能使用他们来创建物体了.最常用的一种物体就是网格(Mesh),网格是由顶点.边.面等组成的物体:其他物体包括线段(Line).骨骼(Bone).粒子系统(ParticleSystem)等.创建物体需要指定几何形状和材质,其中,几何形状决定了物体的顶点位置等信息,材质决定了物体的颜色.纹理等信息. 本篇将介绍创建较为常用的物体:网格,然后介绍如何修改物体的属性. 1.创建网格 在前几篇中,我们学习了如何创建几何形状与材质,而网格的创建非常简单,只要把几何形状与材质

Three.js基础探寻一

1.webGL 一种网络标准,定义了一些较底层的图形接口. 2.Three.js 一个3Djs库,webGL开源框架中比较优秀的一个.除了webGL以外,Three.js还提供了基于Canvas.SVG标签的渲染器. 这是一个开源项目. 3.环境 找一个喜欢的jsIDE.调试建议使用Chrome或者Firefox. 4.下载 传送门 5.使用 <head> <script type="text/javascript" src="js/three.js&quo

Three.js基础探寻四——立方体、平面与球体

前面简单介绍了webGL和Three.js的背景以及照相机的设定,接下来介绍一些Three.js中的几何形状. 1.立方体 虽然这一形状的名字叫立方体(CubeGeometry),但它其实是长方体,也就是长宽高可以设置为不同的值.其构造函数是: THREE.CubeGeometry(width,height,depth,widthSegments,heightSegments, depthSegments) width:x方向上的长度 height:y方向上的长度 depth:z方向上的长度 w

Bootstrap &lt;基础二十六&gt;进度条

Bootstrap 进度条.在本教程中,你将看到如何使用 Bootstrap 创建加载.重定向或动作状态的进度条. Bootstrap 进度条使用 CSS3 过渡和动画来获得该效果.Internet Explorer 9 及之前的版本和旧版的 Firefox 不支持该特性,Opera 12 不支持动画. 默认的进度条 创建一个基本的进度条的步骤如下: 添加一个带有 class .progress 的 <div>. 接着,在上面的 <div> 内,添加一个带有 class .prog

Bootstrap&lt;基础二十四&gt; 缩略图

Bootstrap 缩略图.大多数站点都需要在网格中布局图像.视频.文本等.Bootstrap 通过缩略图为此提供了一种简便的方式.使用 Bootstrap 创建缩略图的步骤如下: 在图像周围添加带有 class .thumbnail 的 <a> 标签. 这会添加四个像素的内边距(padding)和一个灰色的边框. 当鼠标悬停在图像上时,会动画显示出图像的轮廓. 下面的实例演示了默认的缩略图: <!DOCTYPE html> <html> <head> &l

Bootstrap &lt;基础二十五&gt;警告(Alerts)

警告(Alerts)以及 Bootstrap 所提供的用于警告的 class.警告(Alerts)向用户提供了一种定义消息样式的方式.它们为典型的用户操作提供了上下文信息反馈. 您可以为警告框添加一个可选的关闭按钮.为了创建一个内联的可取消的警告框,请使用 警告(Alerts) jQuery 插件. 您可以通过创建一个 <div>,并向其添加一个 .alert class 和四个上下文 class(即 .alert-success..alert-info..alert-warning..ale

Bootstrap &lt;基础二十八&gt;列表组

列表组.列表组件用于以列表形式呈现复杂的和自定义的内容.创建一个基本的列表组的步骤如下: 向元素 <ul> 添加 class .list-group. 向 <li> 添加 class .list-group-item. 下面的实例演示了这点: <!DOCTYPE html> <html> <head> <title>Bootstrap 实例 - 基本的列表组</title> <link href="/boo

Bootstrap &lt;基础二十二&gt;超大屏幕(Jumbotron)

Bootstrap 支持的另一个特性,超大屏幕(Jumbotron).顾名思义该组件可以增加标题的大小,并为登陆页面内容添加更多的外边距(margin).使用超大屏幕(Jumbotron)的步骤如下: 创建一个带有 class .jumbotron. 的容器 <div>. 除了更大的 <h1>,字体粗细 font-weight 被减为 200px. 下面的实例演示了这点: <!DOCTYPE html> <html> <head> <tit

Web3D编程入门总结——WebGL与Three.js基础介绍

1 /*在这里对这段时间学习的3D编程知识做个总结,以备再次出发.计划分成“webgl与three.js基础介绍”.“面向对象的基础3D场景框架编写”.“模型导入与简单3D游戏编写”三个部分,其他零散知识以后有机会再总结.*/ 2 /*第一部分,webgl与three.js基础介绍,要求读者掌握JavaScript入门知识*/ 3 //webgl原理:通过JavaScript语言在浏览器端生成glsl代码,把glsl代码送入显卡执行,把执行结果显示在浏览器中 4 //简单例程: 5 //根据To