import { SvgPrimitive } from "./Primitives/Primitive"; import { ITextMeasurer } from "./Measurer/ITextMeasurer"; import { Diagnostics } from "../Diagnostics"; const RAF_CHANGES_LIMIT = 2000; export interface IDOMManipulator { createElement>(primitive: TPrim, parent: SVGElement, sibling?: SVGElement): TEl; changeChildrenByPrimitives(primitives: SvgPrimitive[], parent: SVGElement); changeByFunc(element: T, func: (el: T) => void); changeByPrimitive(element: SVGElement, primitive: SvgPrimitive); cancelAnimation(); } export class DOMManipulator implements IDOMManipulator { private queue: QueueItem[] = []; private rafRequested: boolean; private rafId: number; constructor(public measurer: ITextMeasurer) { } createElement>(primitive: TPrim, parent: SVGElement, sibling?: SVGElement): TEl { return primitive.createElement(el => { if(parent != null) if(sibling !== undefined) parent.insertBefore(el, sibling); else parent.appendChild(el); }); } changeChildrenByPrimitives(primitives: SvgPrimitive[], parent: SVGElement) { primitives.forEach((primitive, index) => { const element = parent.childNodes[index]; this.changeByPrimitive(element, primitive); }); } changeByFunc(element: T, func: (el: T) => void) { this.doChange(element, func); } changeByPrimitive(element: SVGElement, primitive: SvgPrimitive) { this.doChange(element, primitive); } cancelAnimation() { if(this.rafId !== undefined) { cancelAnimationFrame(this.rafId); this.queue = []; } } protected doChange(element: T, action: SvgPrimitive | ((el: T) => void)) { if(Diagnostics.optimizeUsingRAF) { this.queue.push([element, action]); this.requestAnimation(); } else this.doChangeSync(element, action); } protected doChangeSync(element: T, action: SvgPrimitive | ((el: T) => void)) { if(typeof action === "function") action(element); else action.applyElementProperties(element, this.measurer); } private requestAnimation() { if(!this.rafRequested) { this.rafRequested = true; const func = () => { this.queue.splice(0, RAF_CHANGES_LIMIT).forEach(t => this.doChangeSync(t[0], t[1])); if(this.queue.length) this.rafId = requestAnimationFrame(func); else { this.rafRequested = false; this.rafId = undefined; } }; this.rafId = requestAnimationFrame(func); } } } type QueueItem = [any, SvgPrimitive | ((el: any) => void)]; export class ExportDOMManipulator extends DOMManipulator { protected doChange(element: T, action: SvgPrimitive | ((el: T) => void)) { this.doChangeSync(element, action); } }