-
Notifications
You must be signed in to change notification settings - Fork 12
/
Copy pathcanvas_interactive_cubes_tween.html
267 lines (232 loc) · 10.7 KB
/
canvas_interactive_cubes_tween.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
<!DOCTYPE html>
<html lang="en">
<head>
<title>three.js canvas - interactive - cubes tween</title>
<meta charset="utf-8">
<!--
如果没有设置viewport的width的话,网页很可能会超出手机屏幕宽度,具体多宽,要看浏览器定义的默认宽度是多少
user-scalable=no,规定了用户不能缩放网页,但有些浏览器对该项支持不是很好,故需要设置minimum-scale和maximum-scale相同来限制用户缩放
-->
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<style>
body {
font-family: Monospace;
background-color: #f0f0f0;
margin: 0px;
overflow: hidden;
}
</style>
</head>
<body>
<script src="../build/three.js"></script>
<!--
想要使用CanvasRenderer,必须添加如下两个js文件
Projector.js顾名思义上将3d图像投影到Canvas("2d")上,如果没有该文件会报如下错误
THREE.Projector has been moved to /examples/js/renderers/Projector.js. three.js:42883:3
TypeError: THREE.RenderableVertex is not a constructor
-->
<script src="js/renderers/Projector.js"></script>
<script src="js/renderers/CanvasRenderer.js"></script>
<!--
统计插件(FPS,渲染时间,chrome内存使用率),min表示js代码经过压缩
-->
<script src="js/libs/stats.min.js"></script>
<!--
对属性的改变,生成平滑的补间动画效果
-->
<script src="js/libs/tween.min.js"></script>
<script>
var container, stats;
var camera, scene, renderer;
var raycaster;
var mouse;
init();
animate();
function init() {
container = document.createElement( 'div' );
document.body.appendChild( container );
var info = document.createElement( 'div' );
info.style.position = 'absolute';
info.style.top = '10px';
info.style.width = '100%';
info.style.textAlign = 'center';
info.innerHTML = '<a href="http://threejs.org" target="_blank">three.js</a> - clickable objects';
container.appendChild( info );
/*
透视相机
PerspectiveCamera(fov, aspect, near, far)
fov(视场):从相机位置能够看到的部分场景。推荐默认值45
aspect(长宽比):渲染结果输出区域的横向长度和纵向长度的比值。推荐默认值window.innerWidth/window.innerHeight
near(近面):定义从距离相机多近的地方开始渲染场景。推荐默认值0.1
far(远面):定义相机可以从它所处的位置看多远。默认值1000
*/
camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 1, 10000 );
//定义相机的位置,有如下两种方式。如果不设置的话,相机位置为默认的Vector3{x:0,y:0,z:0}
//camera.position.y = 300;
//camera.position.z = 500;
camera.position.set( 0, 300, 500 );
scene = new THREE.Scene();
var geometry = new THREE.BoxGeometry( 100, 100, 100 );
for ( var i = 0; i < 20; i ++ ) {
/*
MeshBasicMaterial:与光照无关,仅根据材质的颜色或贴图来渲染物体
color:材质的颜色
map:材质的贴图
wireframe: 显示三角形线框还是显示面
side:可选的值有THREE.FrontSide(仅渲染正面)、THREE.BackSide(仅渲染背面)、THREE.DoubleSide(双面渲染)
overdraw:过渡描绘。如果用THREE.CanvasRenderer对象,有缝隙时需设置该值。例如当前如果使用0.5以下的值,三角形的分界线就很明显。但是使用WebGLRenderer则不会有分割线
*/
var object = new THREE.Mesh( geometry, new THREE.MeshBasicMaterial( { color: Math.random() * 0xffffff, opacity: 0.5 } ) );
object.position.x = Math.random() * 800 - 400;
object.position.y = Math.random() * 800 - 400;
object.position.z = Math.random() * 800 - 400;
object.scale.x = Math.random() * 2 + 1;
object.scale.y = Math.random() * 2 + 1;
object.scale.z = Math.random() * 2 + 1;
object.rotation.x = Math.random() * 2 * Math.PI;
object.rotation.y = Math.random() * 2 * Math.PI;
object.rotation.z = Math.random() * 2 * Math.PI;
scene.add( object );
}
//射线
raycaster = new THREE.Raycaster();
mouse = new THREE.Vector2();
renderer = new THREE.CanvasRenderer();
//设置渲染器的"清除色"和"透明度"
renderer.setClearColor( 0xf0f0f0 );
//设置屏幕像素比,与Android上的DIP相仿,作用是在所有设备上的显示效果都相近
renderer.setPixelRatio( window.devicePixelRatio );
//设置待渲染场景的大小
renderer.setSize( window.innerWidth, window.innerHeight );
//将渲染器的DOM元素(即Canvas)添加到HTML中
container.appendChild(renderer.domElement);
//左上角的统计信息(FPS,渲染时间,chrome内存使用率)
stats = new Stats();
//这里注意,统计插件的dom元素是"dom",而不是domElement
container.appendChild( stats.dom );
/*
element.addEventListener(event, function, useCapture)
useCapture,可选。true:事件句柄在捕获阶段执行;false:默认,事件句柄在冒泡阶段执行
*/
//mouse事件是针对PC浏览器的
document.addEventListener( 'mousedown', onDocumentMouseDown, false );
//touch事件是针对手持设备的
document.addEventListener( 'touchstart', onDocumentTouchStart, false );
window.addEventListener( 'resize', onWindowResize, false );
}
function onWindowResize() {
//重新设置相机的宽高比。如果宽高比不对,那么正方形可能就不是正方形了
camera.aspect = window.innerWidth / window.innerHeight;
//更新透视相机的投影矩阵
camera.updateProjectionMatrix();
//更新待渲染场景的大小
renderer.setSize( window.innerWidth, window.innerHeight );
}
function onDocumentTouchStart( event ) {
//通知 Web 浏览器不要执行与事件关联的默认动作(如果存在这样的动作)
event.preventDefault();
/*
html的坐标轴是以左上角为(0,0),右下方向为正方向
event.clientX=event.pageX返回当事件被触发时鼠标指针向对于浏览器可见区域的X坐标
event.offsetX返回当前事件相对于事件源元素(srcElement)的X坐标
event.screenX鼠标相对于用户显示器屏幕左上角的X坐标
*/
event.clientX = event.touches[0].clientX;
event.clientY = event.touches[0].clientY;
onDocumentMouseDown( event );
}
function onDocumentMouseDown( event ) {
event.preventDefault();
/*
mouse.x = (2 * event.clientX - renderer.domElement.clientWidth) / renderer.domElement.clientWidth
mouse.y = (renderer.domElement.clientHeight - 2 * event.clientY) / renderer.domElement.clientHeight
鼠标位置在一个边长为2的正方形内部,正方形中心为(0,0)点
因此,mouse.x和mouse.y的取值范围是[-1,1]
*/
mouse.x = ( event.clientX / renderer.domElement.clientWidth ) * 2 - 1;
mouse.y = - ( event.clientY / renderer.domElement.clientHeight ) * 2 + 1;
//设置该射线从相机位置发出,射向视场的鼠标位置
raycaster.setFromCamera( mouse, camera );
//判断射线是否穿过这些物体,参数是数组。返回的是与射线相交的结果数组,按距离从近到远有序排列
var intersects = raycaster.intersectObjects( scene.children );
/*
intersects[ 0 ] {
distance: double
face: Face3
faceIndex: int
object: Mesh
point: Vector3
uv: Vector2
__proto__: Object
}
*/
if ( intersects.length > 0 ) {
/*
运动算法Easing名称列表:
Linear:线性匀速运动效果;
Quadratic:二次方的缓动(t^2);
Cubic:三次方的缓动(t^3);
Quartic:四次方的缓动(t^4);
Quintic:五次方的缓动(t^5);
Sinusoidal:正弦曲线的缓动(sin(t));
Exponential:指数曲线的缓动(2^t);
Circular:圆形曲线的缓动(sqrt(1-t^2));
Elastic:指数衰减的正弦曲线缓动;
Back:超过范围的三次方缓动((s+1)*t^3 – s*t^2);
Bounce:指数衰减的反弹缓动。
每个效果都分三个缓动方式,分别是:
In:从0开始加速的缓动,也就是先慢后快;
Out:减速到0的缓动,也就是先快后慢;
InOut:前半段从0开始加速,后半段减速到0的缓动。
position: Vector3{x,y,z}
delay: function,延迟执行,单位毫秒
to: function to(properties, duration)
properties: object{x,y,z},注意properties的key必须在Tween的参数中能找到
duration: 动画时间,单位毫秒
onComplete: function,动画完成后的回调
start: function,开始动画
设置补间动画的作用属性,然后设置属性的目标值,最后开启补间动画,在requestAnimationFrame中更新补间
*/
new TWEEN.Tween( intersects[ 0 ].object.position ).to( {
x: Math.random() * 800 - 400,
y: Math.random() * 800 - 400,
z: Math.random() * 800 - 400 }, 2000 )
.easing( TWEEN.Easing.Elastic.Out).start();
new TWEEN.Tween( intersects[ 0 ].object.rotation ).to( {
x: Math.random() * 2 * Math.PI,
y: Math.random() * 2 * Math.PI,
z: Math.random() * 2 * Math.PI }, 2000 )
.easing( TWEEN.Easing.Elastic.Out).start();
}
/*
// Parse all the faces
for ( var i in intersects ) {
//这句已经无效了,因为face下面不存在material,face下存在color,但是即使改变color也改变不了面的颜色
//intersects[ i ].face.material[ 0 ].color.setHex( Math.random() * 0xffffff | 0x80000000 );
intersects[ i ].object.material.color.setHex( Math.random() * 0xffffff );
}
*/
}
function animate() {
requestAnimationFrame( animate );
render();
//这里可以在render前后使用stats.begin和stats.end,也可以在每次渲染的时候调用一次stats.update
stats.update();
}
var radius = 600;
var theta = 0;
function render() {
//更新补间动画
TWEEN.update();
theta += 0.1;
//THREE.Math.degToRad是将角度转换为弧度
camera.position.x = radius * Math.sin( THREE.Math.degToRad( theta ) );
camera.position.y = radius * Math.sin( THREE.Math.degToRad( theta ) );
camera.position.z = radius * Math.cos( THREE.Math.degToRad( theta ) );
//调整相机视角,对着场景中央
camera.lookAt( scene.position );
renderer.render( scene, camera );
}
</script>
</body>
</html>