@@ -374,4 +374,215 @@ modelMatrix.translate(Tx, 0, 0) // 将模型矩阵乘以平移矩阵
374374
375375## 动画
376376
377- 将矩阵变换运用到动画图形中去
377+ 将矩阵变换运用到动画图形中去
378+
379+
380+ ### 动画基础
381+
382+ 为了让一个三角形转动起来,需要做到是:不断擦除和重绘三角形,并且在每次重绘时轻微地改变角度
383+
384+
385+ ``` js
386+ const VSHADER_SOURCE =
387+ ' attribute vec4 a_Position;\n ' +
388+ ' uniform mat4 u_ModelMatrix;\n ' +
389+ ' void main() {\n ' +
390+ ' gl_Position = u_ModelMatrix * a_Position;\n ' +
391+ // 'gl_PointSize = 10.0;\n' +
392+ ' }\n '
393+ const FSHADER_SOURCE =
394+ ' void main() {\n ' +
395+ ' gl_FragColor = vec4(1.0, 1.0, 0.0,1.0);\n ' +
396+ ' }\n '
397+
398+
399+ var ANGLE = 90.0
400+
401+ var ANGLE_STEP = 45.0 // [!code ++]
402+
403+ function main () {
404+ var canvas = document .getElementById (' webgl' )
405+ var gl = getWebGLContext (canvas)
406+ if (! gl) {
407+ console .error (' Failed to get the rendering context for WebGL' )
408+ return ;
409+ }
410+
411+ // 初始化着色器
412+ if (! initShaders (gl, VSHADER_SOURCE , FSHADER_SOURCE )) {
413+ console .error (' Failed to initialize shaders.' )
414+ return ;
415+ }
416+
417+ // 设置顶点着色器
418+ var n = initVertexBuffers (gl);
419+
420+
421+
422+ if (n < 0 ) {
423+ console .error (' Failed to set the positions of the vertices' )
424+ return ;
425+ }
426+
427+ gl .clearColor (0.0 , 0.0 , 0.0 , 1.0 ) // [!code ++]
428+
429+ var u_ModelMatrix = gl .getUniformLocation (gl .program , ' u_ModelMatrix' )
430+
431+ var currentAngle = 0.0 // [!code ++]
432+
433+ var modelMatrix = new Matrix4 ()
434+
435+ var tick = function () { // [!code ++]
436+ currentAngle = animate (currentAngle) // 更新旋转角 // [!code ++]
437+ draw (gl, n, currentAngle, modelMatrix, u_ModelMatrix) // [!code ++]
438+
439+ requestAnimationFrame (tick) // [!code ++]
440+
441+ } // [!code ++]
442+ tick () // [!code ++]
443+ var Tx = 0.5
444+
445+ // 平移旋转
446+ modelMatrix .setRotate (ANGLE , 0 , 0 , 1 )
447+ modelMatrix .translate (Tx, 0.5 , 1 )
448+
449+ // 旋转平移
450+ modelMatrix .setTranslate (Tx, 0.5 , 1 )
451+ modelMatrix .rotate (ANGLE , 0 , 0 , 1 )
452+
453+
454+
455+ gl .uniformMatrix4fv (u_ModelMatrix, false , modelMatrix .elements )
456+
457+
458+
459+ // 获取attribut变量的存储位置
460+ var a_Position = gl .getAttribLocation (gl .program , ' a_Position' )
461+ //
462+ // var u_FragColor = gl.getUniformLocation(gl.program, 'u_FragColor')
463+ if (a_Position < 0 ) {
464+ console .error (' Failed to get the storage location of a_Position' )
465+ return ;
466+ }
467+
468+ canvas .onmousedown = function (ev ) {click (ev, gl, canvas, a_Position, u_FragColor)}
469+
470+
471+ gl .vertexAttrib3f (a_Position, 0.0 , 0.0 , 0.0 )
472+
473+ // 设置canvas背景色
474+ gl .clearColor (0.0 , 0.0 , 0.0 , 1.0 )
475+
476+ // 清空canvas
477+ gl .clear (gl .COLOR_BUFFER_BIT );
478+
479+ // 绘制三个点
480+ gl .drawArrays (gl .TRIANGLES , 0 , n)
481+
482+ }
483+
484+ function draw (gl , n , currentAngle , modelMatrix , u_ModelMatrix ) { // [!code ++]
485+ // 设置旋转矩阵 // [!code ++]
486+ modelMatrix .setRotate (currentAngle, 0 , 0 , 1 ) // [!code ++]
487+ // 将旋转矩阵传输给顶点着色器 // [!code ++]
488+ gl .uniformMatrix4fv (u_ModelMatrix, false , modelMatrix .elements ) // [!code ++]
489+
490+ // 清除 canvas // [!code ++]
491+ gl .clear (gl .COLOR_BUFFER_BIT ) // [!code ++]
492+
493+ // 绘制三角形 // [!code ++]
494+
495+ gl .drawArrays (gl .TRIANGLES , 0 , n) // [!code ++]
496+ } // [!code ++]
497+
498+ var g_last = Date .now () // [!code ++]
499+
500+ function animate (angle ) { // [!code ++]
501+ var now = Date .now () // [!code ++]
502+
503+ var elapsed = now - g_last; // [!code ++]
504+
505+ g_last = now // [!code ++]
506+
507+ var newAngle = angle + (ANGLE_STEP * elapsed) / 1000.0 // [!code ++]
508+
509+ return newAngle %= 360 // [!code ++]
510+ } // [!code ++]
511+
512+ function initVertexBuffers (gl ) {
513+ var vertices = new Float32Array ([
514+ - 0.5 , 0.5 , - 0.5 , - 0.5 , 0.5 , 0.5 , 0.5 , - 0.5
515+ ])
516+ var n = 4 // 点的个数
517+
518+ // 创建缓冲区对象
519+ var vertexBuffer = gl .createBuffer ();
520+ if (! vertexBuffer) {
521+ console .error (' Failed to create the buffer object' )
522+ return - 1 ;
523+ }
524+
525+ // 将缓冲区对象绑定到目标
526+ gl .bindBuffer (gl .ARRAY_BUFFER , vertexBuffer)
527+
528+ // 向缓冲区对象中写入数据
529+ gl .bufferData (gl .ARRAY_BUFFER , vertices, gl .STATIC_DRAW );
530+
531+ var a_Position = gl .getAttribLocation (gl .program , ' a_Position' );
532+
533+ // 将缓冲区对象分配给a_Position变量
534+ gl .vertexAttribPointer (a_Position, 2 , gl .FLOAT , false , 0 , 0 )
535+
536+ // 连接a_Position变量与分配给它的缓冲对象
537+ gl .enableVertexAttribArray (a_Position)
538+ return n
539+ }
540+
541+ ```
542+
543+ ![ 效果图] ( ./images/xxt.gif )
544+
545+ ** 为了是三角形动起来,需要反复进行两步操作**
546+
547+ 1 . 更新三角形的当前角度 currentTriangle
548+ 2 . 调用绘制函数,根据当前角度绘制三角形
549+
550+ ### 请求再次被调用(requestAnimationFrame())
551+
552+ 如果想要Javascript重复执行某个特定的任务,可以使用` setInterval() ` 函数
553+
554+ ` setInterval(func, delay) `
555+ | 参数 | 描述 |
556+ | -------| ----------------|
557+ | func | 指定需要多次调用的函数 |
558+ | delay | 指定时间间隔(以毫秒为单位) |
559+ | 返回值 | Time id |
560+
561+ 现代浏览器都支持多个标签页,每个标签页具有单独的Javascript运行环境,但是自` setInterval() ` 函数诞生之初,浏览器还没有开始支持多标签页。所以在现代浏览器中,
562+ 不管是标签页是否被激活,其中的` setInterval() ` 函数都会反复调用func,如果标签页比较多,就会增加浏览器的负荷(现在浏览器有优化了,失活时间太长,它调用的次数会被减少)。所有后来,浏览器又引入了` requestAnimation() ` 方法,
563+ 该方法只有当标签页处于激活状态时才会生效。` requestAnimationFrame() ` 是新引入的方法。
564+
565+ ** requestAnimationFrame()**
566+ 当调用` requestAnimationFrame() ` 函数时,即是在告诉浏览器在将来的某个时间调用作为第一个参数的函数。
567+
568+ ` requestAnimationFrame(func) `
569+
570+ | 参数 | 描述 |
571+ | ------| -------------------------------------------|
572+ | func | 指定将来某个时刻调用的函数。函数将会接收一个time参数,用来表示此次调用的时间戳 |
573+ | 返回值 | Request id |
574+
575+
576+ 这个函数的好处是可以避免在未激活的标签页上运行动画。
577+
578+ 但是无法指定重复调用的间隔;函数func会在浏览器需要网页的某个元素重绘时被调用。此外还需要注意,在浏览器成功地调用了一次func后,想要再次被调用,就必须要再次发起请求,因为前一次请求已经结束。(也就是说,requestAnimationFrame更像setTimeOut而不是setInterval,
579+ 不会因为你发起一次请求,就会不停地循环调用func)。在调用函数后,需要发出下次调用的请求,因为上一次关于调用的请求在调用完成之后就结束了使命。
580+
581+ ` cancelAnimationFrame() `
582+
583+ 取消由` requestAnimationFrame() ` 发起的请求
584+
585+ | 参数 | 描述 |
586+ | -----------| -------------------------------|
587+ | requestID | 指定requestAnimationFrame()的返回值 |
588+ | 返回值 | 无 |
0 commit comments