一三套绘图引擎总览
在砚码科创里,同一份“画图”需求可以有三套写法。它们语法不同,但绘图思路完全一致:设背景 → 选颜色 → 画图形 → (动态)每帧重画。先认清各自定位,再按需选用。
| 引擎 | 学生写什么 | 底层怎么跑 | 擅长场景 | |
|---|---|---|---|---|
| C++ | GOC 图形编程 | 标准 C++(JSCPP 解释器) | 浏览器 JS 解释执行 | 静态图形、竞赛入门、坐标与几何基础 |
| Python | Python 编程 | Python(Pyodide) | 浏览器 WASM 运行 | 算法、数据处理,可配 turtle 绘图 |
| p5.js | 动态绘图 | C++ 风格或Python 风格(语法壳) | 浏览器 canvas 实时渲染 | 动画、鼠标键盘交互、粒子、分形 |
二p5.js 核心概念
无论用哪套语法,最终都翻译成 p5.js。掌握这四个核心概念,就掌握了动态绘图。
1. 坐标系
画布像一张坐标纸:原点 (0,0) 在左上角,x 向右增大,y 向下增大。单位是像素。
- 画布大小:
createCanvas(宽, 高),如createCanvas(400, 300)。 - 常用宏:
width(画布宽)、height(画布高)——写动画时用它避免写死数值。
circle(x, y, 直径)。画矩形用左上角 + 宽高:rect(x, y, 宽, 高)。2. 颜色
p5.js 默认用 RGB,每个分量 0–255。可选加第 4 个参数表示透明度 alpha。
| 写法 | 含义 |
|---|---|
fill(255, 0, 0) | 填充纯红 |
fill(192, 57, 43) | 填充朱砂红(砚码主题色) |
fill(255, 100, 100, 60) | 填充半透明(alpha=60,颜色叠加可见) |
stroke(44, 95, 138) | 描边靛蓝;noStroke() 关闭描边 |
background(220) | 设背景(单参数=灰度) |
3. setup 与 draw:静态 vs 动态
这是 p5.js 的灵魂:
setup():只在开始时执行一次——建画布、设初始状态。draw():每秒约 60 次循环执行——每帧重画,形成动画。- 只要在
draw()里用了会变的量(frameCount、mouseX…),画面就“动”起来。
setup() 末尾写 noLoop(),draw() 只执行一次。4. 交互:鼠标与键盘
| 系统变量 | 含义 |
|---|---|
mouseX / mouseY | 鼠标当前位置 |
mouseIsPressed | 鼠标是否按下(true/false) |
keyIsPressed | 是否正按着键盘 |
frameCount | 已渲染的帧数(从 0 开始,常作“时间”) |
把图形坐标写成 mouseX, mouseY,图形就会跟着鼠标走。
三C++ 风格绘图
用 C++ 的语法习惯写,底层转译成 p5.js。只替换了类型关键字和函数定义,循环、分支、绘图 API 原样保留,几乎感受不到底层是 JS。
语法对照
| 你写的 C++ 风格 | 转译成 p5.js |
|---|---|
void setup() { } | function setup(){ } |
void draw() { } | function draw(){ } |
int x = 100; | let x = 100; |
float y = 50.5; | let y = 50.5; |
for(int i=0; i<10; i++) | 原样保留(绘图里就是 JS 循环) |
circle(x, y, r); | 原样保留(函数名与 p5.js 完全一致) |
最小示例:一个圆
C++ 风格void setup() { createCanvas(400, 300); } void draw() { background(220); fill(192, 57, 43); circle(200, 150, 100); }
动画示例:鼠标跟随 + 循环
C++ 风格void setup() { createCanvas(400, 300); } void draw() { background(20, 20, 30, 40); // 半透明背景 → 留下轨迹 for(int i = 0; i < 6; i++) { fill(255, i * 40, 100); circle(mouseX + i * 10, mouseY, 30); } }
四Python 风格绘图
用 Python 的缩进写法,转译器按缩进补大括号,映射 def / for range / if / while。
语法对照
| 你写的 Python 风格 | 转译成 p5.js |
|---|---|
def setup(): | function setup(){ |
def draw(): | function draw(){ |
for i in range(8): | for(let i=0; i<8; i+=1){ |
if mouseIsPressed: | if(mouseIsPressed){ |
else: / elif x: | else{ / else if(x){ |
# 注释 | // 注释 |
最小示例:一个圆
Python 风格def setup(): createCanvas(400, 300) def draw(): background(220) fill(192, 57, 43) circle(200, 150, 100)
动画示例:循环点阵
Python 风格def setup(): createCanvas(400, 300) def draw(): background(20) for i in range(8): for j in range(6): fill(255, i * 30, j * 40) circle(40 + i * 48, 30 + j * 46, 26)
五绘图 API 速查(三语对照)
以下 API 在 C++ 风格、Python 风格、p5.js 里写法完全一致(语法壳不改变函数名)。
| 图形 / 功能 | C++ / Python 写法 | 说明 |
|---|---|---|
| 画布 | createCanvas(400, 300) | 宽 × 高(像素) |
| 背景 | background(220) | 也可 RGB:background(255,0,0) |
| 填充色 | fill(r, g, b) | 可加第 4 位透明:fill(r,g,b,a) |
| 描边色 | stroke(r, g, b) | noStroke() 关,noFill() 关填充 |
| 描边粗细 | strokeWeight(n) | n 为像素 |
| 圆 | circle(x, y, d) | 中心(x,y),d 为直径 |
| 椭圆 | ellipse(x, y, w, h) | 中心(x,y),宽 w 高 h |
| 矩形 | rect(x, y, w, h) | 左上角(x,y),宽 w 高 h |
| 直线 | line(x1, y1, x2, y2) | 两点连线 |
| 三角形 | triangle(x1,y1, x2,y2, x3,y3) | 三个顶点 |
| 点 | point(x, y) | 一个像素点 |
| 文字 | text("内容", x, y) | 先 textSize(n)、textAlign() |
| 随机数 | random(最小值, 最大值) | 含小数的随机数 |
| 映射 | map(值, a1, a2, b1, b2) | 把值从区间[a1,a2]映射到[b1,b2] |
| 平移 | translate(x, y) | 原点移到(x,y),便于围绕中心旋转 |
| 正弦 | sin(弧度) | 配合 frameCount 做波浪 |
六教学路径:从静态到动态
按台阶递进,每一步都能看到即时反馈,适合课上一次带学生“动起来”。
第 1 步静态图形 · 建立坐标感
画圆、矩形、直线;理解坐标系、RGB 颜色、setup 里加 noLoop()。一次一屏,先不追求动态。
第 2 步循环生成 · 重复的威力
用 for / range 画一排圆、点阵、棋盘格。体会“算法生成图案”。
第 3 步实时动画 · 动起来
去掉 noLoop(),用 frameCount 让图形运动、旋转、明暗变化。
第 4 步鼠标交互 · 人机对话
图形跟随 mouseX / mouseY,按 mouseIsPressed 变色。画面开始“回应”操作者。
第 5 步数学与物理 · 探究规律
用 sin / cos 做正弦波、行星轨道;map 映射数值;为 GESP / 科创选题铺路。
第 6 步创意综合 · 作品化
粒子、分形、迷你动画。结合“砚码五宝”探究流程,做成可截图存证的作品。
七经典示例(可直接在动态绘图里运行)
1. 正弦波 动态C++
C++void setup() { createCanvas(400, 300); } void draw() { background(240); stroke(44, 95, 138); noFill(); beginShape(); for(int x = 0; x <= width; x += 5) { float y = height/2 + sin(x*0.05 + frameCount*0.05) * 60; vertex(x, y); } endShape(); }
2. 行星轨道 动态Python
Pythondef setup(): createCanvas(400, 300) def draw(): background(10, 10, 20) translate(width/2, height/2) fill(255, 210, 120) circle(0, 0, 50) fill(120, 180, 255) circle(cos(frameCount * 0.05) * 90, sin(frameCount * 0.05) * 90, 18)
3. 点击变色 交互Python
Pythondef setup(): createCanvas(400, 300) def draw(): background(240) if mouseIsPressed: fill(255, 100, 100) else: fill(44, 95, 138) circle(mouseX, mouseY, 40)
4. 粒子雨 创意C++
C++int n = 60; float px[60], py[60]; void setup() { createCanvas(400, 300); for(int i=0; i<n; i++){ px[i]=random(0,width); py[i]=random(0,height); } } void draw() { background(20, 20, 30, 40); for(int i=0; i<n; i++){ py[i] += 2; if(py[i]>height){ py[i]=0; px[i]=random(0,width); } fill(150, 200, 255, 160); circle(px[i], py[i], 4); } }
八常见错误排查
C++ 风格
| 现象 | 原因与解决 |
|---|---|
| 提示“变量未定义” | 用了没声明的变量。先 int x=0; 再使用。 |
| 画不出图形 | 检查是否 noFill() 且没设 fill();或 noStroke() 且图形只有描边。 |
| 页面卡死 | 可能在 draw() 里写了死循环(如 while(1))。点“停止/重启”强制刷新,删掉死循环。 |
Python 风格
| 现象 | 原因与解决 |
|---|---|
| “语法错误:括号/缩进” | 缩进不一致。用统一空格(建议 4 个),别混用 Tab。 |
for i in range(8) 报错 | 少写了冒号 :。每行块开头必须有冒号。 |
| 颜色没变化 | 动画里每帧都 fill() 一次;用 frameCount 让参数变化。 |
通用
noLoop();确认 draw() 里用了会变化的量。九课时建议(2–3 节课)
| 课时 | 主题 | 要点 |
|---|---|---|
| 第 1 节 | 坐标系 · 静态图形 | setup/draw、background、fill、circle/rect;两种语法都跑一遍 |
| 第 2 节 | 循环 + 动画 | for/range 生成图案;去掉 noLoop,用 frameCount 动起来 |
| 第 3 节 | 交互 + 作品 | 鼠标跟随、点击变色;正弦波/粒子;截图导出存证 |
建议结合「砚码五宝」流程:选题(砚)→ 探究(码)→ 表达(笔)→ 存证(纸),让绘图作品进入项目与证据链。