56 lines
4.7 KiB
TypeScript
56 lines
4.7 KiB
TypeScript
/**
|
|
* React Flow 输入对象的结构共享缓存,与计算场景的 scene-projection 分工不同。
|
|
* 按元素代次与 owner 判定同一生命周期,复用值相等的 data/style/position 和整个数组,减少显示刷新。
|
|
* 缓存属于一个 owner/project/视图 canvas,既不是业务文档,也不是跨项目身份仓库。
|
|
*/
|
|
import type {Document,Id} from '../canvas-core/types.ts';
|
|
import {equal,fault} from '../canvas-core/values.ts';
|
|
import type {projectCanvas} from './projection.ts';
|
|
|
|
/** 只处理已经生成的投影,不在这里解释元素组件或计算坐标。 */
|
|
type Projection=ReturnType<typeof projectCanvas>;
|
|
/** 原对象的生命周期证据;同 ID 的重建或移动 owner 后不能直接继承旧显示对象。 */
|
|
type Retained<T>={incarnation:number;ownerCanvasId:Id;value:T};
|
|
/** View-owned rendering cache, never a document store. Keep unchanged React Flow input identities. */
|
|
/** 创建视图专属结构共享器;reuse 会就地替换传入投影数组中的等价条目,调用方应提供派生数组。 */
|
|
export function createProjectionCache(){
|
|
let scope:object|undefined,projectId:Id|undefined,canvasId:Id|undefined;
|
|
let nodeArray:Projection['nodes']|undefined,edgeArray:Projection['edges']|undefined;
|
|
/** 数组长度与逐项引用完全一致时沿用旧数组,顺序改变也会产生新数组身份。 */
|
|
const stable=<T>(previous:T[]|undefined,next:T[])=>previous?.length===next.length&&next.every((value,index)=>value===previous[index])?previous:next;
|
|
let nodes=new Map<Id,Retained<Projection['nodes'][number]>>(),edges=new Map<Id,Retained<Projection['edges'][number]>>();
|
|
/** 对每个显示 ID 核验模型身份,再逐层共享等价字段;返回仅包含本帧元素的新缓存表。 */
|
|
function retain<T extends{id:Id}>(document:Document,values:T[],previous:Map<Id,Retained<T>>){
|
|
const next=new Map<Id,Retained<T>>();
|
|
for(let index=0;index<values.length;index++){
|
|
const value=values[index],record=document.elements.get(value.id);if(!record)throw fault('projection_identity_missing',[value.id]);
|
|
const old=previous.get(value.id),sameLifetime=old&&old.incarnation===record.incarnation&&old.ownerCanvasId===record.ownerCanvasId;
|
|
if(sameLifetime&&old.value===value){next.set(value.id,old);continue;}
|
|
let shared=value;
|
|
// 常见嵌套字段独立共享,使其它显示属性变化时也能保留未变化的 data/style/position。
|
|
if(sameLifetime)for(const key of ['data','style','position'] as const){const incoming=(shared as Record<string,unknown>)[key],before=(old.value as Record<string,unknown>)[key];if(Object.hasOwn(shared,key)&&Object.hasOwn(old.value,key)&&!Object.is(incoming,before)&&equal(incoming,before))shared={...shared,[key]:before};}
|
|
const retained=sameLifetime&&equal(old.value,shared)?old.value:shared;
|
|
values[index]=retained;next.set(value.id,{incarnation:record.incarnation,ownerCanvasId:record.ownerCanvasId,value:retained});
|
|
}
|
|
return next;
|
|
}
|
|
return Object.freeze({
|
|
/** 范围改变先整体清空;保存规范化后的对象表及稳定数组,返回包装后的投影。 */
|
|
reuse(owner:object,document:Document,viewCanvasId:Id,projection:Projection):Projection{
|
|
if(scope!==owner||projectId!==document.projectId||canvasId!==viewCanvasId){nodes.clear();edges.clear();nodeArray=undefined;edgeArray=undefined;scope=owner;projectId=document.projectId;canvasId=viewCanvasId;}
|
|
const nextNodes=retain(document,projection.nodes,nodes),nextEdges=retain(document,projection.edges,edges);
|
|
nodes=nextNodes;edges=nextEdges;nodeArray=stable(nodeArray,projection.nodes);edgeArray=stable(edgeArray,projection.edges);
|
|
return{...projection,nodes:nodeArray,edges:edgeArray};
|
|
},
|
|
/** 读取本轮保留的节点显示对象,供 renderer 测量等显示流程使用。 */
|
|
getNode(id:Id){return nodes.get(id)?.value;},
|
|
/** 只有上下文、生命周期、renderer type、尺寸和 data 均兼容才可沿用旧测量,不能只比较 ID。 */
|
|
canRetainMeasurement(owner:object,document:Document,viewCanvasId:Id,value:Projection['nodes'][number]){
|
|
const old=nodes.get(value.id),record=document.elements.get(value.id);
|
|
return scope===owner&&projectId===document.projectId&&canvasId===viewCanvasId&&!!old&&!!record&&old.incarnation===record.incarnation&&old.ownerCanvasId===record.ownerCanvasId&&old.value.type===value.type&&old.value.width===value.width&&old.value.height===value.height&&equal(old.value.data,value.data);
|
|
},
|
|
/** 清空对象/数组和范围身份,调用方无需也不应据此清除业务文档。 */
|
|
clear(){nodes.clear();edges.clear();nodeArray=undefined;edgeArray=undefined;scope=undefined;projectId=undefined;canvasId=undefined;},
|
|
});
|
|
}
|