# 04-Canvas与SVG
## 📋 学习目标
- 掌握Canvas 2D绘图API
- 理解SVG矢量图形
- 学习Canvas与SVG的应用场景
- 实现图表和交互式图形
## 🎨 Canvas基础
### Canvas元素
```html
```
**要点**:
- width和height属性设置画布尺寸(默认300x150)
- 不要用CSS设置尺寸,会导致缩放失真
- 使用getContext('2d')获取2D渲染上下文
### 绘制基本形状
#### 矩形
```javascript
const ctx = canvas.getContext('2d');
// 填充矩形
ctx.fillStyle = '#ff6b6b';
ctx.fillRect(10, 10, 100, 100);
// 描边矩形
ctx.strokeStyle = '#339af0';
ctx.lineWidth = 2;
ctx.strokeRect(120, 10, 100, 100);
// 清除矩形
ctx.clearRect(50, 50, 20, 20);
```
#### 路径绘制
```javascript
// 三角形
ctx.beginPath();
ctx.moveTo(250, 10);
ctx.lineTo(300, 110);
ctx.lineTo(200, 110);
ctx.closePath();
ctx.fillStyle = '#51cf66';
ctx.fill();
ctx.strokeStyle = '#2f9e44';
ctx.stroke();
// 圆形
ctx.beginPath();
ctx.arc(400, 60, 50, 0, Math.PI * 2);
ctx.fillStyle = '#ffd43b';
ctx.fill();
// 圆弧
ctx.beginPath();
ctx.arc(500, 60, 50, 0, Math.PI, false);
ctx.strokeStyle = '#ff6b6b';
ctx.lineWidth = 3;
ctx.stroke();
// 贝塞尔曲线
ctx.beginPath();
ctx.moveTo(600, 10);
ctx.quadraticCurveTo(650, 10, 650, 60); // 二次贝塞尔
ctx.bezierCurveTo(650, 110, 600, 110, 600, 60); // 三次贝塞尔
ctx.stroke();
```
### 样式和颜色
#### 颜色设置
```javascript
// 填充颜色
ctx.fillStyle = '#ff6b6b';
ctx.fillStyle = 'rgb(255, 107, 107)';
ctx.fillStyle = 'rgba(255, 107, 107, 0.5)';
// 描边颜色
ctx.strokeStyle = '#339af0';
// 渐变
const gradient = ctx.createLinearGradient(0, 0, 200, 0);
gradient.addColorStop(0, '#ff6b6b');
gradient.addColorStop(1, '#339af0');
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, 200, 100);
// 径向渐变
const radialGradient = ctx.createRadialGradient(100, 100, 10, 100, 100, 50);
radialGradient.addColorStop(0, '#fff');
radialGradient.addColorStop(1, '#000');
ctx.fillStyle = radialGradient;
ctx.fillRect(50, 50, 100, 100);
// 图案
const img = new Image();
img.src = 'pattern.png';
img.onload = () => {
const pattern = ctx.createPattern(img, 'repeat');
ctx.fillStyle = pattern;
ctx.fillRect(0, 0, 200, 200);
};
```
#### 线条样式
```javascript
ctx.lineWidth = 5;
ctx.lineCap = 'round'; // butt, round, square
ctx.lineJoin = 'round'; // miter, round, bevel
ctx.setLineDash([5, 10]); // 虚线
ctx.lineDashOffset = 0;
```
### 文本绘制
```javascript
// 填充文本
ctx.font = '30px Arial';
ctx.fillStyle = '#000';
ctx.fillText('Hello Canvas', 10, 50);
// 描边文本
ctx.strokeStyle = '#ff6b6b';
ctx.lineWidth = 2;
ctx.strokeText('Hello Canvas', 10, 100);
// 文本对齐
ctx.textAlign = 'left'; // left, right, center, start, end
ctx.textBaseline = 'top'; // top, middle, bottom, alphabetic, hanging
// 测量文本
const metrics = ctx.measureText('Hello');
console.log(metrics.width); // 文本宽度
```
### 图像处理
```javascript
const img = new Image();
img.src = 'photo.jpg';
img.onload = () => {
// 绘制图像
ctx.drawImage(img, 0, 0);
// 缩放
ctx.drawImage(img, 0, 0, 200, 150);
// 裁剪和绘制
ctx.drawImage(
img,
50, 50, 100, 100, // 源图像裁剪区域
0, 0, 200, 200 // 目标画布区域
);
};
// 像素操作
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const pixels = imageData.data; // [r, g, b, a, r, g, b, a, ...]
// 灰度化
for (let i = 0; i < pixels.length; i += 4) {
const avg = (pixels[i] + pixels[i + 1] + pixels[i + 2]) / 3;
pixels[i] = avg; // R
pixels[i + 1] = avg; // G
pixels[i + 2] = avg; // B
}
ctx.putImageData(imageData, 0, 0);
```
### 变换
#### 基本变换
```javascript
// 平移
ctx.translate(100, 100);
// 旋转(弧度)
ctx.rotate(Math.PI / 4);
// 缩放
ctx.scale(2, 2);
// 重置变换
ctx.setTransform(1, 0, 0, 1, 0, 0);
// 矩阵变换
ctx.transform(1, 0, 0, 1, 0, 0);
```
#### 保存和恢复状态
```javascript
ctx.fillStyle = '#ff6b6b';
ctx.save(); // 保存当前状态
ctx.fillStyle = '#339af0';
ctx.fillRect(0, 0, 100, 100);
ctx.restore(); // 恢复之前的状态
ctx.fillRect(120, 0, 100, 100); // 使用红色
```
### 动画
#### 基本动画循环
```javascript
let x = 0;
function animate() {
// 清除画布
ctx.clearRect(0, 0, canvas.width, canvas.height);
// 绘制
ctx.fillStyle = '#ff6b6b';
ctx.fillRect(x, 100, 50, 50);
// 更新位置
x += 2;
if (x > canvas.width) x = -50;
// 下一帧
requestAnimationFrame(animate);
}
animate();
```
#### 小球弹跳动画
```javascript
const ball = {
x: 100,
y: 100,
vx: 5,
vy: 2,
radius: 20,
color: '#ff6b6b'
};
function drawBall() {
ctx.beginPath();
ctx.arc(ball.x, ball.y, ball.radius, 0, Math.PI * 2);
ctx.fillStyle = ball.color;
ctx.fill();
ctx.closePath();
}
function update() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawBall();
// 更新位置
ball.x += ball.vx;
ball.y += ball.vy;
// 边界检测
if (ball.x + ball.radius > canvas.width || ball.x - ball.radius < 0) {
ball.vx = -ball.vx;
}
if (ball.y + ball.radius > canvas.height || ball.y - ball.radius < 0) {
ball.vy = -ball.vy;
}
requestAnimationFrame(update);
}
update();
```
## 📐 SVG基础
### SVG元素
```html