【OpenHarmony/HarmonyOs 】深色模式下的 Canvas 数学画板:坐标轴、网格、标签与曲线可读性优化
【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.lineWidth = 0.5
}
浅色模式:
#E8EDF2
深色模式:

#3A3A5A
深色网格不适合用纯白,否则会抢过坐标轴和曲线;也不能太接近黑色,否则看不见。#3A3A5A 是一种低亮度蓝紫灰,适合作为背景辅助线。
五、坐标轴颜色:比网格更突出
坐标轴使用更高对比度:
ctx.strokeStyle = this.getColor('#333333', '#EEEEEE')
ctx.lineWidth = 1.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.font = '10px sans-serif'
ctx.textAlign = 'center'
ctx.fillText(this.formatAxisLabel(xi), pt.x, clampedY + 14)
y 轴刻度标签:
ctx.fillStyle = this.getColor('#555555', '#CCCCCC')
ctx.font = '10px sans-serif'
ctx.textAlign = 'right'
ctx.fillText(this.formatAxisLabel(yi), clampedX - 6, pt.y + 3)
深色模式下标签使用 #CCCCCC,比坐标轴稍弱,但比网格清楚。
这符合视觉层级:
曲线/模型 > 坐标轴 > 标签 > 网格 > 背景
八、原点标识:O 也不能漏
原点标识:
if (this.showLabels) {
ctx.fillStyle = this.getColor('#333333', '#EEEEEE')
ctx.font = '11px sans-serif'
ctx.textAlign = 'left'
ctx.fillText('O', origin.x + 4, origin.y - 5)
}
原点使用和坐标轴接近的颜色,因为它属于坐标结构的一部分。
九、标签格式化:避免长数字破坏画面
坐标标签通过 formatAxisLabel() 处理:
formatAxisLabel(val: number): string {
if (Number.isInteger(val)) return val.toString()
if (Math.abs(val) < 0.01 || Math.abs(val) > 9999) {
return val.toExponential(1)
}
return parseFloat(val.toPrecision(4)).toString()
}
这个函数避免出现很长的小数或大数字,保持坐标轴整洁。
数学画板的可读性不仅靠颜色,也靠数字显示克制。
十、深色图标背景:非 Canvas 区域也要统一
画板页还提供了图标背景映射:
getIconBg(bgColor: string): string {
if (!this.isDarkMode) return bgColor
const colorMap: Record<string, string> = {
'#FFFFFF': '#1C1C1E',
'#F5F5F5': '#2C2C2E',
'#FFF8F0': '#000000',
'#EBF5FF': '#1A2744',
'#FFE8F0': '#3D1A28',
}
return colorMap[bgColor] || bgColor
}
Canvas 本身要处理坐标绘制,Canvas 外部的按钮、工具栏、参数面板也要保持主题一致。
十一、为什么这属于“沉浸光感”?
沉浸光感不只是大面积渐变。对数学画板来说,沉浸感来自:
- 背景不刺眼;
- 网格不抢戏;
- 坐标轴清晰;
- 标签可读;
- 曲线颜色突出;
- 工具栏和画布主题一致。
如果深色模式只是简单反色,数学图像会变得很难看。手动绘制层级才是关键。
十二、总结
这篇文章专门讲 Canvas 数学画板的深色可读性,和之前“参数化曲线绘制”文章区分开。
核心实现包括:
- 🌙 用
ThemeManager获取深色状态; - 🎨 用
getColor()切换 Canvas 内部绘制颜色; - 🖼 先清屏再填充深浅色背景;
- 🧱 网格使用低对比辅助色;
- 📏 坐标轴使用高对比主结构色;
- 🔢 刻度标签使用中等对比文本色;
- 🧭 原点和箭头保持坐标方向清晰;
- ✂️ 用
formatAxisLabel()控制数字长度。
Canvas 深色模式不是换背景这么简单。尤其是数学画板,所有线条和文字都要重新考虑层级,这样才能真正做到“看得清、用得久、不累眼”。🚀

更多推荐


所有评论(0)