# 图形与动画基础：Canvas 与他的朋友们

::: tip 🎯 核心问题

以前的网页只能展示干巴巴的文字和图片。但如果你想做打砖块游戏、华丽的动态特效、或是可以自由拖拽的数据报表，仅仅靠 `<div>` 是远远不够的。这就是 **Canvas（画布）** 诞生的原因。

本指南将带你从画下第一条线开始，一路打怪升级，最终亲手写出能在浏览器中流畅运行 60 帧的粒子引擎。

:::

---

## 1. Canvas定义

如果说早期的网页是用**乐高积木**（HTML 标签）拼凑起来的静态模型，那么 HTML5 的 `<canvas>` 标签就是扔给你一张**巨大的数字白纸**，然后递给你一支靠代码控制的**画笔**，剩下的全交给你自由发挥。

这里面的画没有任何标签结构。你用画笔涂上去的心血，一旦落笔就变成了最纯粹的**“像素颜料”**。

### 1.1 Canvas vs SVG：两种不同流派的艺术家

在前端画图界，Canvas 有个宿敌叫 **SVG**。它们代表了两种截然不同的绘画观念：

- **Canvas（位图画板）：**
  - **原理**：就像真实在纸上涂色，几笔画上去就变成一团颜料（像素点）。
  - **优势**：电脑只管往屏幕上“洒颜料”，性能起飞！能同时画出大几千个活蹦乱跳的闪烁粒子。
  - **缺点**：画完就没法单独反悔（没法通过 DOM 节点选择），且放大会造成马赛克发虚。
- **SVG（矢量图拼接）：**
  - **原理**：就像做 PPT。你画一个圆，它就生成一个独立标签的“圆实体”放在画面上。
  - **优势**：不管放大 100 倍还是 10 万倍，永远极其清晰。每个形状都是独立的 DOM 节点，你可以随时用 CSS 和 JS 改变它的颜色或绑定点击事件。
  - **缺点**：如果你试图放几万个对象乱飞，繁重的 DOM 树和排版引擎会直接把浏览器卡死。

**🎮 简单总结：玩动态游戏、做酷炫粒子特效用 Canvas；画精密的 Logo、写交互清晰的小图表用 SVG。**

---

## 2. 第一笔：理解反直觉的坐标系

### 2.1 坐标翻转的原理

当你准备下笔时，得先明白 Canvas 里的尺子是反着的。对于传统的数学课坐标系，中心点零点在中间，越往上越大。但在计算机屏幕显示领域，几乎所有设备的“原点（0，0）”都定在**屏幕的最左上角**。向右走 X 轴变大没问题，但是**向下走，Y 轴变大。**

**Canvas 坐标系统的核心原则：**
- **原生单位：** 像素 (px)，与屏幕物理像素 1:1 对应。
- **X 轴：** 向右为正方向，从 `0` 到 `canvas.width`。
- **Y 轴：** 向下为正方向，从 `0` 到 `canvas.height`。

👇 拖拽下面的小圆点，直观感受计算机图形学中的坐标原点与走向：

<CoordinateSystemDemo />

### 2.2 给你的魔法画笔上调料

有了坐标体系，我们就能召唤画笔了（代码中称为 `Context`，或缩写 `ctx`）。就如同拿着真实的调色盘作画，Canvas 的 API 设计完美遵循了物理作画的三个步骤：

1. **调色（State）**：通过 `fillStyle` 设置填充色，`strokeStyle` 设置描边色。
2. **构形（Path）**：构思你是要画一条线（`lineTo`）、还是一个圆（`arc`）、亦或一个矩形（`rect`）。
3. **极简下笔（Render）**：决定是内部填充（`fill()`）还是勾勒边缘（`stroke()`）。

由于 Canvas 是纯粹的位图画布，“落子无悔”，你一旦画下，它立刻干涸成为像素，无法再被撤销为独立对象。

👇 尝试在下面的演示中挑选不同形状和颜色，看看背后的代码是如何执行上述“三步走”的：

<CanvasBasicsDemo />

---

## 3. 逐帧动画：Flipbook 模式与平滑优化

既然 Canvas 一旦填色就变成了永久的像素，那么各种 HTML5 页游里满屏乱跑的角色是怎么做出来的？

答案是**“骗过你的眼睛”**。这和手翻动画书或者电影胶片的原理一模一样。

1. **擦黑板（Clear）：** 用 `clearRect()` 把整块画布上的内容毫不留情地清空。
2. **计算新位置（Update）：** 让角色的 X 坐标往前偷偷加 2 个像素点。
3. **下笔重画（Render）：** 把角色在新的位置重新画一次。
4. **疯狂循环（Loop）：** 结合浏览器内置的极其精准的节拍器 `requestAnimationFrame`。它会以显示器的刷新率（通常是每秒 60 次，即 60 FPS）重复这三个动作。

由于人眼自带“视觉残留”，在每秒 60 次的【擦除 -> 更新 -> 重绘】中，你看到的不仅不是闪烁的黑板，反而是如同丝绸般顺滑的动画。

👇 在下方的演示中调整播放速度，观察每一帧的位移是如何连缀成流畅运动的：

<AnimationLoopDemo />

---

## 4. Canvas 交互检测：命中判定算法

因为 Canvas 画布在浏览器眼里只是一张没有任何结构的“颜料布”。假设你在画布上用 `arc()` 画了一只怪兽，当你想要实现“点击怪兽扣血”时，你**根本没法**使用传统的 `document.getElementById` 来获取这个怪兽。因为在 HTML 结构中，只有那个宽 600 像素的死板 `<canvas>` 标签。

这就是图形编程中最经典的问题：**碰撞检测 (Collision Detection) 与事件代理**。

由于浏览器只知道你的鼠标点击了 Canvas 的屏幕坐标 `(x, y)`，你需要自己去通过初中的几何数学进行反算：
- **对于圆形：** 通过勾股定理计算 `鼠标点击处` 到 `圆心位置` 的距离，如果距离小于半径，则说明“被点中了”。
- **对于矩形：** 判断点击的 `x` 是否在矩形的左右边界内，同时 `y` 是否在上下边界内。

无论你的画布上有多少元素，鼠标悬停或点击事件永远是绑定在 Canvas 这个唯一容器上的，这就是终极的“事件委托”。

👇 试着在下面使用鼠标（点击、拖拽、悬停）或键盘（方向键移动），体会这种“手动算距离”的底层交互逻辑：

<EventHandlingDemo />

---

## 5. 解放算力：粒子系统与视觉魔法

到了这一步，当我们把“坐标系”、“动画循环”以及“颜色与形状”全部融合，并将其数量暴增到成百上千个微小碎片时，你就掌握了引爆视觉的终极杀气：**粒子系统（Particle System）**。

其核心思路极其粗暴且有效：
1. 建立一个巨大的数组，里面塞满了几百个独立的“粒子对象”。
2. 每个对象拥有自己的独立生命周期（`life`）、加速度（`vx/vy`）、重力阻尼（`gravity`）。
3. 每次 `requestAnimationFrame` 触发时，遍历更新这几百个粒子，然后渲染，最后悄悄清理掉那些“死亡”（生命值耗尽/掉出屏幕）的粒子。

你的浏览器一瞬间就能变成一台制造烟花、大雪和爆炸的梦工厂。

👇 点击不同的效果，调整重力与粒子数，观察它们是如何通过最简单的物理数学公式呈现出复杂的群体视觉：

<ParticleSystemDemo />

---

## 6. 守护 FPS 荣耀：应对高烧的 CPU 的方法

让成千上万个对象在一秒内计算并重画 60 遍是非常消耗性能的。如果毫无章法，你的电脑风扇很快就会起飞。

以下是真正引擎大佬用来抢救帧率的“护体绝技”：

1. **局部擦黑板（脏矩形 Dirty Rect）：**
   一个角色在宽广的草原上奔跑，你千万不要每帧去 `clearRect` 整片大草原！角色经过哪一小块，你就用“小板擦”擦掉那一块并覆盖重绘，性能立刻飙升指数倍。

2. **后台替身魔法（离屏 Canvas）：**
   如果背景是繁星漫天、有着各种复杂绚丽的山脉，每次都实时渲染太蠢了。我们通常在内存里偷偷建一个看不见的 `<canvas>`，把它精美地画上去一次。之后的每一帧刷新中，只需要通过 `drawImage()` 将这张合成好的“静态底片”直接贴出，免去了海量的基础计算。

3. **批量洗画笔（Batching）：**
   调色盘里从红色换到蓝色，在底层是昂贵的。如果画布上有 1000 个红色圆和 1000 个蓝色圆交叉散落。最快的方法是：先把红颜料准备好，遍历画完所有红圈，再换蓝颜料画所有蓝圈。这是著名的批量渲染（Batch Rendering）思想。

👇 将对象数量拉到 3000 以上，看着网页掉进卡顿的深渊，再依次打开右下方的“优化技术”开关，亲眼见证实打实的帧率抢救：

<PerformanceDemo />

---

## 7. 专业名词总结

| 术语 | 通俗解释 |
| --- | --- |
| **Canvas** | HTML5 提供的 2D 画布。绘制极快，但画完就变成颜料像素，不支持通过 DOM 操作内容。 |
| **SVG** | 矢量图。放大永远不模糊，且每个图形都是独立的标签元素，可以轻易绑定各种 CSS 样式和交互。 |
| **Context (ctx)** | 你申请到的那支“2D 魔法画笔”，用来调色、设定形状和绘制各种特殊效果。 |
| **requestAnimationFrame** | 浏览器内置的神级节拍器，会严格依照显示器的刷新率执行回调，是制作丝滑动画的不二之选。 |
| **FPS (Frame Rate)** | 帧率。60 FPS 代表一秒内浏览器帮你无缝擦除了 60 次画布并重画了 60 副新图。 |
| **脏矩形 (Dirty Rect)** | 只在发生变化的那一点微小区域内进行精准擦除和重绘，从而强力保留性能。 |
| **离屏 Canvas** | 藏在内存里的“影子画布”。把极度复杂但不会动的景物提前画好，以后就当死贴图拿来重复使用。 |

> 从一条简单的直线段，到宏大绚丽的粒子系统引擎；一切看似魔法的特效，不过是每秒 60 次的坐标计算与重绘轮回罢了。
