一、canvas:小程序里唯一能"自己画"的地方
前面几个案例练的都是现成组件:滚动视图、地图、按钮,都是微信提供什么就用什么。canvas 不一样,它是一块近乎空白的画布——组件本身只负责给你一块区域,画什么、怎么画、什么颜色,全部要靠代码里的绘图指令一句一句描述。正因如此,canvas 是小程序里自由度最高、也最接近"编程创作"的组件,签名板、涂鸦、图表、数据可视化,底层都是它。
案例 4.10 的目标很清晰:做一个能在画布上自由绘图的小程序。界面是一块蓝色画布加一个"清空画布"按钮,手指在画布上拖动就能留下线条,点按钮画布恢复空白。麻雀虽小,但把这个案例做通,等于同时掌握了三件事:canvas 2D 新接口的初始化流程、触摸事件的坐标采集、以及绘图上下文的基础指令。
二、canvas 2D 新接口:先拿到节点,再拿上下文
canvas 组件有新旧两套接口,案例用的是新的 2D 接口。新接口的用法有一个明显特征:不能一上来就画,要先走一段"初始化仪式"。
这段仪式发生在页面初次渲染完成的时机。第一步,用选择器查询在页面里找到那个画布节点——注意要同时索取"节点"和"尺寸"两项信息,节点用来创建绘图上下文,尺寸用来适配画布大小。第二步,查询完成后在回调里拿到画布对象,用画布对象创建 2D 绘图上下文。这个上下文可以理解为"画笔",之后所有的画线、上色、擦除,都是对它下指令。
第三步最容易被忽略,却是画质好坏的关键:把画布的实际像素尺寸按设备像素比放大,再让上下文按同样比例缩放。原因是逻辑坐标和物理像素不是一回事——不做这一步,代码里画的每一笔在高清屏幕上会被拉伸成一团模糊的锯齿;做了这一步,线条在 retina 屏上依然锐利。这段代码在几乎所有 canvas 2D 教程里都会出现,属于"看不懂可以先背下来、理解后再回味"的部分。
初始化的最后,把画布对象和上下文对象都存到页面级别的变量里。后面每次触摸都要用它们,存成全局变量可以避免重复查询,这是很典型的"初始化一次、处处使用"的模式。
三、画线:起笔、运笔、描边三步走
自由绘图的实现思路是把手指的轨迹当成无数个连续的小线段。触摸事件提供了两个关键钩子:手指按下和手指移动。
按下时(起笔),从事件对象里取出触摸点相对于画布左上角的坐标,然后做三件事:设置线条颜色、设置线帽和线夹样式为圆头、设置线宽为 5 个像素。圆头很重要——如果用默认的平头,画出的线段接缝处会有毛刺,拐弯处尤其难看;圆头加圆滑拐角让整条线像用圆珠笔写出来的。最后把画笔移动到起点,这一步只定位、不落墨。
移动时(运笔),每次都取当前落点坐标,把线条延伸到这个点,然后描边。描边指令才真正把线画出来。因为移动事件触发频率非常高,手指划过的地方就被密集的短线段连成了流畅的曲线。
这里有个值得体会的设计:起笔和运笔时都重新设置了线条颜色。看上去是重复劳动,其实是刻意为之——只有每次下笔前都读取"当前颜色",中途换色才有意义。这也正是扩展功能能顺滑实现的前提。
四、清屏:一个路径习惯 + 一个矩形指令
清空画布只有两行指令,但顺序有讲究。先开启一条新路径,相当于把之前的笔画记录"翻篇";再在画布区域内执行清屏矩形,把所有像素擦成透明。清屏矩形用画布的实际像素尺寸来算范围,这也是为什么初始化时要把画布对象存起来——擦除范围要用它。
一个常见的误区是用"画一个背景色的矩形盖住"来清屏。那样能骗过眼睛,但画布上其实越积越多,时间长了会拖慢性能。正确的清屏是让像素归零,案例用的就是正规做法。
五、功能扩展:把颜色变成"可以变的"
作业要求增加一个按钮,可以改变画笔线条颜色。参考效果图展示了完整的使用流程:一开始用红色画,点一下按钮换成绿色继续画,再清空画布重新来。也就是说,按钮改变的不是"已画好的线"(那在画布上已经是像素了,改不了),而是"下一笔用什么颜色"。
实现的关键一步,是把颜色从写死的常量升级为页面数据里的一个状态。原来绘图指令里的颜色是一成不变的字符串,现在改为从页面数据中读取。按钮的处理函数非常简单:判断当前颜色是什么,切换成另一个颜色(案例效果图里是红和绿之间来回切换),同时可以在控制台打印一下当前颜色方便确认。
这个改动小到只有几行,却是小程序开发思想的又一次演练:界面上的按钮负责改数据,绘图逻辑只认数据。颜色状态一变,下次起笔和运笔自动采用新颜色,画布上的旧笔迹丝毫不受影响——因为它们已经是画好的像素,和"当前颜色"再无关系。想继续扩展也不难:把切换改成颜色数组循环,或者放一排色块让用户点选,都是同一套思路的延伸。
微信小程序 canvas 组件实战笔记
张小明
前端开发工程师
《人月神话》读后感
前段时间我给高并发内存池做收尾工作时,跑了一轮多线程benchmark。4 线程的时候数据还不错,但当我加到8个时,结果吞吐量不升反降。我第一反应是代码有bug,查了半天,发现没有,问了AI才知道真正的原因是线程太…
MySQL 数据库全栈运维实战系列 — 合集索引(24 篇)
MySQL 数据库全栈运维实战系列 — 合集索引(24 篇) 系列定位:《MySQL 数据库全栈运维实战系列》7 大模块 22 篇 2 篇番外,与《Oracle 数据库全栈运维实战系列(VMware 版)》同构对标,方便跨数据…
谍赞把一个“发内容”的动作,做成一条能跑通的流水线
——记一次内容运营团队的 AI 工作台落地项目记录|谍赞智能 企业 AI 转型顾问 服务方式:需求诊断 方案设计 培训 陪跑 兜底 记录时间:2026 年 10 月先把项目说清楚客户是一家做内容运营的团队,人不多,活很杂。选题…
C++课程资源复习地图:从语法到并发项目实战
简介:面向C入门到进阶学习者的黑马程序员课程资料包,聚焦课堂教学与项目实战的衔接,适合希望系统梳理C语法、面向对象编程及STL应用的自学者与在校学生。压缩包约64.14MB,内含课程文档手册、实验项目及多本PDF电子书,文…
STM32开发学习笔记之九【摄像头图像预处理】
STM32开发学习笔记之九【摄像头图像预处理】 前言 为了在STM32H743上实现手写数字识别,我们需要对摄像头输入的画面做一定的预处理,把摄像头图像变成神经网络能用的2828二值图,并用USB虚拟串口把处理结果送到电脑上位机进行调试。前置的CubeM…
slub restore_bytes
restore_bytes() 是 SLUB 调试里修复被破坏内存区域的辅助函数:把一段被越界写或 poison 破坏的字节恢复成期望值,并记录一条 FIX 日志。一、函数签名static void restore_bytes(struct kmem_cache *s, const char *message, u8 data,void *from, void *…