Paint Worklet 必须通过 CSS.paintWorklet.addModule() 加载脚本才能生效,registerPaint() 单独执行会被忽略;需在 CSS 应用前完成异步加载,并在 inputProperties 中声明监听的自定义属性以响应变化。
registerPaint 就生效浏览器会直接忽略未启用 paintWorklet 的注册调用,哪怕代码没报错。根本原因是它属于「模块化能力」,必须显式加载到 document.paintWorklet 才能被 CSS 引用。常见错误是把 registerPaint 放在普通 JS 脚本里执行,但没走 addModule 流程——这时候 CSS 里的 paint(myPattern) 会被静默降级为透明背景。
正确做法是:
pattern-worklet.js 文件,只写 registerPaint 和类定义,不引入任何外部依赖(ESM import 不被支持)document.paintWorklet.addModule('pattern-worklet.js') 加载,且必须在设置 CSS 前完成addModule().then(...) 里操作 style 或 classpaint() 函数响应元素尺寸与自定义属性变化CSS Paint API 不自动监听元素宽高或自定义属性变动,paint(ctx, geom, props) 中的 geom.width/height 是当前绘制区域尺寸,但若容器 resize 后未触发重绘,就会卡在旧尺寸。同时,props.get('--my-color') 只读取初始值,后续 CSS 变量更新不会自动触发重绘。
解决方式很务实:
立即学习“Java免费学习笔记(深入)”;
inputProperties 静态字段中显式声明要监听的 CSS 变量,例如 static get inputProperties() { return ['--cell-size', '--fg']; }
ctx.canvas.width/height 而非缓存的 geom 值,因为 canvas 会随 geom 自动重置尺寸,但需注意:每次 paint() 调用都是全新 canvas,无需手动 clearpaint() 中做复杂计算或创建对象,高频调用下 GC 压力大;纹理逻辑尽量用整数运算 + 位操作,比如 ((x ^ y) & 1) * 255 生成棋盘格ctx 绘图上下文和 Canvas2D 的关键差异Paint Worklet 的 ctx 并非完整 Canvas2D 实例:它删减了大量方法(如 getImageData、createPattern、setTransform),也不支持 canvas.toDataURL()。最易踩坑的是 ctx.fillStyle 接收 CSS 颜色字符串的能力有限——不支持 hsl()、color(display-p3 ...),甚至部分十六进制简写(如 #abc)可能解析失败。
安全做法是:
props.get('--color').toString() 获取变量后,优先转成 rgb(r,g,b) 格式再赋给 fillStyle
ctx.font 和文本测量,Paint Worklet 对字体回退、子像素渲染等支持不稳定ctx.imageSmoothingEnabled(无效),改用双线性采样逻辑在 JS 中预计算像素值ctx.beginPath() + lineTo() + fill() 是最稳路径,clip() 可用但慎嵌套Paint Worklet 错误不会出现在主页面 console,而是藏在 DevTools 的「Rendering」面板 → 勾选「Paint flashing」后看闪烁异常,或更直接:打开 chrome://inspect → 点击「Configure...」→ 添加 localhost 端口 → 在「Remote Target」里找到 worklet 类型页签。这里能看到 registerPaint 语法错误、paint() 中的 throw、甚至内存超限的 RangeError。
几个硬核提示:
console.log 在 worklet 中有效,但输出在 worklet 专用控制台,不在页面 consolepaint() 执行超过 16ms,Chrome 会强制中断并显示「Synchronous paint took too long」警告,此时得拆分逻辑或用 requestIdleCallback(但注意:worklet 环境无该 API,只能精简算法)真正难的不是写出第一个 paint(),而是让纹理在 60fps 下稳定复用——每个像素的计算都得落在 0.2ms 内,否则滚动时就掉帧。这要求你放弃通用抽象,直奔位运算和查表法。