JavaScript高级程序设计笔记 18
第18章 动画与 Canvas 图形
1. 使用 requestAnimationFrame(RAF)
- 问题:
setTimeout/setInterval制作动画不精确,与屏幕刷新率不同步,导致丢帧或卡顿。 - RAF:告诉浏览器在下次重绘之前执行指定回调,通常每秒 60 次(匹配屏幕刷新率)。
- 用法:
function update() { // 更新动画逻辑 requestAnimationFrame(update); } requestAnimationFrame(update); - 优势:浏览器优化,页面不可见时暂停,节省 CPU/电池;回调参数为高精度时间戳(DOMHighResTimeStamp)。
- 取消:
cancelAnimationFrame(id)。
2. 基础 Canvas 用法
<canvas>元素:画布,需要指定width和height(不用 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 字体字符串)、textAlign、textBaseline。 - 测量文本宽度:
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 阴影
shadowColor、shadowOffsetX、shadowOffsetY、shadowBlur。
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?)或带裁剪参数。- 图像源:
HTMLImageElement、HTMLCanvasElement、HTMLVideoElement、ImageBitmap。 - 像素操作:
getImageData(x, y, width, height)返回ImageData对象(含data、width、height)。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):处理像素颜色、纹理等。
- 步骤:
- 创建着色器对象:
gl.createShader(type)(gl.VERTEX_SHADER或gl.FRAGMENT_SHADER)。 - 设置源码:
gl.shaderSource(shader, source)。 - 编译着色器:
gl.compileShader(shader)。 - 创建程序对象:
gl.createProgram()。 - 附加着色器:
gl.attachShader(program, shader)。 - 链接程序:
gl.linkProgram(program)。 - 使用程序:
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_FILTER、gl.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. 常见面试题速查
-
requestAnimationFrame比setTimeout优势?
→ 与屏幕刷新同步,页面不可见时暂停,性能好、省电,回调参数为高精度时间戳。 -
Canvas 中如何绘制一个圆?
→beginPath(),arc(x, y, radius, 0, 2*Math.PI),fill()或stroke()。 -
如何清除画布?
→clearRect(0, 0, canvas.width, canvas.height)。 -
fillStyle可以赋哪些值?
→ 颜色字符串、渐变对象(createLinearGradient)、图案对象(createPattern)。 -
如何实现图像灰度化?
→getImageData,遍历像素数据,计算灰度值(r*0.299 + g*0.587 + b*0.114),重新putImageData。 -
save()和restore()的作用?
→ 保存和恢复绘图状态(变换矩阵、样式、阴影、裁剪区域等),支持堆栈嵌套。 -
什么是离屏渲染?
→ 在不可见的 Canvas 上先绘制,再一次性绘制到主画布,减少重绘开销。 -
如何适配高分屏?
→ 根据devicePixelRatio放大画布尺寸(canvas.width = width * ratio),再用 CSS 缩回逻辑大小。 -
WebGL 中顶点着色器和片元着色器的职责?
→ 顶点着色器处理顶点位置;片元着色器处理像素颜色。 -
WebGL 如何绘制一个三角形?
→ 定义顶点数组,创建缓冲区,编写着色器,使用gl.drawArrays(gl.TRIANGLES, 0, 3)。

浙公网安备 33010602011771号