canvas不解析css变量,ctx.fillstyle="var(--primary-color)"会静默回退为黑色;必须用getcomputedstyle(document.documentelement).getpropertyvalue('--primary-color').trim()获取计算值并缓存复用,否则主题切换后颜色不会更新。

ctx.fillStyle = "var(--primary)" 会静默变成黑色,不是 bug,是 Canvas 2D 上下文根本不解析 CSS 语法。必须用 JavaScript 桥接,把变量“算出来”再喂给 Canvas。
为什么 getComputedStyle 是唯一可靠入口
Canvas 的 fillStyle、strokeStyle 只认最终计算后的颜色字符串:"#3b82f6"、"rgb(59, 130, 246)"、"LightGreen"(注意大小写)。它不访问样式表,也不执行 var() 或 hsl() 函数。
所以:
• element.style.setProperty("--color", "#ff0000") 不行——那是内联样式,没参与 CSSOM 计算
• document.querySelector(":root").style.--color 语法错误,根本不能写
• 未挂载的元素或脚本执行太早时调用 getComputedStyle,返回空字符串
如何安全读取并赋值给 ctx.fillStyle
核心是三件事:选对元素、用对方法、清理字符串。
- 读全局变量(如
:root定义的--accent):传document.documentElement - 读组件级变量(如
<div id="chart" style="--bg: #1e293b">):传 <code>document.getElementById("chart"),不能只读:root - 必须用
.getPropertyValue('--accent'),不是.style.--accent(语法错),也不是.cssText(不包含变量值) - 返回值总是字符串,但可能带前导/尾随空格、换行,务必加
.trim();否则 Safari 旧版直接忽略,ctx.fillStyle失效变黑 - 变量未定义或语法错误时返回
""(空字符串),不是undefined,别用== null判断,建议兜底:const color = getComputedStyle(root).getPropertyValue('--primary').trim() || '#000' - ✅ 合法:
"#3b82f6"、"rgb(59, 130, 246)"、"rgba(255, 128, 0, 0.7)"、"LightGreen"(W3C 标准名,大小写敏感) - ❌ 静默失败:
"var(--primary)"、"hsl(210, 75%, 50%)"、"rgba(255 128 0 / 0.7)"(空格分隔新语法)、"dark blue"(含空格的颜色名)、"--primary-color" - ⚠️ 注意:
alpha值范围是 0–1,不是百分比;写"rgba(0,0,0,50%)"直接失效 - 监听系统主题:
matchMedia('(prefers-color-scheme: dark)').addEventListener('change', redraw) - 监听自定义事件(如
document.dispatchEvent(new Event('themechange'))),并在 handler 里清空画布 + 重绘 - 避免在
requestAnimationFrame循环里反复调用getComputedStyle,强制同步布局,严重拖慢帧率;应在初始化或主题变更时缓存颜色值,后续直接复用 - 别忘了
ctx.clearRect(0, 0, canvas.width, canvas.height)—— 很多人只改变量,没清画布,结果新颜色压根没刷上去
哪些颜色格式 Canvas 能直接用,哪些会静默失败
Canvas 支持有限,且不报错,只静默 fallback 到黑色或透明:
变量变更后 Canvas 不会自动重绘
深色模式切换、运行时改 document.documentElement.style.setProperty("--primary", "#1e40af"),Canvas 图形颜色不会变——它不知道你动了 CSS。
.trim(),结果图形全黑还找不到原因。











