基于Angular+PIXI.js+NgRx的绘图软件避免每帧重建场景的架构咨询
解耦绘制逻辑的方案
可以通过工厂模式 + Angular 依赖注入把不同节点的渲染逻辑从画布组件中完全剥离:
- 首先定义通用的渲染器接口,规范所有节点渲染类的行为:
// 渲染上下文,把通用的frame、offset、matrix等参数封装成统一对象传递 export interface RenderContext { frame?: FrameState; frames: FrameState[]; offsetX: number; offsetY: number; matrix?: PIXI.Matrix; interactive: boolean; } export interface INodeRenderer { // 新建DisplayObject create(node: NodeState, ctx: RenderContext): PIXI.DisplayObject; // 更新已有DisplayObject的属性 update(target: PIXI.DisplayObject, node: NodeState, ctx: RenderContext): void; }
- 为每种节点类型实现独立的可注入渲染服务,比如
LineRendererService、RectRendererService、TextRendererService等,各自实现上面的接口,把你原来在getRenderedNode里的各个分支逻辑拆分到对应服务里。 - 实现
NodeRendererFactory服务,内部维护节点类型和对应渲染器的映射,根据传入的节点类型返回对应渲染器实例:
@Injectable({ providedIn: 'root' }) export class NodeRendererFactory { constructor( private lineRenderer: LineRendererService, private rectRenderer: RectRendererService, // 其他渲染器注入 ) {} getRenderer(type: string): INodeRenderer { const renderMap = { line: this.lineRenderer, rect: this.rectRenderer, // 其他类型映射 }; return renderMap[type] || this.defaultRenderer; } }
- 把节点的事件绑定逻辑也抽成独立的
NodeInteractionService,统一处理所有节点的事件绑定、解绑逻辑,不用在渲染器里重复写。
这样改造之后,画布组件完全不需要感知具体节点的渲染逻辑,后续新增节点类型只需要新增对应的渲染器实现即可,符合开闭原则,也解决了逻辑耦合的问题。
增量更新避免全量销毁重建的方案
核心思路是维护节点唯一ID与DisplayObject的映射缓存,基于状态差量更新:
- 在画布组件内新增缓存映射表,存储已创建的DisplayObject:
private nodeCache = new Map<string, PIXI.DisplayObject>();
- 重写原来的
update方法,删除上来就清空stage的逻辑,改为三步处理:- 新增节点处理:遍历最新的frames节点列表,用节点ID查缓存:
- 缓存不存在:调用对应渲染器的
create方法生成DisplayObject,加入对应父容器,存入缓存,绑定交互事件 - 缓存已存在:调用对应渲染器的
update方法,传入现有对象和最新的节点状态,直接修改位置、颜色、文本、尺寸等属性
- 缓存不存在:调用对应渲染器的
- 删除节点处理:遍历缓存表中的所有ID,检查是否存在于最新的frames节点列表中,不存在的就从父容器移除、销毁对象,同时从缓存中删除
- 层级调整处理:如果节点的父级/排序发生变化,直接调整DisplayObject在容器中的位置即可
- 新增节点处理:遍历最新的frames节点列表,用节点ID查缓存:
- 还可以进一步优化NgRx的订阅逻辑,把大的
frames$selector拆分为更细粒度的节点属性selector,只有节点的对应属性发生变化时才触发更新,进一步减少不必要的重绘。
这种方案下,只有发生变化的节点才会被修改,完全不需要每次清空全量重建,渲染性能会有非常明显的提升。
内容的提问来源于stack exchange,提问作者frank_lbt
相关产品推荐
相关产品推荐

