Canvas 2D API(Canvas 2D API)
一、Canvas 基础
1.1 创建 Canvas
<canvas id="myCanvas" width="800" height="600">
您的浏览器不支持 Canvas
</canvas>
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
// 获取 2D 上下文
console.log('Context:', ctx);
1.2 Canvas 坐标系统
Canvas 使用以左上角为原点的笛卡尔坐标系:
(0,0) ──────────→ X
│
│ (x, y)
│
↓
Y
1.3 基本属性
// 填充和描边颜色
ctx.fillStyle = '#FF0000'; // 红色
ctx.strokeStyle = '#0000FF'; // 蓝色
// 线宽
ctx.lineWidth = 2;
// 透明度
ctx.globalAlpha = 0.5;
二、绘制形状
2.1 矩形
// 填充矩形
ctx.fillStyle = 'red';
ctx.fillRect(10, 10, 100, 50);
// 描边矩形
ctx.strokeStyle = 'blue';
ctx.strokeRect(10, 10, 100, 50);
// 清除矩形(透明)
ctx.clearRect(0, 0, canvas.width, canvas.height);
2.2 路径绘制
// 开始路径
ctx.beginPath();
// 移动到起点
ctx.moveTo(0, 0);
// 画线
ctx.lineTo(100, 100);
ctx.lineTo(200, 50);
// 弧线
ctx.arc(100, 100, 50, 0, Math.PI * 2); // 整圆
// 贝塞尔曲线
ctx.quadraticCurveTo(cp1x, cp1y, x, y); // 二次
ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y); // 三次
// 闭合路径
ctx.closePath();
// 填充路径
ctx.fill();
// 描边路径
ctx.stroke();
2.3 圆形和椭圆
// 圆形
ctx.beginPath();
ctx.arc(x, y, radius, startAngle, endAngle, counterclockwise);
ctx.fill();
// 椭圆
ctx.beginPath();
ctx.ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle);
ctx.fill();
三、文本绘制
3.1 文本基础
ctx.font = '24px Arial';
ctx.fillText('Hello Canvas', 50, 50);
ctx.strokeText('Hello Canvas', 50, 100);
3.2 文本样式
// 字体
ctx.font = 'bold 24px/1.5 Arial, sans-serif';
// 文本对齐
ctx.textAlign = 'left'; // left, center, right, start, end
ctx.textBaseline = 'top'; // top, hanging, middle, alphabetic, ideographic, bottom
// 文本度量
const metrics = ctx.measureText('Hello');
console.log('Width:', metrics.width);
3.3 文本渐变
const gradient = ctx.createLinearGradient(0, 0, 200, 0);
gradient.addColorStop(0, 'red');
gradient.addColorStop(1, 'blue');
ctx.font = '48px Arial';
ctx.fillStyle = gradient;
ctx.fillText('Gradient Text', 50, 100);
四、图像处理
4.1 绘制图像
const img = new Image();
img.onload = () => {
// 绘制图像
ctx.drawImage(img, 0, 0);
// 缩放绘制
ctx.drawImage(img, 0, 0, 200, 150);
// 部分绘制
ctx.drawImage(img, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);
};
img.src = 'image.jpg';
4.2 图像合成
// 全局合成操作
ctx.globalCompositeOperation = 'source-over'; // 默认
// 其他模式:
// source-atop, source-in, source-out
// destination-atop, destination-in, destination-out
// lighter, copy, xor
// multiply, screen, overlay, darken, lighten, color-dodge, color-burn
4.3 裁剪
ctx.save();
// 创建裁剪路径
ctx.beginPath();
ctx.arc(100, 100, 50, 0, Math.PI * 2);
ctx.clip();
// 在裁剪区域内绘制
ctx.drawImage(img, 0, 0);
ctx.restore(); // 恢复
五、样式和渐变
5.1 线性渐变
const gradient = ctx.createLinearGradient(x0, y0, x1, y1);
gradient.addColorStop(0, 'red');
gradient.addColorStop(0.5, 'green');
gradient.addColorStop(1, 'blue');
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, 300, 100);
5.2 径向渐变
const gradient = ctx.createRadialGradient(
x0, y0, r0, // 内圆
x1, y1, r1 // 外圆
);
gradient.addColorStop(0, 'yellow');
gradient.addColorStop(1, 'orange');
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, 300, 300);
5.3 阴影
ctx.shadowColor = 'rgba(0, 0, 0, 0.5)';
ctx.shadowBlur = 10;
ctx.shadowOffsetX = 5;
ctx.shadowOffsetY = 5;
ctx.fillStyle = 'red';
ctx.fillRect(10, 10, 100, 100);
六、变换
6.1 基本变换
ctx.save(); // 保存状态
// 平移
ctx.translate(50, 50);
// 旋转(弧度)
ctx.rotate(Math.PI / 4);
// 缩放
ctx.scale(2, 2);
// 绘制
ctx.fillRect(0, 0, 100, 100);
ctx.restore(); // 恢复状态
6.2 矩阵变换
ctx.save();
ctx.transform(a, b, c, d, e, f);
// a: 水平缩放
// b: 水平倾斜
// c: 垂直倾斜
// d: 垂直缩放
// e: 水平移动
// f: 垂直移动
ctx.restore();
6.3 变换顺序
ctx.translate(100, 100);
ctx.rotate(Math.PI / 4);
ctx.translate(50, 50);
// 结果:先平移到 (100,100),旋转 45 度,再平移 (50,50)
// 最终变换 = T * R * T(从右到左应用)
七、像素操作
7.1 ImageData
// 获取像素数据
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const data = imageData.data; // Uint8ClampedArray
// 遍历像素
for (let i = 0; i < data.length; i += 4) {
const r = data[i]; // 红色
const g = data[i + 1]; // 绿色
const b = data[i + 2]; // 蓝色
const a = data[i + 3]; // 透明度
}
// 修改像素
for (let i = 0; i < data.length; i += 4) {
data[i] = 255 - data[i]; // 反色红色
data[i + 1] = 255 - data[i + 1]; // 反色绿色
data[i + 2] = 255 - data[i + 2]; // 反色蓝色
}
// 放回 canvas
ctx.putImageData(imageData, 0, 0);
7.2 创建 ImageData
// 创建一个新的 ImageData
const newImageData = ctx.createImageData(100, 100);
// 或者从已有的 ImageData 复制
const copiedImageData = ctx.createImageData(existingImageData);
八、动画
8.1 动画循环
function animate() {
// 清除画布
ctx.clearRect(0, 0, canvas.width, canvas.height);
// 更新状态
x += speed;
// 绘制
ctx.fillRect(x, y, width, height);
// 请求下一帧
requestAnimationFrame(animate);
}
requestAnimationFrame(animate);
8.2 动画示例:弹跳球
class Ball {
constructor() {
this.x = canvas.width / 2;
this.y = canvas.height / 2;
this.vx = 3;
this.vy = 2;
this.radius = 20;
}
update() {
this.x += this.vx;
this.y += this.vy;
// 碰撞检测
if (this.x - this.radius < 0 || this.x + this.radius > canvas.width) {
this.vx = -this.vx;
}
if (this.y - this.radius < 0 || this.y + this.radius > canvas.height) {
this.vy = -this.vy;
}
}
draw() {
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
ctx.fillStyle = 'red';
ctx.fill();
}
}
const ball = new Ball();
function animate() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ball.update();
ball.draw();
requestAnimationFrame(animate);
}
animate();
九、性能优化
9.1 分层 Canvas
<canvas id="bg" style="position: absolute;"></canvas>
<canvas id="fg" style="position: absolute;"></canvas>
const bgCanvas = document.getElementById('bg');
const fgCanvas = document.getElementById('fg');
const bgCtx = bgCanvas.getContext('2d');
const fgCtx = fgCanvas.getContext('2d');
// 静态背景只绘制一次
function drawBackground() {
// ... 绘制背景
}
// 频繁更新的前景
function drawForeground() {
// ... 绘制前景
}
9.2 离屏 Canvas
const offscreen = document.createElement('canvas');
offscreen.width = canvas.width;
offscreen.height = canvas.height;
const offCtx = offscreen.getContext('2d');
// 在离屏 canvas 绘制复杂图形
function prepareComplexShape() {
offCtx.clearRect(0, 0, offscreen.width, offscreen.height);
// ... 绘制复杂图形
}
// 在主 canvas 复制
ctx.drawImage(offscreen, 0, 0);