JavaScript高级程序设计笔记 18

第18章 动画与 Canvas 图形

1. 使用 requestAnimationFrame(RAF)

  • 问题:setTimeout/setInterval 制作动画不精确,与屏幕刷新率不同步,导致丢帧或卡顿。
  • RAF:告诉浏览器在下次重绘之前执行指定回调,通常每秒 60 次(匹配屏幕刷新率)。
  • 用法:
    function update() {
      // 更新动画逻辑
      requestAnimationFrame(update);
    }
    requestAnimationFrame(update);
    
  • 优势:浏览器优化,页面不可见时暂停,节省 CPU/电池;回调参数为高精度时间戳(DOMHighResTimeStamp)。
  • 取消:cancelAnimationFrame(id)

2. 基础 Canvas 用法

  • <canvas> 元素:画布,需要指定 widthheight(不用 CSS 设置,否则缩放)。
  • 获取上下文:canvas.getContext('2d') 返回 CanvasRenderingContext2D 对象;canvas.getContext('webgl') 返回 WebGL 上下文。
  • 检测支持:!!context

3. 2D 上下文绘图

3.1 填充与描边

  • fillStyle:填充颜色、渐变或图案。
  • strokeStyle:描边颜色。
  • 默认黑色,支持 CSS 颜色字符串、渐变对象、图案对象。

3.2 矩形(唯一直接绘制形状)

  • fillRect(x, y, width, height):填充矩形。
  • strokeRect(x, y, width, height):描边矩形。
  • clearRect(x, y, width, height):清除矩形区域(变透明)。

3.3 路径

  • 开始路径:beginPath()
  • 常用路径方法:
    • arc(x, y, radius, startAngle, endAngle, counterclockwise):圆弧。
    • arcTo(x1, y1, x2, y2, radius)
    • bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y):三次贝塞尔。
    • quadraticCurveTo(cpx, cpy, x, y):二次贝塞尔。
    • lineTo(x, y):直线。
    • moveTo(x, y):移动笔触。
    • rect(x, y, width, height):矩形路径。
  • 闭合路径:closePath()
  • 绘制路径:fill()stroke()

3.4 文本

  • fillText(text, x, y, maxWidth?)strokeText()
  • 字体样式:font(CSS 字体字符串)、textAligntextBaseline
  • 测量文本宽度:measureText(text).width

3.5 变换

  • 平移:translate(x, y)
  • 旋转:rotate(angle)(弧度)。
  • 缩放:scale(sx, sy)
  • 变换矩阵:transform(a, b, c, d, e, f)setTransform()
  • 保存/恢复状态:save()restore()(堆栈)。

3.6 阴影

  • shadowColorshadowOffsetXshadowOffsetYshadowBlur

3.7 渐变与图案

  • 线性渐变:createLinearGradient(x0, y0, x1, y1)addColorStop(offset, color)
  • 径向渐变:createRadialGradient(x0, y0, r0, x1, y1, r1)
  • 图案:createPattern(image, repetition)(重复方式同 CSS)。

3.8 图像绘制

  • drawImage(image, x, y, width?, height?) 或带裁剪参数。
  • 图像源:HTMLImageElementHTMLCanvasElementHTMLVideoElementImageBitmap
  • 像素操作:
    • getImageData(x, y, width, height) 返回 ImageData 对象(含 datawidthheight)。
    • putImageData(imageData, x, y)
    • createImageData(width, height)

3.9 合成与裁剪

  • globalAlpha:全局透明度(0~1)。
  • globalCompositeOperation:控制新图形如何与已有图形合成。
  • 裁剪区域:clip()(基于当前路径设置裁剪区域)。

4. WebGL(3D 上下文)

  • WebGL 基于 OpenGL ES 2.0,用于在 Canvas 中渲染 2D/3D 图形。

4.1 获取 WebGL 上下文

let gl = canvas.getContext('webgl');
if (!gl) gl = canvas.getContext('experimental-webgl'); // 兼容旧版

4.2 基本概念与初始化

  • 视口(viewport)gl.viewport(x, y, width, height) 定义绘制区域(通常为画布尺寸)。
  • 清空(clear)gl.clearColor(r, g, b, a) 设置背景色;gl.clear(gl.COLOR_BUFFER_BIT) 清空颜色缓冲区。
  • 缓冲区:用于存储顶点数据、颜色等。

4.3 着色器(Shader)

  • WebGL 使用 GLSL(OpenGL Shading Language)编写。
  • 顶点着色器(Vertex Shader):处理顶点位置、变换等。
  • 片元着色器(Fragment Shader):处理像素颜色、纹理等。
  • 步骤:
    1. 创建着色器对象:gl.createShader(type)gl.VERTEX_SHADERgl.FRAGMENT_SHADER)。
    2. 设置源码:gl.shaderSource(shader, source)
    3. 编译着色器:gl.compileShader(shader)
    4. 创建程序对象:gl.createProgram()
    5. 附加着色器:gl.attachShader(program, shader)
    6. 链接程序:gl.linkProgram(program)
    7. 使用程序:gl.useProgram(program)

4.4 缓冲区与绘制

  • 创建缓冲区:gl.createBuffer()
  • 绑定缓冲区:gl.bindBuffer(gl.ARRAY_BUFFER, buffer)
  • 传入数据:gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW)
  • 获取属性位置:gl.getAttribLocation(program, 'aPosition')
  • 启用属性:gl.enableVertexAttribArray(location)
  • 指定解析方式:gl.vertexAttribPointer(location, size, type, normalize, stride, offset)
  • 绘制:gl.drawArrays(gl.TRIANGLES, start, count)gl.drawElements()

4.5 纹理

  • 创建纹理对象:gl.createTexture()
  • 绑定纹理:gl.bindTexture(gl.TEXTURE_2D, texture)
  • 设置参数(如 gl.TEXTURE_MIN_FILTERgl.TEXTURE_MAG_FILTER)。
  • 上传图像:gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, format, type, image)
  • 在片元着色器中使用 uniform sampler2D 采样。

4.6 动画与变换

  • 使用 uniform 传递矩阵(如模型视图矩阵、投影矩阵)。
  • 常用库:gl-matrix 或手动计算矩阵。
  • 每帧更新 uniform 变量,调用 gl.drawArrays 重绘。

4.7 WebGL 2.0

  • 基于 OpenGL ES 3.0,增加更多特性(如 3D 纹理、实例化渲染、变换反馈)。
  • 上下文:canvas.getContext('webgl2')

5. 动画循环(结合 2D 与 WebGL)

  • 基本模式:
    function draw(now) {
      // 更新状态(位置、矩阵等)
      // 清空画布或缓冲区
      // 绘制新帧
      requestAnimationFrame(draw);
    }
    requestAnimationFrame(draw);
    
  • 平滑动画:根据时间差(now - lastTimestamp)计算移动距离,保证速度与帧率无关。
  • 常用技术:移动、碰撞检测、粒子系统等。

6. 性能与注意事项

  • 避免在高频动画中执行复杂计算或 DOM 操作。
  • 离屏渲染:使用多个 Canvas 或 OffscreenCanvas 提升性能。
  • 注意内存:及时释放 ImageData 和大尺寸图像。
  • 高 DPI 适配:考虑 devicePixelRatio 调整画布尺寸。
  • WebGL 中谨慎使用 gl.readPixels,避免性能瓶颈。
  • 纹理尺寸建议为 2 的幂(如 256x256),以避免兼容性问题。

7. 小结

  • 动画帧用 RAF,与刷新率同步不卡顿。
  • Canvas 2D 上下文,矩形路径文本图像。
  • 变换 save/restore,渐变阴影图案全。
  • WebGL 上下文 webgl,着色器缓冲区画三角。
  • 纹理矩阵加动画,3D 图形也能搞。

8. 常见面试题速查

  1. requestAnimationFramesetTimeout 优势?
    → 与屏幕刷新同步,页面不可见时暂停,性能好、省电,回调参数为高精度时间戳。

  2. Canvas 中如何绘制一个圆?
    beginPath()arc(x, y, radius, 0, 2*Math.PI)fill()stroke()

  3. 如何清除画布?
    clearRect(0, 0, canvas.width, canvas.height)

  4. fillStyle 可以赋哪些值?
    → 颜色字符串、渐变对象(createLinearGradient)、图案对象(createPattern)。

  5. 如何实现图像灰度化?
    getImageData,遍历像素数据,计算灰度值(r*0.299 + g*0.587 + b*0.114),重新 putImageData

  6. save()restore() 的作用?
    → 保存和恢复绘图状态(变换矩阵、样式、阴影、裁剪区域等),支持堆栈嵌套。

  7. 什么是离屏渲染?
    → 在不可见的 Canvas 上先绘制,再一次性绘制到主画布,减少重绘开销。

  8. 如何适配高分屏?
    → 根据 devicePixelRatio 放大画布尺寸(canvas.width = width * ratio),再用 CSS 缩回逻辑大小。

  9. WebGL 中顶点着色器和片元着色器的职责?
    → 顶点着色器处理顶点位置;片元着色器处理像素颜色。

  10. WebGL 如何绘制一个三角形?
    → 定义顶点数组,创建缓冲区,编写着色器,使用 gl.drawArrays(gl.TRIANGLES, 0, 3)

posted @ 2024-05-06 09:26  Li_pk  阅读(9)  评论(0)    收藏  举报