本篇将介绍如果使用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定义的方法:

  1. setInterval(func, msec)

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

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

  1. id = setInterval(draw, 20);

  在draw函数中,我们首先设定在每帧中的变化,这里我们让场景中的长方体绕y轴转动。然后,执行渲染:

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

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

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

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

  对应的stop函数为:

  1. function stop() {
  2.  
  3. if (id !== null) {
  4.  
  5. clearInterval(id);
  6.  
  7. id = null;
  8.  
  9. }
  10.  
  11. }

  源码:

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>3.js测试10.1</title>
  6. </head>
  7. <body onload="init()">
  8. <canvas id="mainCanvas" width="400px" height="300px" ></canvas>
  9. <button id="stopBtn" onclick="stop()">Stop</button>
  10. </body>
  11. <script type="text/javascript" src="js/three.min.js"></script>
  12. <script type="text/javascript">
  13. var scene = null;
  14. var camera = null;
  15. var renderer = null;
  16.  
  17. var mesh = null;
  18. var id = null;
  19.  
  20. function init() {
  21. renderer = new THREE.WebGLRenderer({
  22. canvas: document.getElementById('mainCanvas')
  23. });
  24. renderer.setClearColor(0x000000);
  25. scene = new THREE.Scene();
  26.  
  27. camera = new THREE.OrthographicCamera(-5, 5, 3.75, -3.75, 0.1, 100);
  28. camera.position.set(5, 5, 20);
  29. camera.lookAt(new THREE.Vector3(0, 0, 0));
  30. scene.add(camera);
  31.  
  32. mesh = new THREE.Mesh(new THREE.CubeGeometry(1, 2, 3),
  33. new THREE.MeshLambertMaterial({
  34. color: 0xffff00
  35. }));
  36. scene.add(mesh);
  37.  
  38. var light = new THREE.DirectionalLight(0xffffff);
  39. light.position.set(20, 10, 5);
  40. scene.add(light);
  41.  
  42. id = setInterval(draw, 20);
  43. }
  44.  
  45. function draw() {
  46. mesh.rotation.y = (mesh.rotation.y + 0.01) % (Math.PI * 2);
  47. renderer.render(scene, camera);
  48. }
  49.  
  50. function stop() {
  51. if (id !== null) {
  52. clearInterval(id);
  53. id = null;
  54. }
  55. }
  56. </script>
  57. </html>

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

  1.3 requestAnimationFrame方法

  如果不在意多久重绘一次,可以使用requestAnimationFrame方法。它告诉浏览器在合适的时候调用指定函数,通常可能达到60FPS。

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

  1. function stop() {
  2.  
  3. if (id !== null) {
  4.  
  5. cancelAnimationFrame(id);
  6.  
  7. id = null;
  8.  
  9. }
  10.  
  11. }

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

  1. function draw() {
  2.  
  3. mesh.rotation.y = (mesh.rotation.y + 0.01) % (Math.PI * 2);
  4.  
  5. renderer.render(scene, camera);
  6.  
  7. id = requestAnimationFrame(draw);
  8.  
  9. }

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

  1. var requestAnimationFrame = window.requestAnimationFrame
  2.  
  3. || window.mozRequestAnimationFrame
  4.  
  5. || window.webkitRequestAnimationFrame
  6.  
  7. || window.msRequestAnimationFrame;
  8.  
  9. window.requestAnimationFrame = requestAnimationFrame;

  源码:

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>3.js测试10.2</title>
  6. </head>
  7. <body onload="init()">
  8. <canvas id="mainCanvas" width="400px" height="300px" ></canvas>
  9. <button id="stopBtn" onclick="stop()">Stop</button>
  10. </body>
  11. <script type="text/javascript" src="js/three.min.js"></script>
  12. <script type="text/javascript">
  13. var requestAnimationFrame = window.requestAnimationFrame
  14. || window.mozRequestAnimationFrame
  15. || window.webkitRequestAnimationFrame
  16. || window.msRequestAnimationFrame;
  17. window.requestAnimationFrame = requestAnimationFrame;
  18.  
  19. var scene = null;
  20. var camera = null;
  21. var renderer = null;
  22.  
  23. var mesh = null;
  24. var id = null;
  25.  
  26. function init() {
  27. renderer = new THREE.WebGLRenderer({
  28. canvas: document.getElementById('mainCanvas')
  29. });
  30. renderer.setClearColor(0x000000);
  31. scene = new THREE.Scene();
  32.  
  33. camera = new THREE.OrthographicCamera(-5, 5, 3.75, -3.75, 0.1, 100);
  34. camera.position.set(5, 5, 20);
  35. camera.lookAt(new THREE.Vector3(0, 0, 0));
  36. scene.add(camera);
  37.  
  38. mesh = new THREE.Mesh(new THREE.CubeGeometry(1, 2, 3),
  39. new THREE.MeshLambertMaterial({
  40. color: 0xffff00
  41. }));
  42. scene.add(mesh);
  43.  
  44. var light = new THREE.DirectionalLight(0xffffff);
  45. light.position.set(20, 10, 5);
  46. scene.add(light);
  47.  
  48. id = requestAnimationFrame(draw);
  49. }
  50.  
  51. function draw() {
  52. mesh.rotation.y = (mesh.rotation.y + 0.01) % (Math.PI * 2);
  53. renderer.render(scene, camera);
  54. id = requestAnimationFrame(draw);
  55. }
  56.  
  57. function stop() {
  58. if (id !== null) {
  59. cancelAnimationFrame(id);
  60. id = null;
  61. }
  62. }
  63. </script>
  64. </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中引用:

  1. <script type="text/javascript" src="js/stat.js"></script>

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

  1. var stat = null;
  2.  
  3. function init() {
  4.  
  5. stat = new Stats();
  6.  
  7. stat.domElement.style.position = 'absolute';
  8.  
  9. stat.domElement.style.right = '0px';
  10.  
  11. stat.domElement.style.top = '0px';
  12.  
  13. document.body.appendChild(stat.domElement);
  14.  
  15. // Three.js init ...
  16.  
  17. }

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

  1. function draw() {
  2.  
  3. stat.begin();
  4.  
  5. mesh.rotation.y = (mesh.rotation.y + 0.01) % (Math.PI * 2);
  6.  
  7. renderer.render(scene, camera);
  8.  
  9. stat.end();
  10.  
  11. }

  最终就能得到FPS效果了。

  源码:

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>3.js测试10.4</title>
  6. </head>
  7. <body onload="init()">
  8. <canvas id="mainCanvas" width="400px" height="300px" ></canvas>
  9. <button id="stopBtn" onclick="stop()">Stop</button>
  10. </body>
  11. <script type="text/javascript" src="js/three.js"></script>
  12. <script type="text/javascript" src="js/stats.min.js"></script>
  13. <script type="text/javascript">
  14. var requestAnimationFrame = window.requestAnimationFrame
  15. || window.mozRequestAnimationFrame
  16. || window.webkitRequestAnimationFrame
  17. || window.msRequestAnimationFrame;
  18. window.requestAnimationFrame = requestAnimationFrame;
  19.  
  20. var scene = null;
  21. var camera = null;
  22. var renderer = null;
  23.  
  24. var mesh = null;
  25. var id = null;
  26.  
  27. var stat = null;
  28.  
  29. function init() {
  30. stat = new Stats();
  31. stat.domElement.style.position = 'absolute';
  32. stat.domElement.style.right = '0px';
  33. stat.domElement.style.top = '0px';
  34. document.body.appendChild(stat.domElement);
  35.  
  36. renderer = new THREE.WebGLRenderer({
  37. canvas: document.getElementById('mainCanvas')
  38. });
  39. renderer.setClearColor(0x000000);
  40. scene = new THREE.Scene();
  41.  
  42. camera = new THREE.OrthographicCamera(-5, 5, 3.75, -3.75, 0.1, 100);
  43. camera.position.set(5, 5, 20);
  44. camera.lookAt(new THREE.Vector3(0, 0, 0));
  45. scene.add(camera);
  46.  
  47. mesh = new THREE.Mesh(new THREE.CubeGeometry(1, 2, 3),
  48. new THREE.MeshLambertMaterial({
  49. color: 0xffff00
  50. }));
  51. scene.add(mesh);
  52.  
  53. var light = new THREE.DirectionalLight(0xffffff);
  54. light.position.set(20, 10, 5);
  55. scene.add(light);
  56.  
  57. id = requestAnimationFrame(draw);
  58. }
  59.  
  60. function draw() {
  61. stat.begin();
  62.  
  63. mesh.rotation.y = (mesh.rotation.y + 0.01) % (Math.PI * 2);
  64. renderer.render(scene, camera);
  65. id = requestAnimationFrame(draw);
  66.  
  67. stat.end();
  68. }
  69.  
  70. function stop() {
  71. if (id !== null) {
  72. cancelAnimationFrame(id);
  73. id = null;
  74. }
  75. }
  76. </script>
  77. </html>

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

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

  1. Three.js基础探寻一

    1.webGL 一种网络标准,定义了一些较底层的图形接口. 2.Three.js 一个3Djs库,webGL开源框架中比较优秀的一个.除了webGL以外,Three.js还提供了基于Canvas.SV ...

  2. Three.js基础探寻二——正交投影照相机

    本篇主要介绍照相机中的正交投影照相机. 第一篇传送门:Three.js基础探寻一 1.照相机 图形学中的照相机定义了三维空间到二维屏幕的投影方式. 针对投影方式照相机分为正交投影照相机和透视投影照相机 ...

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

    在学习了几何形状和材质之后,我们就能使用他们来创建物体了.最常用的一种物体就是网格(Mesh),网格是由顶点.边.面等组成的物体:其他物体包括线段(Line).骨骼(Bone).粒子系统(Partic ...

  4. Three.js基础探寻八——法向材质与材质的纹理贴图

    4.法向材质 法向材质可以将材质的颜色设置为其法向量的方向,有时候对于调试很有帮助. 法向材质的设定很简单,甚至不用设置任何参数: new THREE.MeshNormalMaterial() 材质的 ...

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

    前面简单介绍了webGL和Three.js的背景以及照相机的设定,接下来介绍一些Three.js中的几何形状. 1.立方体 虽然这一形状的名字叫立方体(CubeGeometry),但它其实是长方体,也 ...

  6. Three.js基础探寻三——透视投影照相机

    本篇主要介绍Three.js照相机中的透视投影照相机. 上一篇:正交投影照相机 5.透视投影照相机构造函数 透视投影照相机(Perspective Camera)的构造函数是: THREE.Persp ...

  7. Three.js基础探寻五——正二十面体、圆环面等

    除了立方体.平面.球体,Three.js还提供了很多其他几何形状. 1.圆形 CircleGeometry可以创建圆形或者扇形: THREE.CircleGeometry(radius, segmen ...

  8. Three.js基础探寻七——Lamber材质与Phong材质

    材质(Material)是独立于物体顶点信息之外的与渲染效果相关的属性.通过设置材质可以改变物体的颜色.纹理贴图.光照模式等. 本篇将介绍基本材质以及两种基于光照模型的材质(Lamber与Phong) ...

  9. Three.js基础探寻六——文字形状与自定义形状

    1.文字形状 说起3d文字想起了早年word里的一些艺术字: 时间真快. 那么TextGeometry可以用来创建三维的文字形状. 使用文字形状需要下载和引用额外的字体库.这里,我们以 helveti ...

随机推荐

  1. Eclipse中用User Library管理jar包

    目的:为了更方便的管理jar包,而不是一股脑儿的将引用的jar包全部放在Web App Library下. 管理和配置: 第一步:管理 新建Library并引入项目中 右键项目->Build P ...

  2. Java管道流

    管道流的主要作用可以用于两个线程之间的通信,有管道输出流 PipeOutputStream和管道输入流 PipeInputStream.然后通过connect将两个管道连接起来. import jav ...

  3. (转)面试题--JAVA中静态块、静态变量加载顺序详解

    public class Test { //1.第一步,准备加载类 public static void main(String[] args) { new Test(); //4.第四步,new一个 ...

  4. 【转】Java 5种字符串拼接方式性能比较。

    最近写一个东东,可能会考虑到字符串拼接,想了几种方法,但对性能未知,于是用Junit写了个单元测试. 代码如下: import java.util.ArrayList; import java.uti ...

  5. java正则表达式取括号里面的内容

    public static String changeCompName(String compName){ String NewCompName=""; //cm1230NHL6X ...

  6. phpcms list页实现分页

    {pc:content action="lists" catid="41" order="id ASC" num="1" ...

  7. ubuntu下安装与卸载qt的方法

    http://blog.csdn.net/huyisu/article/details/24014407 ubuntu下安装与卸载qt的方法 分类: linux 2014-04-18 14:20 18 ...

  8. lifecycle of opensource products--x86-64

    x86是指intel的开发的一种32位指令集,从386开始时代开始的,一直沿用至今,是一种cisc指令集,所有intel早期的cpu,amd早期的cpu都支持这种指令集,ntel官方文档里面称为“IA ...

  9. hadoop、hbase、hive、zookeeper版本对应关系

    本文引用自:http://www.aboutyun.com/blog-61-62.html 最新版本: hadoop和hbase版本对应关系: Hbase    Hadoop 0.92.0 1.0.0 ...

  10. jquery mobile 方法收集.

    1.在列表项和按钮上禁用文本截断     如果你的列表项或者按钮上是一个很长的文本,它将会被jQuery Mobile自动截断,要禁用这个截断设置,需要在CSS选择器上添加属性"white- ...