react-diagrams
react-diagrams copied to clipboard
Repaint throttling rather than debouncing
I have a huge graph with hundreds of nodes and links, which trigger a LOT of repainting. since I only need one repainting no matter which node triggers it, I need a throttle on the repaint method.
There already is debouncing, but if I call repaint 1000 times in a second, the debouncing will still call it 1000 times but in gaps of x ms, while the throttle will throw away many repaints. Let's say that I drag a node for 1 second and want a repaint once every 10ms, but call repaint 1000 times in that second. I only want 100 repaints.
So I changed the code of CanvasEngine to this and it worked! Any chance the contributors would add throttle in addition to debounce?
The following is the complied code I changed (changes occured in CanvasEngine.js only in constructor and repaintCanvas):
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.CanvasEngine = void 0;
const lodash_1 = require("lodash");
const FactoryBank_1 = require("./core/FactoryBank");
const BaseObserver_1 = require("./core/BaseObserver");
const geometry_1 = require("@projectstorm/geometry");
const ActionEventBus_1 = require("./core-actions/ActionEventBus");
const ZoomCanvasAction_1 = require("./actions/ZoomCanvasAction");
const DeleteItemsAction_1 = require("./actions/DeleteItemsAction");
const StateMachine_1 = require("./core-state/StateMachine");
class CanvasEngine extends BaseObserver_1.BaseObserver {
constructor(options = {}) {
super();
this.model = null;
this.eventBus = new ActionEventBus_1.ActionEventBus(this);
this.stateMachine = new StateMachine_1.StateMachine(this);
this.layerFactories = new FactoryBank_1.FactoryBank();
this.registerFactoryBank(this.layerFactories);
/**
* Overrides the standard options with the possible given options
*/
this.options = Object.assign({ registerDefaultDeleteItemsAction: true, registerDefaultZoomCanvasAction: true, repaintDebounceMs: 0 }, options);
if (this.options.registerDefaultZoomCanvasAction === true) {
this.eventBus.registerAction(new ZoomCanvasAction_1.ZoomCanvasAction());
}
if (this.options.registerDefaultDeleteItemsAction === true) {
this.eventBus.registerAction(new DeleteItemsAction_1.DeleteItemsAction());
}
/**
* The actual repaint function
*/
const repaint = () => {
console.log("Repainting!");
this.iterateListeners((listener) => {
if (listener.repaintCanvas) {
listener.repaintCanvas();
}
});
};
// if the `repaintDebounceMs` option is > 0, then apply the debounce
this.repaintFn = repaint;
if (options.repaintDebounceMs > 0) {
this.repaintFn = lodash_1.throttle(repaint, options.repaintDebounceMs);
}
}
getStateMachine() {
return this.stateMachine;
}
getRelativeMousePoint(event) {
const point = this.getRelativePoint(event.clientX, event.clientY);
return new geometry_1.Point((point.x - this.model.getOffsetX()) / (this.model.getZoomLevel() / 100.0), (point.y - this.model.getOffsetY()) / (this.model.getZoomLevel() / 100.0));
}
getRelativePoint(x, y) {
const canvasRect = this.canvas.getBoundingClientRect();
return new geometry_1.Point(x - canvasRect.left, y - canvasRect.top);
}
registerFactoryBank(factory) {
factory.registerListener({
factoryAdded: (event) => {
event.factory.setDiagramEngine(this);
},
factoryRemoved: (event) => {
event.factory.setDiagramEngine(null);
}
});
}
getActionEventBus() {
return this.eventBus;
}
getLayerFactories() {
return this.layerFactories;
}
getFactoryForLayer(layer) {
if (typeof layer === 'string') {
return this.layerFactories.getFactory(layer);
}
return this.layerFactories.getFactory(layer.getType());
}
setModel(model) {
this.model = model;
if (this.canvas) {
requestAnimationFrame(() => {
this.repaintCanvas();
});
}
}
getModel() {
return this.model;
}
repaintCanvas(promise) {
const { repaintDebounceMs } = this.options;
if (promise) {
return new Promise((resolve) => {
const l = this.registerListener({
rendered: () => {
resolve();
l.deregister();
}
});
this.repaintFn();
});
}
this.repaintFn();
}
setCanvas(canvas) {
if (this.canvas !== canvas) {
this.canvas = canvas;
if (canvas) {
this.fireEvent({}, 'canvasReady');
}
}
}
getCanvas() {
return this.canvas;
}
getMouseElement(event) {
return null;
}
zoomToFit() {
const xFactor = this.canvas.clientWidth / this.canvas.scrollWidth;
const yFactor = this.canvas.clientHeight / this.canvas.scrollHeight;
const zoomFactor = xFactor < yFactor ? xFactor : yFactor;
this.model.setZoomLevel(this.model.getZoomLevel() * zoomFactor);
this.model.setOffset(0, 0);
this.repaintCanvas();
}
}
exports.CanvasEngine = CanvasEngine;
//# sourceMappingURL=CanvasEngine.js.map
I think you have to fork, change the code and PR to author so he can validate the changes and integrate to main