一、引言canvas画布是微信小程序中用于自由绘图、图表展示、游戏渲染的核心组件。通过 canvas 2D 绘图上下文开发者可以像使用浏览器 Canvas API 一样在画布上绘制线条、图形、文字等。本文以「案例 4.10 canvas 组件」为主线带你完成一个可在画布上自由绘图的小程序并在此基础上完成作业扩展增加一个可以改变画笔线条颜色的按钮。二、项目结构小程序项目根目录 ├── app.json 全局配置 ├── app.wxss 全局样式 └── pages └── index ├── index.json 页面配置 ├── index.wxml 页面结构 ├── index.wxss 页面样式 └── index.js 页面逻辑三、页面布局设计1. 自定义顶部导航栏为保证任何机型下标题稳定显示采用自定义固定导航栏view classnav-bar stylepadding-top: {{statusBarHeight}}px; text classnav-titlecanvas组件/text /view.nav-bar { position: fixed; top: 0; left: 0; right: 0; height: 88rpx; background: #ffffff; display: flex; align-items: center; justify-content: center; box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.08); z-index: 999; }padding-top 用状态栏高度statusBarHeight撑开避开手机顶部状态栏区域。2. 画布与按钮区scroll-view classscrollarea scroll-y typelist view classpage-content view classbox view classtitle自由绘图/view canvas type2d idmy-canvas bind:touchstartpaintStart bind:touchmovepaintMove / view classbtn-layout button typewarn sizemini bind:tapclear清空画布/button button typeprimary sizemini bind:tapchangeColor改变画笔颜色/button /view /view /view /scroll-view核心canvas type2d声明使用 Canvas 2D 接口bind:touchstart/bind:touchmove负责捕获手指按下与滑动用于绘图。四、Canvas 2D 绘图上下文创建核心微信小程序 Canvas 2D 接口与浏览器标准 API 一致需要通过选择器拿到 canvas 原生节点再调用getContext(2d)获取绘图上下文onReady() { const query wx.createSelectorQuery() query.select(#my-canvas) .fields({ node: true, size: true }) .exec((res) { const canvas res[0].node // canvas 原生对象 const ctx canvas.getContext(2d) // 2d 绘图上下文 const dpr wx.getWindowInfo().pixelRatio // 设备像素比 // 设置画布宽高适配高清屏 canvas.width res[0].width * dpr canvas.height res[0].height * dpr // 坐标系缩放保证绘制清晰 ctx.scale(dpr, dpr) this.canvas canvas this.ctx ctx }) }关键点fields({ node: true, size: true })同时获取节点实例和尺寸canvas.width / height用逻辑尺寸 × dpr避免高清屏模糊ctx.scale(dpr, dpr)让坐标单位与逻辑像素一致canvas.width 赋值会清空画布因此只能初始化时执行一次五、自由绘图实现1. 手指按下 —— 起点paintStart(e) { const x e.touches[0].x const y e.touches[0].y const ctx this.ctx ctx.strokeStyle this.data.currentColor // 线条颜色 ctx.lineCap round // 端点圆角 ctx.lineJoin round // 连接点圆角 ctx.lineWidth 5 // 线宽 ctx.moveTo(x, y) // 起点 }2. 手指滑动 —— 连续绘制paintMove(e) { const x e.touches[0].x const y e.touches[0].y const ctx this.ctx ctx.strokeStyle this.data.currentColor ctx.lineTo(x, y) // 连线到新位置 ctx.stroke() // 绘制路径 }moveTo 定起点lineTo stroke 持续画线实现流畅的自由手绘。3. 清空画布clear() { const canvas this.canvas const ctx this.ctx ctx.beginPath() // 开始新路径 ctx.clearRect(0, 0, canvas.width, canvas.height) // 清空矩形区域 }六、作业扩展改变画笔颜色作业要求在案例基础上增加一个「改变画笔颜色」的按钮且只影响接下来新画的线条已画内容颜色不变。实现思路用 data 维护一个颜色列表 colors 和当前颜色 currentColor点击按钮在颜色列表中循环切换 currentColor绘图时读取 currentColor 作为 strokeStyledata: { colors: [#ff0000, #0000ff, #00ff00, #ff9900, #9900ff, #000000], currentColor: #ff0000 // 刚开始画笔的颜色红色 }, changeColor() { const colors this.data.colors const cur this.data.currentColor const idx colors.indexOf(cur) const next colors[(idx 1) % colors.length] // 取下一个循环 this.setData({ currentColor: next }) }关键原理canvas 已画内容是固化像素改 strokeStyle 只决定新笔触的颜色不会回头修改旧线条。因此只要不重设 canvas.width、不重绘画布旧线条颜色就保持原样。七、完整代码清单app.json{ pages: [pages/index/index], window: { navigationBarTextStyle: black, navigationStyle: custom }, style: v2, renderer: webview, sitemapLocation: sitemap.json }app.wxss.box { margin: 20rpx; padding: 20rpx; border: 2rpx solid purple; } .title { font-size: 40rpx; font-weight: bolder; text-align: center; margin-bottom: 30rpx; color: red; }pages/index/index.wxmlview classnav-bar stylepadding-top: {{statusBarHeight}}px; text classnav-titlecanvas组件/text /view scroll-view classscrollarea scroll-y typelist view classpage-content view classbox view classtitle自由绘图/view canvas type2d idmy-canvas bind:touchstartpaintStart bind:touchmovepaintMove / view classbtn-layout button typewarn sizemini bind:tapclear清空画布/button button typeprimary sizemini bind:tapchangeColor改变画笔颜色/button /view /view /view /scroll-viewpages/index/index.wxsspage { height: 100vh; display: flex; flex-direction: column; } .nav-bar { position: fixed; top: 0; left: 0; right: 0; height: 88rpx; background: #ffffff; display: flex; align-items: center; justify-content: center; box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.08); z-index: 999; } .nav-title { font-size: 34rpx; color: #000000; font-weight: 500; } .scrollarea { flex: 1; overflow-y: hidden; } .page-content { padding-top: 260rpx; } canvas { width: 100%; height: 800rpx; background-color: cornflowerblue; } .btn-layout { margin-top: 20rpx; display: flex; justify-content: center; } .btn-layout button { margin: 0 10rpx; }pages/index/index.jsPage({ data: { statusBarHeight: 20, colors: [#ff0000, #0000ff, #00ff00, #ff9900, #9900ff, #000000], currentColor: #ff0000 }, onLoad() { const info wx.getWindowInfo() this.setData({ statusBarHeight: info.statusBarHeight }) }, onReady() { const query wx.createSelectorQuery() query.select(#my-canvas) .fields({ node: true, size: true }) .exec((res) { const canvas res[0].node const ctx canvas.getContext(2d) const dpr wx.getWindowInfo().pixelRatio canvas.width res[0].width * dpr canvas.height res[0].height * dpr ctx.scale(dpr, dpr) this.canvas canvas this.ctx ctx }) }, paintStart(e) { const x e.touches[0].x const y e.touches[0].y const ctx this.ctx ctx.strokeStyle this.data.currentColor ctx.lineCap round ctx.lineJoin round ctx.lineWidth 5 ctx.moveTo(x, y) }, paintMove(e) { const x e.touches[0].x const y e.touches[0].y const ctx this.ctx ctx.strokeStyle this.data.currentColor ctx.lineTo(x, y) ctx.stroke() }, changeColor() { const colors this.data.colors const cur this.data.currentColor const idx colors.indexOf(cur) const next colors[(idx 1) % colors.length] this.setData({ currentColor: next }) }, clear() { const canvas this.canvas const ctx this.ctx ctx.beginPath() ctx.clearRect(0, 0, canvas.width, canvas.height) } })八、运行与调试要点修改 app.json 后热重载不生效需点击【编译】或【清缓存】【编译】。canvas.width/height 只在 onReady 初始化时设置一次再次赋值会清空画布。画笔颜色存在 data.currentColor改色只影响新笔触旧线条像素不变。高清屏模糊问题通过 dpr 缩放解决。九、总结通过案例 4.10我掌握了Canvas 2D 绘图上下文的创建getContext (‘2d’) dpr 适配基于触摸事件touchstart /touchmove的自由绘图清空画布与画笔颜色切换扩展关键原理canvas 内容是固化像素改 strokeStyle 不影响旧笔迹
企业数字化 ERP 产品动态
相关推荐
C# 委托与事件 在.NET Core的开发体系中,委托是构建灵活、低耦合应用架构的核心基础,它打破了传统方法调用的强绑定关系,让开发者可以将方法作为一等对象进行传递和管理。从基础的函数指针封装,到事件机制、观察者模式的落地实现,委托… · 2026/11/5 3:48:17
Qt 对话框、QLineEdit 与布局管理实战详解 目录
引言
一、模态对话框:accept /reject 消息机制
1. 项目搭建
2. 主界面 ↔ 登录界面互相切换
二、QT内置标准对话框
2.1 QColorDialog 颜色对话框
2.2 QFileDialog 文件选择对话框
2.3 QInputDialog 输入对话框(4 种类型)
2.4 Q… · 2026/11/5 3:48:17
alertmanger 告警rule文件配置(二) #样例groups:
- name: dev-node-exporterrules:- alert: dev-node-exporter-down #告警名称expr: up{job"node-exporter"} 0 # PromQL表达式for: 5m #最大持续时间labels: serverity: 100 # 告警程度
# team: test #team分组 Alertmanager对应值分组告警an… · 2026/11/5 3:48:17
量子神经混合计算:72维超智能系统架构解析 1. 项目概述:量子-神经混合超智能生态系统的诞生背景在当代科技发展的最前沿,量子计算与神经科学的交叉融合正在催生新一代智能系统架构。7FLOOR Seventy-Two-Pointed Star(72星)超维工程系统正是这一趋势下的代表性产物。这个系统… · 2026/11/5 4:41:54
X平台开源推荐算法解析与工程实践 1. 开源推荐算法的行业影响解析当全球最具影响力的社交平台之一决定公开其核心算法时,这不仅仅是一次技术透明化的尝试,更是对内容分发行业的一次颠覆性冲击。作为从业十余年的推荐系统工程师,我亲历过多个主流平台的算法迭代周期,… · 2026/11/5 4:41:54
AI语音交互革新:大模型如何提升智能客服体验 1. 项目概述:AI语音交互的范式革新去年夏天,我参与了一个智能客服系统的改造项目。当时客户抱怨最多的问题是:"为什么机器人总是答非所问?"这个痛点在传统语音交互领域普遍存在——系统能识别语音转文字,却无… · 2026/11/5 4:41:54
专科生必备的9款AI工具:降低门槛提升效率 1. 项目概述作为一名在AI工具领域摸爬滚打多年的从业者,我深知专科生在学习和工作中使用AI工具时面临的独特挑战。专科教育更注重实践技能培养,但往往缺乏系统的AI理论学习,这就导致很多同学在使用AI工具时容易陷入"会用但不懂原理"… · 2026/11/5 4:41:54
轻量级Raw Socket流量监控系统:WinXP兼容、无驱动、单文件抓包 简介:这是一套基于Visual C开发的Windows平台网络流量监控系统源码,面向C中级开发者、网络编程学习者及网络安全实践人员,用于理解数据包捕获、协议解析、实时流量统计与MFC界面集成等核心能力。资源共24个文件,含8个头文件&#… · 2026/11/5 4:41:54
零基础3个月速成网络安全工程师实战指南 1. 项目概述作为一名在网络安全领域摸爬滚打多年的从业者,我经常被问到同一个问题:"零基础如何快速入门网络安全?"今天我就把自己当年用3个月时间从完全不懂到能够独立完成基础渗透测试的学习路径完整分享出来。这份清单之所以被称… · 2026/11/5 4:40:54
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/4 3:16:49
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/4 6:59:29
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/4 7:02:38
多智能体协作系统架构设计与实践优化 1. 多智能体协作系统的时代背景2017年Transformer架构的诞生彻底改变了人工智能的发展轨迹。当行业还沉浸在单一大模型带来的性能突破时,Anthropic的研究团队已经将目光投向了更前沿的领域——如何让多个智能体通过协作产生超越单体能力的系统智能。这就像交响乐团中… · 2026/11/5 0:00:39
大模型系统性入门实战指南:从原理到部署微调的全链路路线 提到大模型的系统性入门,我先说个结论:这事真不难,难的是资料太散、路线太多,今天这个框架明天那个工具,新人很容易在“收藏夹吃灰”和“教程看到一半放弃”之间反复横跳。市面上关于大模型的内容少说有几十万篇&#… · 2026/11/5 0:00:39
鸿蒙文件系统沙箱机制解析与开发实践 1. 鸿蒙文件系统沙箱机制深度解析作为一名从Android转向鸿蒙开发的工程师,我深刻体会到文件系统访问是迁移过程中最先遇到的"拦路虎"。在Android上运行良好的文件读写代码,在鸿蒙真机环境却频频出现文件写入后神秘消失或直接抛出权限拒绝错误的… · 2026/11/5 0:00:39
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/4 3:16:49
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/4 6:59:29
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/4 7:02:38