24 lines
1.8 KiB
TypeScript
24 lines
1.8 KiB
TypeScript
/**
|
|
* 交互会话通知的显示帧合并。每个订阅拥有一条 frame queue,读快照不等帧。
|
|
* 这里只发现视图和手势的显示失效;选择、工具及预览事实继续保存在原 session。
|
|
*/
|
|
import type {SessionSnapshot} from '../canvas-core/gestures.ts';
|
|
import type {ExternalSource} from './selectors.ts';
|
|
import {createFrameQueue,type FrameClock} from './frame-queue.ts';
|
|
/** One subscription owns one disposable frame queue. Reads always see current sessions. */
|
|
/** 对比前后会话引用生成 view/element 脏 key;退订时同步撤销帧队列。 */
|
|
export function createSessionFrameSource(source:ExternalSource<SessionSnapshot>,clock:FrameClock):ExternalSource<SessionSnapshot>{
|
|
return Object.freeze({getSnapshot:source.getSnapshot,subscribe(notify:()=>void){
|
|
let previous=source.getSnapshot();const queue=createFrameQueue<string>(clock,()=>notify());
|
|
// 两次遍历覆盖视图新增/删除;手势结束要同时刷新旧目标,才能去掉它们的预览。
|
|
const stop=source.subscribe(()=>{const next=source.getSnapshot(),dirty=new Set<string>();
|
|
for(const [id,view]of previous.views)if(next.views.get(id)!==view)dirty.add(`view:${id}`);
|
|
for(const [id,view]of next.views)if(previous.views.get(id)!==view)dirty.add(`view:${id}`);
|
|
if(previous.gesture!==next.gesture)for(const state of [previous.gesture,next.gesture])for(const target of state?.targets??[])dirty.add(`element:${target.id}`);
|
|
// 连线手势与普通几何手势分别跟踪,改变或取消时都让旧/新目标失效。
|
|
if(previous.connectionGesture!==next.connectionGesture)for(const state of [previous.connectionGesture,next.connectionGesture])if(state)dirty.add(`element:${state.target.id}`);
|
|
previous=next;queue.invalidate(dirty);
|
|
});return()=>{stop();queue.dispose();};
|
|
}});
|
|
}
|