核心是将图像中重复的像素级元素抽离为不可变内部状态统一缓存,位置缩放等动态信息作为外部状态运行时注入;es6类封装享元角色,weakmap防泄漏,rectregion聚合同色区域,object.freeze保障不可变性。

核心思路是:把图像中大量重复的像素级图形元素(如相同颜色的图块、滤镜参数组合、图层样式模板)抽离为不可变的内部状态,统一缓存复用;而位置、缩放、透明度等动态信息作为外部状态,在运行时注入。ES6类提供清晰的结构封装,享元模式负责对象瘦身。
拆分内部状态与外部状态
在图片编辑器中,并非每个像素或图层都需要独立对象。例如:
- 同一张滤镜预设(如“暖色调V2”)被上千个图层引用,只需一个共享实例
- 100×100像素的纯色填充块,只要颜色值相同,无论出现在画布哪个位置,都可共用同一个享元
- SVG路径指令字符串(如 d="M10 10 L50 50")是只读的,可作内部状态;而它的 transform 属性(如 scale(1.2) translate(20,30))必须外部传入
用ES6类实现享元角色
借助 class 语法明确职责边界,避免原型污染和状态混淆:
- Flyweight(抽象享元):定义 render(context, x, y, scale) 接口,不保存坐标或尺寸
- ConcreteFlyweight(具体享元):用 readonly 字段存颜色、路径数据、滤镜配置等;构造函数禁止接收坐标类参数
- FlyweightFactory(工厂类):用 Map 按唯一键(如 `${color}-${filterId}`)缓存实例;支持 WeakMap 防止内存泄漏
针对亿级像素做轻量级对象池
不直接创建像素对象,而是按“逻辑单元”聚合享元:
- 将连续同色区域压缩为 RectRegion 对象,仅存 x, y, width, height, fillStyle 四个字段,fillStyle 指向共享的 ColorFlyweight
- 对WebGL纹理上传,复用同一份 Uint8Array 缓冲区,不同图层通过 offset 和 stride 访问子区域
- 使用 Object.freeze() 冻结所有享元内部状态,确保不可变性,避免意外修改导致共享污染
配合浏览器机制做内存兜底
享元不是万能的,需与平台能力协同:
- 对已脱离视口的图层,调用 factory.release(key) 主动从池中移除,触发 WeakRef 清理
- 监听 resize 或 visibilitychange 事件,批量回收临时享元(如缩略图生成器用的过渡样式)
- 利用 OffscreenCanvas 将高频重绘逻辑移出主线程,享元对象只负责描述,不参与绘制执行











