从零打造 Canvas 飞机游戏与 ECharts 可视化:HTML5 前端双实战
小时候偷偷在电脑课上玩 Flash 小游戏,现在我用 300 行代码复刻了那个味道——还顺手搭了个销售数据大屏。
一、为什么选这两个项目?
HTML5 时代,Canvas 和 ECharts 是前端工程师的两把利器:
- Canvas:底层绘制 API,游戏、动画、可视化图表的基石
- ECharts:基于 Canvas 封装的数据可视化库,开箱即用
本文带你从 Canvas 原生 API 到 ECharts 高级配置,完成两个完整实战项目。
二、Canvas 飞机射击游戏:300 行代码的复古浪漫
2.1 游戏架构一览
requestAnimationFrame 主循环
├── 输入处理(键盘事件)
├── 游戏逻辑更新(移动、碰撞、生成)
└── 绘制渲染(星空背景、玩家、子弹、敌机)
2.2 核心机制拆解
帧动画:为什么不用 setInterval?
// ❌ 不推荐:setInterval 与屏幕刷新率不同步
setInterval(() => {
update(); draw();
}, 16); // 约60fps,但实际可能撕裂
// ✅ 推荐:requestAnimationFrame 自动适配刷新率
function loop(timestamp) {
update(timestamp);
draw();
requestAnimationFrame(loop); // 递归调用,与显示器同步
}
requestAnimationFrame(loop);
requestAnimationFrame 是浏览器提供的帧同步调度函数,它会自动匹配显示器的刷新率(通常是 60Hz 或 120Hz),避免画面撕裂和性能浪费。
碰撞检测:简单的矩形碰撞
function rectCollide(a, b) {
return (
Math.abs(a.x - b.x) < (a.w + b.w) / 2 &&
Math.abs(a.y - b.y) < (a.h + b.h) / 2
);
}
用中心点距离判断,比四个边界比较更简洁。虽然不够精确(飞机是三角形),但对街机游戏来说完全够用。
星空背景:伪随机滚动
for (let i = 0; i < 60; i++) {
const sx = (i * 137 + 50) % canvas.width;
const sy = (i * 211 + timestamp * 0.05) % canvas.height;
ctx.fillStyle = `rgba(255,255,255,${0.3 + (i % 5) * 0.15})`;
ctx.fillRect(sx, sy, 1.5, 1.5);
}
用固定乘数(137、211)生成伪随机坐标,配合 timestamp 实现滚动效果。不需要额外数组存储星星状态,纯函数式渲染,简洁优雅。
玩家飞机绘制:Canvas Path2D 基础
ctx.fillStyle = '#00d4ff';
ctx.beginPath();
ctx.moveTo(0, -player.h / 2); // 机头顶点
ctx.lineTo(-player.w / 2, player.h / 2); // 左翼
ctx.lineTo(0, player.h / 4); // 机尾凹槽
ctx.lineTo(player.w / 2, player.h / 2); // 右翼
ctx.closePath();
ctx.fill();
用 beginPath + moveTo + lineTo 绘制三角形机身,加上圆形驾驶舱和三角形尾焰,一个像素风飞机就诞生了。
2.3 游戏状态管理
let score = 0;
let gameOver = false;
let bullets = []; // 子弹池
let enemies = []; // 敌机池
// 射击冷却机制
const SHOOT_COOLDOWN = 200; // 毫秒
let lastShootTime = 0;
if (keys[' '] && timestamp - lastShootTime >= SHOOT_COOLDOWN) {
shoot();
lastShootTime = timestamp;
}
用时间戳差值做冷却控制,比 setTimeout 更精确,也更适合帧循环架构。
三、ECharts 销售数据可视化:让数据开口说话
3.1 数据层:优雅的 Getter 设计
const salesData = {
company: '肖氏电商集团',
category: '运动鞋',
unit: '百万元',
year: 2025,
monthly: [
{ month: '1月', sales: 18.7 },
// ... 12个月数据
],
get months() {
return this.monthly.map(item => item.month);
},
get values() {
return this.monthly.map(item => item.sales);
}
};
用 getter 动态提取数组,数据结构和展示逻辑解耦。修改原始数据时,图表自动同步。
3.2 图表配置:渐变柱状图
const option = {
title: {
text: `${salesData.company} — ${salesData.category}销售`,
subtext: `${salesData.year}年 月度销售额(${salesData.unit})`,
left: 'center'
},
series: [{
type: 'bar',
data: salesData.values,
itemStyle: {
color: {
type: 'linear',
x: 0, y: 0, x2: 0, y2: 1,
colorStops: [
{ offset: 0, color: '#667eea' }, // 顶部紫蓝
{ offset: 1, color: '#764ba2' } // 底部深紫
]
},
borderRadius: [6, 6, 0, 0] // 顶部圆角
},
emphasis: {
itemStyle: {
color: {
type: 'linear',
colorStops: [
{ offset: 0, color: '#f093fb' }, // hover 变粉红
{ offset: 1, color: '#f5576c' }
]
}
}
}
}]
};
渐变配色 + 圆角柱顶 + hover 高亮,三行配置让图表从"能看"变成"好看"。
3.3 响应式适配
window.addEventListener('resize', () => {
myChart.resize();
});
ECharts 内置 resize 方法,窗口变化时自动重绘,大屏展示必备。
四、两个项目的对比与思考
| 维度 | Canvas 飞机游戏 | ECharts 数据可视化 |
|---|---|---|
| 抽象层级 | 底层 API,逐像素控制 | 高层封装,声明式配置 |
| 开发效率 | 低(需手写绘制逻辑) | 高(配置即图表) |
| 灵活性 | 极高(任意图形、动画) | 中(受限于内置类型) |
| 适用场景 | 游戏、自定义动画 | 报表、Dashboard、大屏 |
| 性能优化 | 手动管理(对象池、脏矩形) | 自动优化(Canvas 底层) |
核心洞察:ECharts 本质上是 Canvas 的高级封装。理解 Canvas 原理,才能用好 ECharts;会用 ECharts,也要知道底层发生了什么。
五、工程化:Vite 脚手架
两个项目都用 Vite 初始化:
{
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build"
},
"dependencies": {
"echarts": "^6.1.0"
}
}
type: "module"原生 ESM,无需 Babel- Vite 开发服务器秒级冷启动
- 生产构建自动 Tree Shaking
六、写在最后
Canvas 是前端的"画笔",ECharts 是"印刷机"。一个让你自由创作,一个让你高效交付。
小时候玩游戏,现在写游戏——技术让热爱有了形状。
完整代码已开源,欢迎 Star 和 PR。
本文代码基于 Vite + ECharts 6.x + 原生 Canvas API,浏览器兼容性:Chrome 80+、Firefox 75+、Edge 80+。