【OpenHarmony/HarmonyOs 】深色模式下的 Canvas 数学画板坐标轴、网格、标签与曲线可读性优化项目类型OpenHarmony / HarmonyOS ArkTS 数学学习应用项目名称数学视界对应主题沉浸光感、全新视觉与交互体验关键词ArkTS、Canvas、深色模式、数学画板、坐标轴、可读性 一、为什么 Canvas 深色模式要单独写普通 ArkUI 组件可以通过backgroundColor、fontColor切换深浅色但 Canvas 不一样。Canvas 里的网格、坐标轴、刻度、标签、曲线都是手动绘制的。如果只把页面背景变黑而不处理 Canvas 内部颜色就会出现网格看不清坐标轴太暗标签和背景混在一起曲线颜色对比度不足数学图像难以阅读。数学视界项目中的CanvasBoard.ets对 Canvas 深色模式做了手动适配这篇文章就专门讲这部分。二、深色模式状态来源画板页在进入时读取主题状态aboutToAppear(): void { const themeManager ThemeManager.getInstance() this.isDarkMode themeManager.getIsDark() themeManager.addListener((isDark: boolean) { this.isDarkMode isDark }) this.loadBoardData() }并提供颜色选择函数getColor(lightColor:string,darkColor:string):string{ return this.isDarkMode ? darkColor : lightColor }Canvas 绘制时不能自动继承主题色所以所有绘制颜色都需要主动调用getColor()。三、画布背景先清屏再填充绘制坐标系时先清空画布ctx.clearRect(0, 0,w,h)然后根据主题填充背景ctx.fillStyle this.getColor(#FFFFFF,#000000) ctx.fillRect(0,0, w, h)浅色模式下是白色背景深色模式下是黑色背景。这一步必须在绘制网格和坐标轴之前完成否则旧图像可能残留或者背景和线条层级错乱。四、网格颜色深色下不能太亮也不能太暗网格绘制代码drawGrid(ctx: CanvasRenderingContext2D, origin: DrawPoint): void { const gridStep: number this.getGridStep() ctx.strokeStyle this.getColor(#E8EDF2,#3A3A5A) ctx.lineWidth0.5}浅色模式#E8EDF2深色模式#3A3A5A深色网格不适合用纯白否则会抢过坐标轴和曲线也不能太接近黑色否则看不见。#3A3A5A是一种低亮度蓝紫灰适合作为背景辅助线。五、坐标轴颜色比网格更突出坐标轴使用更高对比度ctx.strokeStyle this.getColor(#333333,#EEEEEE)ctx.lineWidth1.5浅色模式下用深灰#333333深色模式下用浅灰#EEEEEE。坐标轴比网格更粗ctx.lineWidth 1.5这让用户能清楚区分网格辅助坐标轴主结构曲线学习对象。六、箭头绘制坐标方向要明确x 轴箭头ctx.beginPath()ctx.moveTo(w- 8,clampedY- 4)ctx.lineTo(w,clampedY)ctx.lineTo(w- 8,clampedY 4)ctx.stroke()y 轴箭头ctx.beginPath()ctx.moveTo(clampedX- 4, 8)ctx.lineTo(clampedX, 0)ctx.lineTo(clampedX 4, 8)ctx.stroke()箭头和坐标轴使用同一颜色能保证深色模式下方向标识仍然清楚。七、刻度标签文本颜色也要切换x 轴刻度标签ctx.fillStyle this.getColor(#555555,#CCCCCC) ctx.font10px sans-serifctx.textAligncenterctx.fillText(this.formatAxisLabel(xi), pt.x, clampedY 14)y 轴刻度标签ctx.fillStyle this.getColor(#555555,#CCCCCC) ctx.font10px sans-serifctx.textAlignrightctx.fillText(this.formatAxisLabel(yi), clampedX -6, pt.y3)深色模式下标签使用#CCCCCC比坐标轴稍弱但比网格清楚。这符合视觉层级曲线/模型 坐标轴 标签 网格 背景八、原点标识O 也不能漏原点标识if(this.showLabels) { ctx.fillStyle this.getColor(#333333,#EEEEEE) ctx.font11px sans-serifctx.textAlignleftctx.fillText(O, origin.x4, origin.y-5) }原点使用和坐标轴接近的颜色因为它属于坐标结构的一部分。九、标签格式化避免长数字破坏画面坐标标签通过formatAxisLabel()处理formatAxisLabel(val:number):string{if(Number.isInteger(val)) returnval.toString()if(Math.abs(val) 0.01||Math.abs(val) 9999) { returnval.toExponential(1)} return parseFloat(val.toPrecision(4)).toString()}这个函数避免出现很长的小数或大数字保持坐标轴整洁。数学画板的可读性不仅靠颜色也靠数字显示克制。十、深色图标背景非 Canvas 区域也要统一画板页还提供了图标背景映射getIconBg(bgColor:string):string{if(!this.isDarkMode)returnbgColorconstcolorMap: Recordstring,string {#FFFFFF:#1C1C1E,#F5F5F5:#2C2C2E,#FFF8F0:#000000,#EBF5FF:#1A2744,#FFE8F0:#3D1A28, }returncolorMap[bgColor] || bgColor }Canvas 本身要处理坐标绘制Canvas 外部的按钮、工具栏、参数面板也要保持主题一致。十一、为什么这属于“沉浸光感”沉浸光感不只是大面积渐变。对数学画板来说沉浸感来自背景不刺眼网格不抢戏坐标轴清晰标签可读曲线颜色突出工具栏和画布主题一致。如果深色模式只是简单反色数学图像会变得很难看。手动绘制层级才是关键。十二、总结这篇文章专门讲 Canvas 数学画板的深色可读性和之前“参数化曲线绘制”文章区分开。核心实现包括 用ThemeManager获取深色状态 用getColor()切换 Canvas 内部绘制颜色 先清屏再填充深浅色背景 网格使用低对比辅助色 坐标轴使用高对比主结构色 刻度标签使用中等对比文本色 原点和箭头保持坐标方向清晰✂️ 用formatAxisLabel()控制数字长度。Canvas 深色模式不是换背景这么简单。尤其是数学画板所有线条和文字都要重新考虑层级这样才能真正做到“看得清、用得久、不累眼”。