必须用 change 事件更新参数并驱动重绘,而非依赖 matchMedia 自动刷新;监听主题变化缓存 isDark 状态,解耦滤镜逻辑为参数驱动,在 requestAnimationFrame 中安全重绘,卸载时显式移除监听。

不能靠 window.matchMedia 自动更新滤镜状态——它只发通知,不执行重绘。真正生效的关键,是把 change 事件当作“刷新指令”,在回调里更新控制参数,并驱动 Canvas 下一帧用新参数重算像素。
监听主题变化并同步状态变量
创建 MediaQueryList 实例后,立即读取当前匹配状态,缓存为布尔值(如 isDark):
- const mql = window.matchMedia('(prefers-color-scheme: dark)')
- let isDark = mql.matches
- 在 change 回调中更新 isDark,并触发重绘调度(例如 requestAnimationFrame(draw))
- 务必保存 handler 引用,以便后续移除:mql.removeEventListener('change', handler)
滤镜逻辑解耦:参数驱动,非状态轮询
把 isDark 当作配置项传入图像处理函数,而不是每帧都查 matchMedia:
- processPixel(r, g, b, isDark) 内部按需分支:深色模式下调低亮度增益、提升对比度阈值
- 避免在 draw 循环里重复调用 mql.matches —— 首次初始化和 change 时才更新参数
- 若使用 getImageData/putImageData,确保首次绘制就应用正确参数,防止首屏跳变
异步切换场景下的安全重绘策略
当主题变更与 Canvas 渲染不同步(如动画中、资源加载未完成),需避免阻塞或重复计算:
- change 回调中不做 getImageData 等高开销操作,只更新 isDark 和标记“需重绘”
- 在 requestAnimationFrame 的 draw 函数开头检查标记,再执行像素处理
- 若 Canvas 正在播放视频流,可在 video.onloadeddata 后同步一次主题,再开始 requestAnimationFrame 循环
卸载前清理监听,防止内存泄漏
尤其在单页应用或组件级封装中,必须显式移除事件监听:
- 在组件销毁、页面离开或脚本清理阶段调用 mql.removeEventListener('change', handler)
- 不要依赖 onbeforeunload —— 它不保证执行,且现代浏览器已限制其用途
- 推荐将监听逻辑封装为可销毁的 hook 或类实例,统一管理生命周期

















