Canvas 深入解析:从基础到实战

Canvas 深入解析:从基础到实战

引言

Canvas 是 HTML5 引入的一个强大的 2D 图形绘制 API,它为 Web 开发提供了像素级的图形控制能力。通过 Canvas,我们可以在浏览器中实现复杂的数据可视化、游戏开发、图像处理以及动画效果。


一、Canvas 基础概念

1.1 什么是 Canvas

Canvas 是一个 HTML 元素,提供了一个通过 JavaScript 脚本来绘制图形的画布区域。它本质上是一个位图容器,可以用来渲染图形、图表、动画以及图像合成等。

Canvas 的渲染上下文(Context)提供了实际的绘图方法和属性。目前主要有两种上下文:

  • 2D Context:用于 2D 图形绘制
  • WebGL Context:用于 3D 图形渲染

1.2 Canvas 与 SVG 的区别

graph TB A[图形绘制技术] --> B[Canvas] A --> C[SVG] B --> D[位图/像素级操作] B --> E[JavaScript 驱动] B --> F[适合动画密集场景] C --> G[矢量图/DOM元素] C --> H[声明式] C --> I[适合交互式图形]

核心差异:

  • Canvas 基于像素,绘制后无法直接修改单个图形对象
  • SVG 基于矢量,每个图形都是 DOM 节点,支持事件绑定
  • Canvas 适合高频动画和大量图形渲染
  • SVG 适合需要交互和可缩放的场景

二、Canvas 基本使用

2.1 创建 Canvas 元素

首先需要在 HTML 中定义 Canvas 元素,并通过 JavaScript 获取绘图上下文。

javascript 复制代码
// HTML: <canvas id="myCanvas" width="800" height="600"></canvas>

const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');

// 设置 Canvas 分辨率适配高清屏幕
const dpr = window.devicePixelRatio || 1;
canvas.width = 800 * dpr;
canvas.height = 600 * dpr;
canvas.style.width = '800px';
canvas.style.height = '600px';
ctx.scale(dpr, dpr);

说明: 上述代码展示了如何正确处理高分辨率屏幕(如 Retina 屏)。通过 devicePixelRatio 调整实际绘制分辨率,确保图形清晰度。

2.2 Canvas 坐标系统

Canvas 使用笛卡尔坐标系,原点 (0, 0) 位于左上角,x 轴向右延伸,y 轴向下延伸。

graph LR A["(0,0) 原点"] --> B["x 轴 →"] A --> C["y 轴 ↓"] B --> D["(width, 0)"] C --> E["(0, height)"]

三、核心绘图 API

3.1 绘制基本图形

矩形绘制

Canvas 提供了三种直接绘制矩形的方法,无需路径操作。

javascript 复制代码
// 填充矩形
ctx.fillStyle = '#4CAF50';
ctx.fillRect(50, 50, 200, 100);

// 描边矩形
ctx.strokeStyle = '#2196F3';
ctx.lineWidth = 3;
ctx.strokeRect(300, 50, 200, 100);

// 清除矩形区域
ctx.clearRect(60, 60, 50, 50);

说明: fillRectstrokeRect 是立即渲染方法,clearRect 用于擦除指定区域的像素。

路径绘制

路径是 Canvas 绘制复杂图形的基础,通过一系列绘图指令构建形状。

javascript 复制代码
// 绘制三角形
ctx.beginPath();
ctx.moveTo(100, 200);
ctx.lineTo(200, 200);
ctx.lineTo(150, 100);
ctx.closePath();
ctx.fillStyle = '#FF5722';
ctx.fill();
ctx.strokeStyle = '#000';
ctx.stroke();

绘制流程:

flowchart LR A[beginPath] --> B[moveTo/lineTo] B --> C[closePath] C --> D{填充或描边} D -->|fill| E[填充路径] D -->|stroke| F[描边路径]

3.2 圆形与弧线

圆形绘制使用 arc() 方法,需要指定圆心、半径、起始角度和结束角度。

javascript 复制代码
// 绘制完整圆形
ctx.beginPath();
ctx.arc(400, 300, 80, 0, Math.PI * 2);
ctx.fillStyle = 'rgba(33, 150, 243, 0.5)';
ctx.fill();

// 绘制扇形
ctx.beginPath();
ctx.moveTo(600, 300);
ctx.arc(600, 300, 80, 0, Math.PI * 0.75);
ctx.closePath();
ctx.fillStyle = '#FFC107';
ctx.fill();

参数说明:

  • arc(x, y, radius, startAngle, endAngle, anticlockwise)
  • 角度使用弧度制:360° = 2π

3.3 贝塞尔曲线

贝塞尔曲线用于绘制平滑曲线,常用于图形设计和动画路径。

javascript 复制代码
// 二次贝塞尔曲线
ctx.beginPath();
ctx.moveTo(50, 400);
ctx.quadraticCurveTo(200, 300, 350, 400);
ctx.strokeStyle = '#9C27B0';
ctx.lineWidth = 2;
ctx.stroke();

// 三次贝塞尔曲线
ctx.beginPath();
ctx.moveTo(400, 400);
ctx.bezierCurveTo(500, 300, 600, 500, 700, 400);
ctx.strokeStyle = '#E91E63';
ctx.stroke();

四、样式与颜色

4.1 颜色与透明度

Canvas 支持多种颜色格式,包括命名颜色、十六进制、RGB 和 RGBA。

javascript 复制代码
// 多种颜色设置方式
ctx.fillStyle = 'red';                          // 命名颜色
ctx.fillStyle = '#FF5722';                      // 十六进制
ctx.fillStyle = 'rgb(255, 87, 34)';            // RGB
ctx.fillStyle = 'rgba(255, 87, 34, 0.6)';      // RGBA

// 全局透明度
ctx.globalAlpha = 0.5;
ctx.fillRect(100, 100, 200, 150);
ctx.globalAlpha = 1.0; // 恢复默认

4.2 渐变效果

Canvas 提供线性渐变和径向渐变两种渐变类型。

javascript 复制代码
// 线性渐变
const linearGradient = ctx.createLinearGradient(0, 0, 400, 0);
linearGradient.addColorStop(0, '#FF6B6B');
linearGradient.addColorStop(0.5, '#4ECDC4');
linearGradient.addColorStop(1, '#45B7D1');
ctx.fillStyle = linearGradient;
ctx.fillRect(50, 50, 400, 100);

// 径向渐变
const radialGradient = ctx.createRadialGradient(300, 300, 20, 300, 300, 100);
radialGradient.addColorStop(0, '#FFF');
radialGradient.addColorStop(1, '#FF6B6B');
ctx.fillStyle = radialGradient;
ctx.beginPath();
ctx.arc(300, 300, 100, 0, Math.PI * 2);
ctx.fill();

4.3 阴影效果

通过设置阴影属性,可以为图形添加立体感。

javascript 复制代码
ctx.shadowColor = 'rgba(0, 0, 0, 0.5)';
ctx.shadowBlur = 15;
ctx.shadowOffsetX = 5;
ctx.shadowOffsetY = 5;

ctx.fillStyle = '#2196F3';
ctx.fillRect(100, 200, 200, 150);

// 清除阴影设置
ctx.shadowColor = 'transparent';

五、文本绘制

5.1 基础文本 API

Canvas 提供了强大的文本渲染能力,支持字体、对齐、基线等多种属性设置。

javascript 复制代码
// 设置字体样式
ctx.font = 'bold 48px Arial, sans-serif';
ctx.fillStyle = '#333';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';

// 填充文本
ctx.fillText('Hello Canvas', 400, 300);

// 描边文本
ctx.strokeStyle = '#2196F3';
ctx.lineWidth = 2;
ctx.strokeText('Hello Canvas', 400, 400);

// 测量文本宽度
const metrics = ctx.measureText('Hello Canvas');
console.log('文本宽度:', metrics.width);

文本对齐属性:

  • textAlign: left | right | center | start | end
  • textBaseline: top | middle | bottom | alphabetic | hanging

5.2 文本换行与截断

Canvas 不支持自动换行,需要手动实现。

javascript 复制代码
function wrapText(ctx, text, x, y, maxWidth, lineHeight) {
  const words = text.split(' ');
  let line = '';
  let offsetY = 0;

  for (let word of words) {
    const testLine = line + word + ' ';
    const metrics = ctx.measureText(testLine);

    if (metrics.width > maxWidth && line !== '') {
      ctx.fillText(line, x, y + offsetY);
      line = word + ' ';
      offsetY += lineHeight;
    } else {
      line = testLine;
    }
  }
  ctx.fillText(line, x, y + offsetY);
}

ctx.font = '16px Arial';
wrapText(ctx, '这是一段很长的文本,需要自动换行显示', 50, 100, 300, 24);

六、图像处理

6.1 绘制图像

Canvas 可以绘制图像文件、其他 Canvas 元素或视频帧。

javascript 复制代码
const image = new Image();
image.src = 'photo.jpg';

image.onload = () => {
  // 基础绘制
  ctx.drawImage(image, 0, 0);

  // 缩放绘制
  ctx.drawImage(image, 0, 0, 400, 300);

  // 裁剪绘制
  ctx.drawImage(
    image,
    100, 100, 200, 200,  // 源图像裁剪区域
    50, 50, 300, 300     // 目标画布位置和尺寸
  );
};

6.2 像素操作

通过 getImageDataputImageData 可以直接操作像素数据。

javascript 复制代码
// 获取像素数据
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const data = imageData.data; // Uint8ClampedArray [r, g, b, a, r, g, b, a, ...]

// 灰度滤镜
for (let i = 0; i < data.length; i += 4) {
  const avg = (data[i] + data[i + 1] + data[i + 2]) / 3;
  data[i] = avg;     // R
  data[i + 1] = avg; // G
  data[i + 2] = avg; // B
  // data[i + 3] 是 alpha 通道,保持不变
}

// 应用修改后的像素数据
ctx.putImageData(imageData, 0, 0);

像素数据结构:

graph LR A[ImageData] --> B[data: Uint8ClampedArray] B --> C[像素1: R G B A] B --> D[像素2: R G B A] B --> E[像素3: R G B A] C --> F[范围: 0-255]

七、变换与变形

7.1 基础变换

Canvas 提供了平移、旋转、缩放等几何变换方法。

javascript 复制代码
ctx.save(); // 保存当前状态

// 平移
ctx.translate(200, 200);

// 旋转(弧度制)
ctx.rotate(Math.PI / 4);

// 缩放
ctx.scale(1.5, 1.5);

// 绘制图形
ctx.fillStyle = '#FF5722';
ctx.fillRect(-50, -50, 100, 100);

ctx.restore(); // 恢复之前保存的状态

变换执行流程:

flowchart TD A[save保存状态] --> B[translate平移] B --> C[rotate旋转] C --> D[scale缩放] D --> E[绘制图形] E --> F[restore恢复状态]

7.2 变换矩阵

高级变换可以通过变换矩阵实现。

javascript 复制代码
// transform(a, b, c, d, e, f)
// a: 水平缩放, b: 水平倾斜, c: 垂直倾斜
// d: 垂直缩放, e: 水平移动, f: 垂直移动

ctx.transform(1, 0.5, -0.5, 1, 0, 0);
ctx.fillRect(100, 100, 100, 100);

// 重置变换矩阵
ctx.setTransform(1, 0, 0, 1, 0, 0);

八、动画实现

8.1 动画循环

Canvas 动画的核心是持续更新和重绘,通常使用 requestAnimationFrame 实现。

javascript 复制代码
let x = 0;
let velocity = 2;

function animate() {
  // 清除画布
  ctx.clearRect(0, 0, canvas.width, canvas.height);

  // 更新位置
  x += velocity;
  if (x > canvas.width || x < 0) {
    velocity *= -1;
  }

  // 绘制对象
  ctx.fillStyle = '#2196F3';
  ctx.beginPath();
  ctx.arc(x, 300, 30, 0, Math.PI * 2);
  ctx.fill();

  // 请求下一帧
  requestAnimationFrame(animate);
}

animate();

动画循环流程:

flowchart LR A[清除画布] --> B[更新状态] B --> C[绘制图形] C --> D[requestAnimationFrame] D --> A

8.2 粒子系统

粒子系统是实现复杂视觉效果的常用技术。

javascript 复制代码
class Particle {
  constructor(x, y) {
    this.x = x;
    this.y = y;
    this.vx = (Math.random() - 0.5) * 4;
    this.vy = (Math.random() - 0.5) * 4;
    this.life = 1;
  }

  update() {
    this.x += this.vx;
    this.y += this.vy;
    this.life -= 0.01;
  }

  draw(ctx) {
    ctx.fillStyle = `rgba(255, 100, 100, ${this.life})`;
    ctx.beginPath();
    ctx.arc(this.x, this.y, 5, 0, Math.PI * 2);
    ctx.fill();
  }
}

const particles = [];

function animateParticles() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);

  // 添加新粒子
  if (Math.random() < 0.1) {
    particles.push(new Particle(400, 300));
  }

  // 更新并绘制粒子
  for (let i = particles.length - 1; i >= 0; i--) {
    particles[i].update();
    particles[i].draw(ctx);

    if (particles[i].life <= 0) {
      particles.splice(i, 1);
    }
  }

  requestAnimationFrame(animateParticles);
}

animateParticles();

九、性能优化

9.1 离屏 Canvas

对于复杂图形,使用离屏 Canvas 预渲染可以显著提升性能。

javascript 复制代码
// 创建离屏 Canvas
const offscreenCanvas = document.createElement('canvas');
offscreenCanvas.width = 200;
offscreenCanvas.height = 200;
const offscreenCtx = offscreenCanvas.getContext('2d');

// 在离屏 Canvas 上绘制复杂图形
offscreenCtx.fillStyle = '#FF5722';
offscreenCtx.beginPath();
offscreenCtx.arc(100, 100, 80, 0, Math.PI * 2);
offscreenCtx.fill();

// 在主 Canvas 上多次使用预渲染结果
function render() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);

  for (let i = 0; i < 10; i++) {
    ctx.drawImage(offscreenCanvas, i * 150, 100);
  }

  requestAnimationFrame(render);
}

render();

9.2 分层渲染

将静态内容和动态内容分离到不同的 Canvas 层。

javascript 复制代码
// 静态背景层
const bgCanvas = document.getElementById('bgCanvas');
const bgCtx = bgCanvas.getContext('2d');

// 动态内容层
const fgCanvas = document.getElementById('fgCanvas');
const fgCtx = fgCanvas.getContext('2d');

// 只绘制一次背景
bgCtx.fillStyle = '#f0f0f0';
bgCtx.fillRect(0, 0, bgCanvas.width, bgCanvas.height);

// 动态内容持续更新
function animate() {
  fgCtx.clearRect(0, 0, fgCanvas.width, fgCanvas.height);
  // 绘制动态内容...
  requestAnimationFrame(animate);
}

分层架构:

graph TB A[Canvas 分层] --> B[背景层 - 静态] A --> C[内容层 - 动态] A --> D[UI层 - 交互] B --> E[渲染一次] C --> F[持续更新] D --> G[按需更新]

9.3 性能优化技巧

javascript 复制代码
// 1. 批量绘制路径
ctx.beginPath();
for (let i = 0; i < 1000; i++) {
  ctx.rect(Math.random() * 800, Math.random() * 600, 10, 10);
}
ctx.fill(); // 一次性填充所有矩形

// 2. 避免不必要的状态改变
const style = '#FF5722';
ctx.fillStyle = style;
for (let i = 0; i < 100; i++) {
  // 不要在循环内重复设置相同的样式
  ctx.fillRect(i * 10, 100, 8, 8);
}

// 3. 使用整数坐标
ctx.fillRect(Math.floor(x), Math.floor(y), width, height);

// 4. 限制重绘区域
ctx.clearRect(x, y, width, height); // 只清除必要区域

十、实战案例

10.1 数据可视化:动态图表

实现一个实时更新的折线图。

javascript 复制代码
class LineChart {
  constructor(canvas, maxPoints = 50) {
    this.canvas = canvas;
    this.ctx = canvas.getContext('2d');
    this.data = [];
    this.maxPoints = maxPoints;
  }

  addData(value) {
    this.data.push(value);
    if (this.data.length > this.maxPoints) {
      this.data.shift();
    }
  }

  render() {
    const { ctx, canvas, data } = this;
    const padding = 40;
    const width = canvas.width - padding * 2;
    const height = canvas.height - padding * 2;

    ctx.clearRect(0, 0, canvas.width, canvas.height);

    // 绘制坐标轴
    ctx.strokeStyle = '#ccc';
    ctx.beginPath();
    ctx.moveTo(padding, padding);
    ctx.lineTo(padding, canvas.height - padding);
    ctx.lineTo(canvas.width - padding, canvas.height - padding);
    ctx.stroke();

    if (data.length < 2) return;

    // 绘制折线
    const max = Math.max(...data);
    const min = Math.min(...data);
    const range = max - min || 1;
    const step = width / (this.maxPoints - 1);

    ctx.strokeStyle = '#2196F3';
    ctx.lineWidth = 2;
    ctx.beginPath();

    data.forEach((value, index) => {
      const x = padding + index * step;
      const y = canvas.height - padding - ((value - min) / range) * height;

      if (index === 0) {
        ctx.moveTo(x, y);
      } else {
        ctx.lineTo(x, y);
      }
    });

    ctx.stroke();
  }
}

const chart = new LineChart(canvas);

function updateChart() {
  chart.addData(Math.random() * 100);
  chart.render();
  setTimeout(updateChart, 200);
}

updateChart();

10.2 游戏开发:碰撞检测

实现简单的矩形碰撞检测系统。

javascript 复制代码
class GameObject {
  constructor(x, y, width, height, color) {
    this.x = x;
    this.y = y;
    this.width = width;
    this.height = height;
    this.color = color;
    this.vx = (Math.random() - 0.5) * 4;
    this.vy = (Math.random() - 0.5) * 4;
  }

  update(canvas) {
    this.x += this.vx;
    this.y += this.vy;

    // 边界反弹
    if (this.x <= 0 || this.x + this.width >= canvas.width) {
      this.vx *= -1;
    }
    if (this.y <= 0 || this.y + this.height >= canvas.height) {
      this.vy *= -1;
    }
  }

  draw(ctx) {
    ctx.fillStyle = this.color;
    ctx.fillRect(this.x, this.y, this.width, this.height);
  }

  collidesWith(other) {
    return this.x < other.x + other.width &&
           this.x + this.width > other.x &&
           this.y < other.y + other.height &&
           this.y + this.height > other.y;
  }
}

const objects = [
  new GameObject(100, 100, 50, 50, '#FF5722'),
  new GameObject(300, 200, 50, 50, '#2196F3'),
  new GameObject(500, 300, 50, 50, '#4CAF50')
];

function gameLoop() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);

  objects.forEach(obj => {
    obj.update(canvas);
    obj.draw(ctx);

    // 检测碰撞
    objects.forEach(other => {
      if (obj !== other && obj.collidesWith(other)) {
        obj.color = '#FFC107';
        other.color = '#FFC107';
      }
    });
  });

  requestAnimationFrame(gameLoop);
}

gameLoop();

碰撞检测流程:

flowchart TD A[遍历所有对象] --> B[更新位置] B --> C[边界检测] C --> D[与其他对象比较] D --> E{AABB碰撞检测} E -->|碰撞| F[触发碰撞响应] E -->|未碰撞| G[继续检测] F --> H[绘制对象] G --> H H --> I[下一帧]

10.3 图像编辑:实时滤镜

实现多种图像滤镜效果。

javascript 复制代码
class ImageFilter {
  static grayscale(imageData) {
    const data = imageData.data;
    for (let i = 0; i < data.length; i += 4) {
      const avg = (data[i] + data[i + 1] + data[i + 2]) / 3;
      data[i] = data[i + 1] = data[i + 2] = avg;
    }
    return imageData;
  }

  static sepia(imageData) {
    const data = imageData.data;
    for (let i = 0; i < data.length; i += 4) {
      const r = data[i];
      const g = data[i + 1];
      const b = data[i + 2];

      data[i] = Math.min(255, r * 0.393 + g * 0.769 + b * 0.189);
      data[i + 1] = Math.min(255, r * 0.349 + g * 0.686 + b * 0.168);
      data[i + 2] = Math.min(255, r * 0.272 + g * 0.534 + b * 0.131);
    }
    return imageData;
  }

  static brightness(imageData, value) {
    const data = imageData.data;
    for (let i = 0; i < data.length; i += 4) {
      data[i] += value;
      data[i + 1] += value;
      data[i + 2] += value;
    }
    return imageData;
  }

  static contrast(imageData, value) {
    const data = imageData.data;
    const factor = (259 * (value + 255)) / (255 * (259 - value));

    for (let i = 0; i < data.length; i += 4) {
      data[i] = factor * (data[i] - 128) + 128;
      data[i + 1] = factor * (data[i + 1] - 128) + 128;
      data[i + 2] = factor * (data[i + 2] - 128) + 128;
    }
    return imageData;
  }
}

// 使用示例
const img = new Image();
img.src = 'photo.jpg';
img.onload = () => {
  ctx.drawImage(img, 0, 0);
  const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);

  ImageFilter.sepia(imageData);
  ImageFilter.brightness(imageData, 20);

  ctx.putImageData(imageData, 0, 0);
};

十一、Canvas 最佳实践

11.1 内存管理

javascript 复制代码
// 及时清理不再使用的资源
function cleanup() {
  // 清除事件监听器
  canvas.removeEventListener('mousemove', handleMouseMove);

  // 清空大型数组
  particles.length = 0;

  // 取消动画帧
  cancelAnimationFrame(animationId);
}

// 避免内存泄漏
let imageCache = new Map();

function loadImage(url) {
  if (imageCache.has(url)) {
    return Promise.resolve(imageCache.get(url));
  }

  return new Promise((resolve) => {
    const img = new Image();
    img.onload = () => {
      imageCache.set(url, img);
      resolve(img);
    };
    img.src = url;
  });
}

11.2 跨浏览器兼容性

javascript 复制代码
// 兼容性检查
if (!canvas.getContext) {
  console.error('Canvas not supported');
  return;
}

// 特性检测
const ctx = canvas.getContext('2d');
if (typeof ctx.ellipse !== 'function') {
  // 使用降级方案
  ctx.ellipse = function(x, y, radiusX, radiusY, rotation, startAngle, endAngle) {
    this.save();
    this.translate(x, y);
    this.rotate(rotation);
    this.scale(radiusX, radiusY);
    this.arc(0, 0, 1, startAngle, endAngle);
    this.restore();
  };
}

11.3 调试技巧

javascript 复制代码
// 性能监控
class PerformanceMonitor {
  constructor() {
    this.fps = 0;
    this.lastTime = performance.now();
    this.frames = 0;
  }

  update() {
    this.frames++;
    const currentTime = performance.now();

    if (currentTime >= this.lastTime + 1000) {
      this.fps = Math.round((this.frames * 1000) / (currentTime - this.lastTime));
      this.frames = 0;
      this.lastTime = currentTime;
    }
  }

  draw(ctx) {
    ctx.fillStyle = '#000';
    ctx.font = '16px monospace';
    ctx.fillText(`FPS: ${this.fps}`, 10, 30);
  }
}

const monitor = new PerformanceMonitor();

function debugRender() {
  monitor.update();
  monitor.draw(ctx);
  requestAnimationFrame(debugRender);
}

十二、Canvas 生态与工具链

12.1 常用库与框架

2D 渲染引擎:

  • Fabric.js:强大的 Canvas 对象模型和交互库
  • Konva.js:高性能的 2D Canvas 框架,支持事件系统
  • Paper.js:矢量图形脚本框架,基于 Canvas
  • PixiJS:WebGL 渲染引擎,可降级到 Canvas

图表库:

  • Chart.js:简洁的响应式图表库
  • ECharts:百度开源的企业级可视化库
  • D3.js:数据驱动的文档操作库(SVG + Canvas)

12.2 开发工具

javascript 复制代码
// Canvas 调试工具:Spector.js
// 可以录制和回放 Canvas 操作序列

// Chrome DevTools Canvas Inspector
// 在 Chrome 开发者工具中启用 Canvas 调试
// More tools > Rendering > Canvas

// 性能分析
console.time('render');
// 渲染代码
console.timeEnd('render');

// 使用 Performance API
const perfEntry = performance.measure('canvas-render', 'start', 'end');
console.log('渲染耗时:', perfEntry.duration, 'ms');

十三、Canvas 高级应用

13.1 WebGL 集成

Canvas 不仅支持 2D 绘图,还可以通过 WebGL 实现 3D 渲染。

javascript 复制代码
const gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');

if (!gl) {
  console.error('WebGL not supported');
}

// 设置视口
gl.viewport(0, 0, canvas.width, canvas.height);

// 清除颜色缓冲区
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);

Canvas 渲染上下文选择:

graph TD A[Canvas Context] --> B[2D Context] A --> C[WebGL Context] A --> D[WebGL2 Context] A --> E[OffscreenCanvas] B --> F[2D 图形/图表/游戏] C --> G[3D 渲染/复杂特效] D --> H[高级 3D 功能] E --> I[Web Worker 中渲染]

13.2 OffscreenCanvas

OffscreenCanvas 允许在 Web Worker 中进行渲染,避免阻塞主线程。

javascript 复制代码
// 主线程
const offscreen = canvas.transferControlToOffscreen();
const worker = new Worker('render-worker.js');
worker.postMessage({ canvas: offscreen }, [offscreen]);

// render-worker.js
self.onmessage = (e) => {
  const canvas = e.data.canvas;
  const ctx = canvas.getContext('2d');

  function render() {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    // 执行渲染操作
    ctx.fillStyle = '#2196F3';
    ctx.fillRect(50, 50, 200, 150);

    requestAnimationFrame(render);
  }

  render();
};

13.3 视频处理

Canvas 可以实时处理视频流,实现特效和滤镜。

javascript 复制代码
const video = document.getElementById('video');
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');

navigator.mediaDevices.getUserMedia({ video: true })
  .then(stream => {
    video.srcObject = stream;
    video.play();
  });

function processVideo() {
  if (video.paused || video.ended) return;

  // 绘制当前帧
  ctx.drawImage(video, 0, 0, canvas.width, canvas.height);

  // 应用实时滤镜
  const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
  ImageFilter.grayscale(imageData);
  ctx.putImageData(imageData, 0, 0);

  requestAnimationFrame(processVideo);
}

video.addEventListener('play', processVideo);

十四、总结与展望

14.1 核心要点回顾

Canvas 作为 Web 图形技术的重要组成部分,具有以下核心优势:

  1. 高性能渲染:直接操作像素,适合大量图形和动画
  2. 灵活性强:完全由 JavaScript 控制,可实现任意效果
  3. 生态丰富:众多成熟的库和框架支持
  4. 应用广泛:从数据可视化到游戏开发,覆盖多个领域

学习路径:

graph LR A[Canvas 基础] --> B[绘图 API] B --> C[动画与交互] C --> D[性能优化] D --> E[实战项目] E --> F[高级应用] F --> G[WebGL/3D]

14.2 技术发展趋势

2025 年 Canvas 发展方向:

  1. OffscreenCanvas 普及:主流浏览器全面支持,多线程渲染成为标准
  2. WebGPU 崛起:下一代图形 API,性能超越 WebGL
  3. AI 集成:机器学习模型在 Canvas 中的实时推理应用
  4. AR/VR 支持:Canvas 与 WebXR API 的深度整合
  5. 性能优化:浏览器引擎对 Canvas 的原生优化持续增强

14.3 学习资源推荐

  • MDN Web Docs:最权威的 Canvas API 文档
  • HTML5 Canvas Tutorials:系统化的教程网站
  • CodePen:丰富的 Canvas 示例和交互式代码
  • GitHub:优秀的开源 Canvas 项目

参考资料


相关推荐
oden2 小时前
1 小时速通!手把手教你从零搭建 Astro 博客并上线
前端
若梦plus2 小时前
JS之类型化数组
前端·javascript
若梦plus2 小时前
Canvas渲染原理与浏览器图形管线
前端·javascript
C_心欲无痕2 小时前
vue3 - 依赖注入(provide/inject)组件跨层级通信的优雅方案
前端·javascript·vue.js
幺零九零零2 小时前
全栈程序员-前端第二节- vite是什么?
前端
你脸上有BUG3 小时前
TreeSelect 组件 showCheckedStrategy 属性不生效问题
前端·vue
小北方城市网3 小时前
第 6 课:Vue 3 工程化与项目部署实战 —— 从本地开发到线上发布
大数据·运维·前端·ai
BD_Marathon3 小时前
Vue3_响应式数据的处理方式
前端·javascript·vue.js