现阶段前端页面所使用的动画技术方案总结

1. CSS3 动画

CSS3动画基于DOM的CSS样式,主要分为以下几个核心概念:

1.1 transition 过渡

transition 是一个动画元素的附加属性,它可以定义动画产生时的效果与方式,以及动画所作用的范围。

过渡可以为一个元素在不同状态之间切换的时候定义不同的过渡效果。比如在不同的伪元素之间切换,像是 :hover:active 或者通过 JavaScript 实现的状态变化。

transition CSS 属性是 transition-propertytransition-durationtransition-timing-functiontransition-delay 的一个简写属性。

1.2 transform

在 CSS3 当中,transform 用于元素进行旋转、缩放、移动或倾斜,和设置样式的动画并没有什么关系,就相当于color一样用来设置元素的“外表”。transform 分为 2D 转换与 3D 转换:

2D

transform 的 2D 转换可以让元素在 2D 平面中进行样式的变化, transform 不会让元素脱离标准流,所以元素发生变化后仍保留元素原有的占位。

2D转换的主要 API 包括:缩放(scale)、移动(translate)、旋转(rotate)、翻转(skew)以及转换函数 (matrix)。

3D

transform 3D 变化是在一个三维坐标中进行的:

img

3D 变换与 2D 变化不同的是空间上增加了 Z 轴,可以让元素在空间上进行改变。

通过设置 perspective (视距)属性可以实现在 Z 轴空间上的透视效果;通过设置 perspective-origin (透视原点)可以设置观察的位置,不同的观察位置会对物体产生不同的形变效果。

transform 3d 的位移方法为 translate3d(x,y,z) ,与 2d 位移不同的是增加了 Z 轴方向的坐标,同样的缩放也提供了一个 Z 轴方向上的缩放 scale3d(x,y,z),旋转提供了物体可以在空间中绕坐标轴进行旋转的能力 rotate3d(x,y,z,Ndeg)

matrix 矩阵

实际上,所有的形变操作都是通过矩阵matrix实现的,而 matrix 同样分为 2d 操作与 3d 操作。

目前有多种用来描述转换坐标模型,最常用的是 笛卡尔坐标系统齐次坐标

2d matrix:

matrix(a,b,c,d,e,f)函数有a,b,c,d,e,f这6个参数。而x和y是变形前元素的任意点。通过以下矩阵变换,生成对应的新坐标x’和y’。

img

1
2
x' = ax + cy + e;
y' = bx + dy + f;

由此可得到默认a、d为1,b、c、e、f为0。a和d控制缩放,且不可为0;c和b控制倾斜;而e和f控制位移

3d matrix:

 3d变形函数位移、旋转和缩放都是通过矩阵设置不同的参数而实现的。相比于2d矩阵martrix()的6个参数而言,3d矩阵matrix3d却有12个参数。其变形规则与2dmatrix()类似,只不过是从33矩阵,变成了44矩阵

1
matrix3d(a,b,c,0,d,e,f,0,g,h,i,0,j,k,l,1)

img

1.3 animation

animation 定义了元素使用一组由 @keyframes 定义的动画,并且规定了动画的持续时间执行次数等,完整的定义如下:

1
animation: 定义的动画名称 持续时间  执行次数  是否反向  运动曲线 延迟执行(infinite 表示无限次)

CSS animation 属性是 animation-nameanimation-duration, animation-timing-functionanimation-delayanimation-iteration-countanimation-directionanimation-fill-modeanimation-play-state 属性的一个简写属性形式。

使用 @keyframes 定义关键帧动画可以制作更为复杂且需要连续的动画,通过定义百分比或者关键字可以定义动画时间内不同时间段的动画。

animation 有一个很显然的缺点就是 CSS3 动画没有时间轴的概念,通常去控制多个关键帧动画之间的切换需要用到延迟参数,将每个动画拼接出来:

动画时间轴

1.4 事件

虽然 css 不支持时间轴,拼接多个动画需要手动设置每个动画的延迟时间,但是同时可以通过 javascript 来对元素添加动画事件的监听来捕获动画开始、结束等时机:

事件 描述
animationstart 如果需要,在动画开始时发生,计算任何动画延迟(由 animation-delay 属性指定)。负延迟会导致事件以等于延迟绝对值的经过时间触发。
animationend 动画结束时发生。
animationiteration 在动画的每次迭代结束时发生。仅当 animation-iteration-count 属性设置为大于1的值时,才会发生此事件。
transitionend transitionend 事件会在 CSS transition 结束后触发. 当transition完成前移除transition时,比如移除css的transition-property 属性,事件将不会被触发.如在transition完成前设置 display 为”none”,事件同样不会被触发。

PS:使用 Javascript 编写动画的 Web Animations API 现仍处于草案阶段:https://drafts.csswg.org/web-animations/

1.5 性能

性能优化

实现页面动画的效果有多种途径,比如将一个页面元素的高度从100px渐变到200px,可以直接改变元素的height属性,也可以操控CSS transform属性,但是前者会对引起页面的relayout以及对向集合线程发送重新结算的bitmap信息,具体的流程如下:

img

所以高度的变化是很耗时的,但是如果我们改用CSS transform属性来缩放元素,总体的处理过程如下:

img

依据规范,CSS transform属性并不会触发当前元素或附近元素的relayout。浏览器将当前元素视为一个整体,它会缩放、旋转、移动这一整个元素。浏览器只需要在动画开始之时生成位图,然后将位图发送给GPU。之后浏览器不需要做额外的relayout和repaint,甚至不需要发送位图给GPU。浏览器只需要充分发挥GPU的长处:绘制同一张位图到不同的位置、旋转角度和缩放比例。

CSS 对比 JavaScript 的性能

网络上有很多网页和评论从性能的角度讨论了 CSS 和 JavaScript 动画的相对优点。以下是要记住的几个要点:

  • 基于 CSS 的动画以及原生支持的网络动画通常由一个名为“合成器线程”的线程处理。这不同于在其中执行样式、布局、绘制和 JavaScript 的浏览器“主线程”。这意味着,如果浏览器正在主线程上运行一些高开销任务,则这些动画可以继续运行而不中断。
  • 在许多情况下,变形和透明度的其他更改还可由合成器线程来处理。
  • 如果任何动画触发绘制、布局或同时触发这两者,则“主线程”将必须执行工作。这点同时适用于基于 CSS 和 JavaScript 的动画,并且布局或绘制的开销可能拖慢与 CSS 或 JavaScript 执行相关的任何工作,使问题变得无意义。

更多:https://developers.google.com/web/fundamentals/design-and-ux/animations/animations-and-performance?hl=zh-cn#css-vs-javascript-performance

2. Canvas 2D

Canvas 是 HTML5 中取代 Flash 的新技术,Canvas 和 Flash 的思路完全不一样,Flash是上屏幕之后还是对象,编程语言叫做 Action Script 也是ECMAScript范畴。Canvas上屏幕之后像素化了,再也不能得到这个对象了,所以要想让这个元素运动,必须擦除整个屏幕、重绘这个元素,相比之下Canvas更加流畅,移动端也能保持很高的流畅度。

<Canvas> 是一个可以使用脚本(通常为JavaScript)来绘制图形的 HTML 元素.例如,它可以用于绘制图表、制作图片构图或者制作简单的(以及不那么简单的)动画。

2.1 Canvas 的基本结构

canvas 元素本身没有任何外观,它就是一块空白画板,提供给JS的一套API,最早由 Safari 引入,IE9之前可以使用一些类库在IE中模拟canvas,大部分的API都不在canvas元素自身定义,canvas元素自身属性与常规的HTML元素并没有多大区别。

1
2
3
4
5
6
7
<html>
<head>
<title>坐标系demo</title>
</head>
<body>
<canvas id = 'square' width= 200 heigth=200></canvas>
</body>

2.2 上下文对象

我们通常使用 ctx 变量作为 Canvas 操作的上下文对象,对 Canvas 上像素的操作即对该上下文的操作。

<canvas> 元素有一个叫做 getContext() 的方法:

1
canvas.getContext(contextType, contextAttributes);

这个方法是用来获得渲染上下文和它的绘画功能

参数 说明
contextType 上下文类型,一个字符串,值可以是 2dwebgl
contextAttributes 创建上下文时设置属性,一个字典对象,默认为空即可

对于 2d 图像而言,可以使用下面的代码获取渲染上下文

1
2
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');

ctx 现在 CanvasRenderingContext2D 的一个实例。

2.3 Canvas 的绘制过程

Canvas 有一个概念称作”笔触“也叫作“描边”,Canvas中的任何形状都是由这两个部分组成的。

笔触在canvas中视为一个“Path”的实例,必须stroke之后才能上屏幕;填充用fill才能上屏幕。

绘制路径

使用 beginPath() 代表开始绘制;moveTo(x, y) 代表将笔触移动到某一坐标;lineTo(x, y) 代表直线绘制到某一点;使用 stroke() 能将绘制的图像显示出来。

如下可以绘制出一条折线:

1
2
3
4
5
ctx.beginPath();
ctx.moveTo(100, 100);
ctx.lineTo(300, 300);
ctx.lineTo(600, 300);
ctx.stroke();

img

使用 closePath() 可以将最后一个绘制点【该绘制点是被用moveTo(x, y)打断绘制前的那个点】与第一个绘制点连接:

1
2
3
4
5
6
  ctx.beginPath();
ctx.moveTo(100, 100);
ctx.lineTo(300, 300);
ctx.lineTo(600, 300);
+ ctx.closePath();
ctx.stroke();

img

连续使用 moveTo() 可以让开始新的绘制点:

1
2
3
4
5
6
7
8
ctx.beginPath();
ctx.moveTo(100, 100);
ctx.lineTo(300, 300);
ctx.lineTo(600, 300);

ctx.moveTo(600, 400);
ctx.lineTo(700, 400);
ctx.stroke();

img

绘制样式

在绘制前可以对绘制的线段进行样式设置:

1
2
3
4
ctx.lineWidth = "10";
ctx.strokeStyle = "red";
... ...
ctx.stock();

img

在绘制之后,使用 fill() 可以填充封闭图像(如果没有封闭也会自动封闭)的颜色,设置 fillStyle 属性可以来设置填充颜色:

1
2
3
4
... ...
ctx.stock();
ctx.fillStyle = "skyblue"
ctx.fill();

img

使用了 fill() 之后,Canvas会自动认为之前的绘制操已经绘制了一个独立的图形。

快速绘制

strokeReact(x, y, w, h)快速绘制一个矩形

1
2
ctx.fillStyle = "lightseagreen"
ctx.strokeReact(100, 100, 300, 200);

img

filRecto是一个快捷方法,让你省略了beginPath、move To、lineTo。所以fillRect(100,100,300,200)等价于:

1
2
3
4
5
6
ctx.move(100,100);
ctx.lineTo(400,100);
ctx.lineTo(400,300);
ctx.lineTo(100,300);
ctx.closePath();
ctx.fill();

绘制复杂图像

1
2
3
4
5
6
7
8
for (var i = 0; i <= 500; i += 10) {
ctx.beginPath();
ctx.moveTo(i, i);
ctx.lineTo(i + 200, i);
ctx.lineTo(i, i + 300);
ctx.closePath();
ctx.stroke();
}

img

2.4 Canvas动画

如果想要在 Canvas 2D 画布中绘制出一组动画,那么需要进行如下几个步骤:

  1. 清空 canvas
    除非接下来要画的内容会完全充满 canvas (例如背景图),否则你需要清空所有。最简单的做法就是用 clearRect 方法。
  2. 保存 canvas 状态
    如果你要改变一些会改变 canvas 状态的设置(样式,变形之类的),又要在每画一帧之时都是原始状态的话,你需要先保存一下。
  3. 绘制动画图形(animated shapes)
    这一步才是重绘动画帧。
  4. 恢复 canvas 状态
    如果已经保存了 canvas 的状态,可以先恢复它,然后重绘下一帧。

2.5 性能优化策略

使用 requestAnimationFrame

requestAnimationFrame采用系统时间间隔,保持最佳绘制效率,不会因为间隔时间过短,造成过度绘制,增加开销;也不会因为间隔时间太长,使用动画卡顿不流畅,让各种网页动画效果能够有一个统一的刷新机制,从而节省系统资源,提高系统性能,改善视觉效果。

大多数电脑显示器的刷新频率是60Hz,大概相当于每秒钟重绘60次。大多数浏览器都会对重绘操作加以限制,不超过显示器的重绘频率,因为即使超过那个频率用户体验也不会有提升。因此,最平滑动画的最佳循环间隔是1000ms/60,约等于16.6ms

requestAnimationFrame 的特点:

  • requestAnimationFrame会把每一帧中的所有DOM操作集中起来,在一次重绘或回流中就完成,并且重绘或回流的时间间隔紧紧跟随浏览器的刷新频率
  • 在隐藏或不可见的元素中,requestAnimationFrame将不会进行重绘或回流,这当然就意味着更少的CPU、GPU和内存使用量
  • requestAnimationFrame是由浏览器专门为动画提供的API,在运行时浏览器会自动优化方法的调用,并且如果页面不是激活状态下的话,动画会自动暂停,有效节省了CPU开销

使用离屏 canvas 优化性能

如果要将相似图元重复绘制到屏幕的多个帧上,可以通过预呈现场景中较大的部分从而显著提升性能。预呈现是指在一张(或多张)离屏画布上呈现临时图片,然后使用 drawImage() 方法将离屏画布重新呈现到显示的画布上。离屏canvas的操作都是在内存中进行并且离屏canvas的任何操作都不会导致页面变(重绘、重排)

避免浮点数的坐标点,用整数取而代之

当你画一个没有整数坐标点的对象时会发生子像素渲染。

1
ctx.drawImage(myImage, 0.3, 0.5);

浏览器为了达到抗锯齿的效果会做额外的运算。为了避免这种情况,请保证在你调用 drawImage() 函数时,用Math.floor()函数对所有的坐标点取整。

集中批量调用画布

绘制操作的性能开销较高,因此效率更高的做法是,加载带有一长串命令的绘制状态机,然后通过状态机将命令全部转储到缓冲区中,最后集中进行绘制(stock)操作

关闭透明度

如果使用画布而且不需要透明,当使用 HTMLCanvasElement.getContext() 创建一个绘图上下文时把 alpha 选项设置为 false 。这个选项可以帮助浏览器进行内部优化。

1
var ctx = canvas.getContext('2d', { alpha: false });

使用多层画布去画一个复杂的场景

在绘制图像时,特别是制作游戏时,有些元素不断地改变或者移动,而其它的元素,例如外观,永远不变。这种情况的一种优化是去用多个画布元素去创建不同层次。

例如,我们可以在最顶层创建一个外观层,而且仅仅在用户输入的时候被画出。还可以创建一个游戏层,在上面会有不断更新的元素和一个背景层,给那些较少更新的元素。

其他方案

对于Canvas的优化策略大多都集中在如何减少画面的重绘工作量,这样可以通过降低Canvas的资源消耗从而提升性能,除了上述的常见优化方案,还有以下的优化方案:

  • 尽量利用 CSS
  • 尽量不要频繁地调用比较耗时的API
  • 渲染绘制操作不要频繁调用
  • 尽量少的改变状态机 ctx的里状态
  • 尽量少的调用 canvas API
  • 用CSS transforms特性缩放画布
  • 不要在用drawImage时缩放图像
  • 将画布的函数调用集合到一起(例如,画一条折线,而不要画多条分开的直线)
  • 使用不同的办法去清除画布 clearRect() vs. fillRect() vs. 调整canvas大小

3. WebGL

WebGL是一种JavaScript API,用于在不使用插件的情况下在任何兼容的网页浏览器中呈现交互式2D和3D图形。WebGL完全集成到浏览器的所有网页标准中,可将影像处理和效果的GPU加速使用方式当做网页Canvas的一部分。

WebGL元素可以加入其他HTML元素之中并与网页或网页背景的其他部分混合。WebGL程序由JavaScript编写的句柄和OpenGL Shading Language(GLSL)编写的着色器代码组成,该语言类似于C或C++,并在计算机的图形处理器(GPU)上运行。

20190820204937.png

3.1 WebGL核心概念

着色器

在OpenGL ES 2.0 中可以使用着色器编程,意味着可以调用显卡并行运算的能力,来进行业务中需要的开发。

着色器是使用 OpenGL ES Shading Language(GLSL)编写的程序,它携带着绘制形状的顶点信息以及构造绘制在屏幕上像素的所需数据,换句话说,它负责记录着像素点的位置和颜色。

绘制WebGL时候有两种不同的着色器函数, 顶点着色器(VertexShader)片段着色器(FragmentShader) 。开发者需要通过用GLSL 编写这些着色器,并将代码文本传递给WebGL, 使之在GPU执行时编译。

顶点着色器和片元着色器的代码是单独分开的,使用GLSL语言编写。

顶点着色器的作用是计算顶点的位置。根据计算出的一系列顶点位置,WebGL 可以对点、线和三角形在内的一些图元进行光栅化处理。当对这些图元进行光栅化处理时需要使用片段着色器方法。片段着色器的作用是计算出当前绘制图元中每个像素的颜色值。

光栅化是指将构成图形的一系列的点进行上色。

渲染管线

在开发WebGL应用程序时,我们需要写Shader语言代码与GPU进行沟通。使用JavaScript编写的程序,其中包括以下操作的控制代码:

  • 初始化WebGL − JavaScript是用于初始化WebGL的上下文。
  • 创建数组 − 我们创建JavaScript数组来保存几何数据。
  • 缓冲区对象 − 通过将数组作为参数来创建缓冲区对象(顶点和索引)。
  • 着色器 − 我们创建,编译和使用JavaScript链接着色器。
  • 属性− 我们可以创建属性,启用它们并使用JavaScript缓冲区对象相关联。
  • 制服− 我们还可以使用 JavaScript 制服(uniforms)关联。
  • 变换矩阵 − 使用JavaScript,我们可以创建变换矩阵。

WebGL 渲染管线在 WebGL Api 下的执行过程如下图所示:

20190820212148.png

缓冲区

buffer 是一个重要的概念,开发者在 js 中定义的坐标不能够直接使用,必须将原数据绑定到一个顶点着色器 buffer 中,再将这个顶点着色器与 WebGL 绑定,获取到在 GLSL 语言编写的着色器代码变量,buffer 可以自动将开发者编写的2d坐标转化为三维坐标点,再传入着色器代码中,以下是绘制一个三角形所需要生成的缓冲区:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function initVertexBuffers(gl) {
// 传入三角形的三个顶点到 vertices
var vertices = new Float32Array([
0, 0.5, -0.5, -0.5, 0.5, -0.5
])
// 顶点个数
var n = 3
// 创建一个 buffer
var vertexBuffer = gl.createBuffer()
// 将 vertexBuffer 与 webgl 绑定
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer)
// 将数据写入到 vertexBuffer 中
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW)
// 获取变量 a_Position 在 vertex shader 中的地址
var a_Position = gl.getAttribLocation(gl.program, 'a_Position')
// 将 bufferData 传入到 a_Position 的地址,同时需要规定一个顶点对应数组中的几个数据
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0)
// 启用 a_Position variable
gl.enableVertexAttribArray(a_Position)
return n
}
var n = initVertexBuffers(gl)

但是绘制于缓冲区的图像并不能展示在视图上,需要通过绘制才能展现:

1
2
3
4
5
6
7
8
9
gl.clearColor(0, 0, 0, 1)
// 绘制
function draw() {
// 调用clear方法将当前绘制结果清空
gl.clear(gl.COLOR_BUFFER_BIT)
// 按照三角形的图源去绘制,从 buffer 的起始位获取数据,绘制 n 个顶点
gl.drawArrays(gl.TRIANGLES, 0, n)
}
draw()

矩阵运算

如果我们想让图像从初始位置移动到另一个位置,则需要通过相应的计算来得出目标位置的坐标,在WebGL中,坐标的运算则是需要通过矩阵运算得出的,最常见的移动为平移与绕轴旋转,他们的常用计算公式如下:

在平面平移时的矩阵计算:

20190821151121.png

在旋转时的矩阵运算:

20190821151156.png

绕轴渲染的矩阵运算:

20190821151215.png

20190821151248.png

利用旋转矩阵,与计算好的矩阵与原坐标向量 (a_Position) 相乘,就可以得到图形处理后的向量了,利用这个原理就可以移动图形了。我们可以将定点着色器的GLSL代码改写为:

1
2
3
4
5
6
7
VSHADER_SOURCE = `
attribute vec4 a_Position;
uniform mat4 u_ModelMatrix;
void main(){
gl_Position = a_Position * u_ModelMatrix;
}
`

定义了一个常量 u_ModelMatrix 我们传入这个常量与原向量相乘就可以得到新的顶点坐标位置。

3.2 ThreeJS

ThreeJS 概述

由于原生WebGL相对复杂,所以使用ThreeJS可以大幅减少开发成本,ThreeJS 将常用的 WebGL 表达式、算法、图形封装,以便开发者可以便捷使用,减少对 WebGL 复杂 api 的使用。

使用 ThreeJS 的优势:

  • 弥补原生 WebGL 的缺乏抽象和模块化的缺点
  • 简便图形学算法的实现
  • 简化 GLSL 开发和调试,尽量避免使用 GLSL

缺点:

  • 缺少自由度

以下是 ThreeJS 中封装的主要对象:

渲染器 Renderer

在场景中设立了物体与光线以及相机后,需要渲染器将场景渲染出来。

Renderer要绑定一个canvas对象,实例化一个 Renderer 的过程如下:

1
2
3
4
var canvas = document.getElementById("demo-canvas");
var renderer = new THREE.WebGLRenderer({
canvas: canvas
})

通过 Render 可以设置背景色与大小,通常这个大小与整个画布相等:

1
2
renderer.setClearColor(new THREE.Color(0x000000, 1.0))
renderer.setSize(400, 400)

当完成了相机和场景的定义后,就可以使用渲染器上的 render() 方法将其渲染到画面上,第一个参数位传入实例化的 scene,第二个参数位传入实例化的 camera:

1
renderer.render(scene, camera)

通常渲染器会放在一个render函数中被重复调用,渲染器在每次渲染时会自动将上一帧场景清除,重新绘制一帧,这样不停的重新渲染,就会产生动态效果了:

1
2
3
4
5
var render = function () {
// ... 每一帧对场景进行应有的变动
renderer.render(scene, camera)
requestAnimationFrame(render)
}

相机 Camera

在 ThreeJS 中相机分为两种相机,分别是 正交投影相机透视投影相机

正交投影相机 OrthographicCamera:

注:图中的”视点”对应着Three中的Camera。

这里补充一个视景体的概念:视景体是一个几何体,只有视景体内的物体才会被我们看到,视景体之外的物体将被裁剪掉。这是为了去除不必要的运算。

正交投影相机的视景体是一个长方体,OrthographicCamera的构造函数是这样的:OrthographicCamera( left, right, top, bottom, near, far )

Camera本身可以看作是一个点,left则表示左平面在左右方向上与Camera的距离。另外几个参数同理。于是六个参数分别定义了视景体六个面的位置。

可以近似地认为,视景体里的物体平行投影到近平面上,然后近平面上的图像被渲染到屏幕上。

实例化一个简单的正交相机可以使用 new THREE.OrthographicCamera() 传入的参数分别为定义的空间范围(上下左右前后):

1
var camera = new THREE.OrthographicCamera(-width / 2, width / 2, height / 2, -height / 2, -1000, 1000)

在一个3D的空间中,相机需要摆放到一个固定的点去观察物体,同时还要设置观察的方向:

1
2
3
4
5
// 相机由 (0,0,100) 的坐标望向 (0,0,0) 的坐标
camera.position.x = 0
camera.position.y = 0
camera.position.z = 100
camera.lookAt(new THREE.Vector3(0, 0, 0))

假如我们在点 (0,0,0) 处设置了一个平面三角形,按照相机的摆放位置看上去是这样的:

20190824174049.png

当将相机摆放在(100,100,100)的位置,即摆放在三角形的右上角,观察三角形的情况为:

20190824174230.png

由于我们使用了正交相机,图形没有近大远小的效果,看起来很奇怪,但是由 AxisHelp 坐标可以看出视角已经发生了变化

透视投影相机:

透视投影相机的视景体是个四棱台,它的构造函数是这样的:PerspectiveCamera( fov, aspect, near, far )

fov对应着图中的视角,是上下两面的夹角。aspect是近平面的宽高比。在加上近平面距离near,远平面距离far,就可以唯一确定这个视景体了。

透视投影相机很符合我们通常的看东西的感觉,因此大多数情况下我们都是用透视投影相机展示3D效果。

场景:

场景是所有物体的容器,也对应着我们创建的三维世界,只有我们在 scene 中添加的物体才会被展示出来。

创建一个 scene 实例:

1
var scene = new THREE.Scene()

向scene中添加一个物体,如AxisHelper(辅助坐标,可以帮助我们观察场景):

1
2
var axesHelper = new THREE.AxisHelper(100)
scene.add(this.axesHelper)