devexpress-diagram
Version:
DevExpress Diagram Control
90 lines (79 loc) • 3.4 kB
text/typescript
import { SvgPrimitive } from "./Primitives/Primitive";
import { ITextMeasurer } from "./Measurer/ITextMeasurer";
import { Diagnostics } from "../Diagnostics";
const RAF_CHANGES_LIMIT = 2000;
export interface IDOMManipulator {
createElement<TEl extends SVGElement, TPrim extends SvgPrimitive<TEl>>(primitive: TPrim, parent: SVGElement, sibling?: SVGElement): TEl;
changeChildrenByPrimitives(primitives: SvgPrimitive<SVGElement>[], parent: SVGElement);
changeByFunc<T>(element: T, func: (el: T) => void);
changeByPrimitive(element: SVGElement, primitive: SvgPrimitive<SVGElement>);
cancelAnimation();
}
export class DOMManipulator implements IDOMManipulator {
private queue: QueueItem[] = [];
private rafRequested: boolean;
private rafId: number;
constructor(public measurer: ITextMeasurer) { }
createElement<TEl extends SVGElement, TPrim extends SvgPrimitive<TEl>>(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<SVGElement>[], parent: SVGElement) {
primitives.forEach((primitive, index) => {
const element = <SVGElement>parent.childNodes[index];
this.changeByPrimitive(element, primitive);
});
}
changeByFunc<T>(element: T, func: (el: T) => void) {
this.doChange(element, func);
}
changeByPrimitive(element: SVGElement, primitive: SvgPrimitive<SVGElement>) {
this.doChange(element, primitive);
}
cancelAnimation() {
if(this.rafId !== undefined) {
cancelAnimationFrame(this.rafId);
this.queue = [];
}
}
protected doChange<T>(element: T, action: SvgPrimitive<SVGElement> | ((el: T) => void)) {
if(Diagnostics.optimizeUsingRAF) {
this.queue.push([element, action]);
this.requestAnimation();
}
else
this.doChangeSync(element, action);
}
protected doChangeSync<T>(element: T, action: SvgPrimitive<SVGElement> | ((el: T) => void)) {
if(typeof action === "function")
action(element);
else
action.applyElementProperties(<SVGElement><any>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<SVGElement> | ((el: any) => void)];
export class ExportDOMManipulator extends DOMManipulator {
protected doChange<T>(element: T, action: SvgPrimitive<SVGElement> | ((el: T) => void)) {
this.doChangeSync(element, action);
}
}