Canvas 和 ECharts:一个蓝色方块引发的血案

从 Canvas 画方块到打飞机

本文记录了我从零开始学习 Canvas 绘图、用 requestAnimationFrame 做游戏循环,再到用 ECharts 做数据可视化的全过程。所有代码都是我自己写的,附带了和 AI 对话的提示词,希望能给正在学习前端可视化的同学一些参考。


一、先扔个方块:Canvas 初体验

学习前端可视化,绕不开 Canvas。这东西说白了就是一个画布标签,配合 JS API,你想怎么画就怎么画。

第一个 Demo:画矩形

最简单的,画一个蓝色矩形:

js 复制代码
const canvas = document.getElementById('myCanvas')
const ctx = canvas.getContext('2d')
ctx.fillStyle = '#4299E1'
ctx.fillRect(20, 20, 100, 80)

getContext('2d') 拿到的就是画笔上下文,后面所有绘制操作都通过它来执行。fillRect(x, y, width, height) 从左上角开始画矩形,坐标原点在 Canvas 左上角。

我还试了画边框矩形和清除矩形:

js 复制代码
ctx.strokeStyle = '#f56565'   // 边框颜色
ctx.lineWidth = 4             // 边框宽度
ctx.strokeRect(150, 20, 100, 80)  // 画边框矩形
ctx.clearRect(50, 50, 40, 30)     // 擦掉一小块

clearRect 就像橡皮擦,能把画布上指定区域清空。

让方块动起来

光画静态的没意思,我想让方块动起来。最简单的思路:先擦掉旧的,再画新的

js 复制代码
let x = 20
const speed = 3

function animate() {
  ctx.clearRect(0, 0, canvas.width, canvas.height)  // 擦掉整块画布
  ctx.fillStyle = '#4299E1'
  ctx.fillRect(x, 20, 100, 80)  // 在新的位置画
  x += speed                      // 更新位置

  if (x > canvas.width) {        // 超出边界就回到起点
    x = -100
  }

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

这里有一个关键点:为什么不用 setInterval,而用 requestAnimationFrame?

方法 说明
setInterval(cb, 16) 强行每隔 16ms 执行一次,跟显示器刷新频率可能不同步,导致掉帧或卡顿
requestAnimationFrame(cb) 浏览器自动跟随屏幕刷新率(60Hz),帧率协调,体验更流畅

我的笔记里有句话:「requestAnimationFrame 等于刷帧率,体验更协调」。这就是它的核心优势------跟显示设备同频共振


二、上强度:用 Canvas 写一个打飞机小游戏

方块会动了,那就搞点真东西------打飞机小游戏

提示词

我是这样让 AI 帮我梳理需求的:

用原生 HTML Canvas 写一个打飞机小游戏:

  1. 底部玩家飞机,方向键上下左右移动,边界限制
  2. 空格键发射子弹,子弹向上飞行
  3. 顶部随机生成敌机向下移动
  4. 子弹击中敌机销毁并加分,敌机碰到玩家游戏结束
  5. 用 requestAnimationFrame 做游戏循环,全部绘制在 canvas 里,不要引入第三方游戏引擎

项目结构

bash 复制代码
ailplane/
├── index.html
├── package.json
├── src/
│   ├── main.js        # 入口:初始化 Canvas、键盘监听、启动循环
│   ├── game.js        # 游戏主循环:update → draw
│   ├── player.js      # 玩家飞机:移动、绘制、碰撞包围盒
│   ├── bullet.js      # 子弹系统:发射、移动、回收
│   ├── enemy.js       # 敌机系统:随机生成、动态难度、移动
│   ├── collision.js   # AABB 碰撞检测
│   ├── renderer.js    # 渲染:星空背景、HUD、Game Over
│   └── style.css

核心架构:游戏循环

整个游戏的核心在 game.js,遵循标准的游戏循环模式

js 复制代码
class Game {
  loop(keys) {
    const now = performance.now()
    this.update(keys, now)   // 第一步:更新逻辑
    this.draw()               // 第二步:绘制画面
    this.animationId = requestAnimationFrame(() => this.loop(keys))
  }
}

每一帧做两件事:

  1. update --- 处理输入、移动物体、检测碰撞、更新分数
  2. draw --- 清空画布,重新绘制所有对象

玩家飞机:用 API 画出来的战机

我没有用图片,而是直接用 Canvas API 手绘了一架飞机:

js 复制代码
// player.js --- 绘制飞机
draw(ctx) {
  const cx = this.x + this.width / 2
  const cy = this.y + this.height / 2

  ctx.save()
  ctx.translate(cx, cy)

  // 引擎火焰
  ctx.fillStyle = '#ff6600'
  ctx.beginPath()
  ctx.moveTo(-8, this.height / 2 - 2)
  ctx.lineTo(0, this.height / 2 + 12)
  ctx.lineTo(8, this.height / 2 - 2)
  ctx.closePath()
  ctx.fill()

  // 机身主体
  ctx.fillStyle = '#4488ff'
  ctx.beginPath()
  ctx.moveTo(0, -this.height / 2)     // 机头
  ctx.lineTo(-10, this.height / 2 - 4) // 左下
  ctx.lineTo(10, this.height / 2 - 4)  // 右下
  ctx.closePath()
  ctx.fill()

  ctx.restore()
}

save()restore() 用来保存和恢复画笔状态,避免 translate 影响到后面的绘制。

碰撞检测:AABB 算法

子弹打没打中敌机,敌机有没有撞到玩家,靠的是 AABB(轴对齐包围盒)碰撞检测

js 复制代码
// collision.js
export function checkCollision(a, b) {
  return (
    a.x < b.x + b.width &&
    a.x + a.width > b.x &&
    a.y < b.y + b.height &&
    a.y + a.height > b.y
  )
}

原理很简单:两个矩形的四条边,如果都没有在对方之外,那就碰撞了。 这比用圆形碰撞检测要轻量得多,适合子弹数量多的情况。

动态难度:分数越高敌机越快

js 复制代码
// enemy.js
getSpeed(score) {
  const speed = this.baseSpeed + Math.floor(score / 100) * 0.5
  return Math.min(speed, MAX_SPEED)
}

每得 100 分,敌机速度就提升 0.5,上限 5。同时生成间隔也会随分数缩短,分数越高压力越大。

子弹冷却:防止贴脸秒杀

js 复制代码
// bullet.js
const FIRE_COOLDOWN = 200  // 毫秒

fire(x, y, now) {
  if (now - this.lastFireTime < FIRE_COOLDOWN) return
  this.bullets.push(new Bullet(x, y))
  this.lastFireTime = now
}

用时间戳控制发射频率,防止按住空格键变成机关枪。


三、数据可视化:用 ECharts 画柱状图

游戏写完了,换个口味。用 Vite 搭了一个 ECharts 项目,老板想看一年运动鞋的销售数据。

提示词

帮我随机生成肖氏电商集团一年运动鞋的每个月销售数据,有一定的涨跌,以百万元为单位,等下用于柱状图的绘制,单独帮我放到一个 data.js 文件中

数据文件

js 复制代码
// data.js
export const salesData = {
  company: '李氏电商集团',
  product: '运动鞋',
  unit: '百万元',
  months: [
    { month: '1月',  sales: 8.2 },
    { month: '2月',  sales: 6.5 },
    { month: '3月',  sales: 9.8 },
    // ... 全年数据
    { month: '11月', sales: 21.9 },
    { month: '12月', sales: 10.4 },
  ],
}

数据有季节性涨跌:春节后走低(2月),暑期和双十一走高(10-11月),符合电商的真实规律。

用 ECharts 绘制

js 复制代码
import * as echarts from 'echarts'

const chartDom = document.getElementById('chart')
const myChart = echarts.init(chartDom)

const option = {
  xAxis: { type: 'category', data: categories },
  yAxis: { type: 'value', name: '销售额(百万元)' },
  series: [{
    type: 'bar',
    data: values,
    itemStyle: {
      color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
        { offset: 0, color: '#83bff6' },
        { offset: 1, color: '#188df0' },
      ]),
      borderRadius: [4, 4, 0, 0],
    },
  }],
}

myChart.setOption(option)

这里用了 线性渐变 给柱状图着色,顶部浅蓝、底部深蓝,比纯色好看很多。borderRadius 让柱子顶部带圆角,细节提升质感。


四、经验和总结

模块 技术栈 核心知识点
Canvas 基础 原生 Canvas 2D API fillRect、clearRect、坐标系统
动画循环 requestAnimationFrame GPU 同步、帧动画模式
打飞机游戏 Canvas + Vite 游戏循环、AABB碰撞、对象池
数据可视化 ECharts + Vite 柱状图配置、线性渐变

几个值得记住的点:

  1. Canvas 是状态机 --- save/restore 管理绘制状态,translate/rotate 变换坐标系,这些是写复杂绘制逻辑的基础
  2. 游戏循环 = update + draw --- 逻辑和数据更新分离,每一帧先算后画,这是所有游戏引擎的底层模式
  3. 不要用 setInterval 做动画 --- requestAnimationFrame 是浏览器亲儿子,帧率同步、省电、不掉帧
  4. ECharts 配置项很丰富 --- 渐变、圆角、tooltip 自定义,花点时间调样式,数据展示效果会好很多

以上就是我这三天的学习成果。从一个蓝色方块到完整的打飞机游戏,再到数据可视化图表,每一步都踩了一些坑但也学到了东西。

欢迎评论区交流:你写过哪些有意思的 Canvas 项目?或者你最想实现什么效果但一直没动手?

相关推荐
mimu34561 小时前
做PPT方案适合搭配哪些办公效率工具
人工智能
蓝速科技1 小时前
蓝速科技 AI 数字人部署与交互实战指南
人工智能·科技·交互
雪隐1 小时前
个人电脑玩AI-03让5060 Ti给你打工——paddleOCR
人工智能·后端
Coffeeee1 小时前
Codachi — 藏在 Claude Code 状态栏里的电子宠物
人工智能·程序员·claude
张某布响丸辣1 小时前
Spring AI 极简入门:Java 开发者快速上手 AI 开发
java·人工智能·spring·springai
Deepoch2 小时前
VLA多模态架构加持 采摘机器人实现精细化智能采收
人工智能·机器人·开发板·具身模型·deepoc·采摘
橘子星2 小时前
基于 Vite 的多模态生图前端工程实践
前端·javascript·人工智能
谁似人间西林客2 小时前
工业AI原生企业是什么?制造业智能化升级的新路径
大数据·人工智能·ai-native
段一凡-华北理工大学2 小时前
LangChain框架在高炉炼铁智能化领域的应用~系列文章09:工具调用Tool — 让AI学会操作高炉仪表盘
网络·人工智能·架构·langchain·高炉炼铁·高炉智能化·高炉智能体
工业胶粘剂技术2 小时前
K-1306双组份丙烯酸结构胶技术白皮书:TDS全参数解析、核壳增韧机理与高端制造选型指南
大数据·人工智能·制造