@antv/layout
Version:
graph layout algorithm
95 lines (76 loc) • 2.04 kB
text/typescript
import EventEmitter from '@antv/event-emitter';
import { SimulationOptions } from './types';
export type { SimulationOptions };
export abstract class BaseSimulation<
T extends SimulationOptions = SimulationOptions,
> extends EventEmitter {
protected iteration = 0;
protected judgingDistance = Infinity;
protected running = false;
protected options!: T;
private tickCallback: (() => void) | null = null;
private endCallback: (() => void) | null = null;
private timer: number = 0;
initialize(options: T) {
this.options = options;
this.iteration = 0;
this.judgingDistance = Infinity;
}
on(event: 'tick' | 'end', cb: () => void) {
if (event === 'tick') this.tickCallback = cb;
if (event === 'end') this.endCallback = cb;
return this;
}
tick(iterations = 1) {
for (let i = 0; i < iterations; i++) {
const distance = this.runOneStep();
this.judgingDistance = distance;
this.iteration++;
this.tickCallback?.();
}
return this;
}
restart() {
if (this.running) return this;
const {
maxIteration = 500,
minMovement = 0,
animate = true,
} = this.options;
/** 非动画 or 非浏览器环境 */
if (!animate || typeof window === 'undefined') {
while (
this.iteration < maxIteration &&
(this.judgingDistance > minMovement || this.iteration < 1)
) {
this.tick(1);
}
Promise.resolve().then(() => {
this.endCallback?.();
});
return this;
}
/** 动画模式 */
this.running = true;
this.timer = window.setInterval(() => {
this.tick(1);
if (
this.iteration >= maxIteration ||
this.judgingDistance < minMovement
) {
this.stop();
this.endCallback?.();
}
}, 0);
return this;
}
stop() {
this.running = false;
if (this.timer) {
clearInterval(this.timer);
this.timer = 0;
}
return this;
}
protected abstract runOneStep(): number;
}