diff --git a/modules/graph-layers/src/core/graph-engine.ts b/modules/graph-layers/src/core/graph-engine.ts index 1db68db05..a85bae086 100644 --- a/modules/graph-layers/src/core/graph-engine.ts +++ b/modules/graph-layers/src/core/graph-engine.ts @@ -12,17 +12,28 @@ import {log} from '../utils/log'; export type GraphEngineProps = { graph: Graph; layout: GraphLayout; + /** + * Throttle layout change notifications (in milliseconds). When greater than zero the + * engine will avoid emitting more than one `onLayoutChange` event within the configured + * interval. This is useful to slow down very fast layout updates so that the + * visualization can animate at a comfortable pace. + */ + layoutUpdateThrottleMs?: number; }; /** Graph engine controls the graph data and layout calculation */ export class GraphEngine extends EventTarget { - props: Readonly>; + props: Readonly; private readonly _graph: Graph; private readonly _layout: GraphLayout; + private readonly _layoutUpdateThrottleMs: number; private readonly _cache = new Cache<'nodes' | 'edges', Node[] | Edge[]>(); private _layoutDirty = false; private _transactionInProgress = false; + private _layoutChangeTimeout: ReturnType | null = null; + private _lastLayoutChangeTs = 0; + private _pendingLayoutChange = false; constructor(props: GraphEngineProps); /** @deprecated Use props constructor: new GraphEngine(props) */ @@ -40,6 +51,7 @@ export class GraphEngine extends EventTarget { this.props = props; this._graph = props.graph; this._layout = props.layout; + this._layoutUpdateThrottleMs = props.layoutUpdateThrottleMs ?? 0; } /** Getters */ @@ -91,7 +103,7 @@ export class GraphEngine extends EventTarget { /** * @fires GraphEngine#onLayoutChange */ - _onLayoutChange = () => { + private _emitLayoutChangeEvent = () => { log.log(0, 'GraphEngine: layout update event')(); /** * @event GraphEngine#onLayoutChange @@ -100,10 +112,66 @@ export class GraphEngine extends EventTarget { this.dispatchEvent(new CustomEvent('onLayoutChange')); }; + private _clearPendingLayoutChange() { + if (this._layoutChangeTimeout) { + clearTimeout(this._layoutChangeTimeout); + this._layoutChangeTimeout = null; + } + this._pendingLayoutChange = false; + } + + private _handleLayoutChange = () => { + if (!this._layoutUpdateThrottleMs) { + this._clearPendingLayoutChange(); + this._lastLayoutChangeTs = Date.now(); + this._emitLayoutChangeEvent(); + return; + } + + const now = Date.now(); + const elapsed = now - this._lastLayoutChangeTs; + if (!this._layoutChangeTimeout) { + if (elapsed >= this._layoutUpdateThrottleMs) { + this._lastLayoutChangeTs = now; + this._emitLayoutChangeEvent(); + return; + } + + const delay = Math.max(this._layoutUpdateThrottleMs - elapsed, 0); + this._scheduleThrottledLayoutChange(delay); + return; + } + + this._pendingLayoutChange = true; + }; + + private _scheduleThrottledLayoutChange(delay: number) { + this._layoutChangeTimeout = setTimeout(() => { + this._layoutChangeTimeout = null; + this._lastLayoutChangeTs = Date.now(); + this._emitLayoutChangeEvent(); + if (this._pendingLayoutChange) { + this._pendingLayoutChange = false; + if (this._layoutUpdateThrottleMs > 0) { + this._scheduleThrottledLayoutChange(this._layoutUpdateThrottleMs); + } + } + }, delay); + } + + _onLayoutChange = () => { + this._handleLayoutChange(); + }; + /** * @fires GraphEngine#onLayoutDone */ _onLayoutDone = () => { + if (this._layoutUpdateThrottleMs && this._layoutChangeTimeout) { + this._clearPendingLayoutChange(); + this._lastLayoutChangeTs = Date.now(); + this._emitLayoutChangeEvent(); + } log.log(0, 'GraphEngine: layout end')(); /** * @event GraphEngine#onLayoutDone @@ -142,6 +210,8 @@ export class GraphEngine extends EventTarget { run = () => { log.log(1, 'GraphEngine: run')(); // TODO: throw if running on a cleared engine + this._clearPendingLayoutChange(); + this._lastLayoutChangeTs = 0; this._graph.addEventListener('transactionStart', this._onTransactionStart); this._graph.addEventListener('transactionEnd', this._onTransactionEnd); @@ -172,6 +242,8 @@ export class GraphEngine extends EventTarget { this._layout.removeEventListener('onLayoutChange', this._onLayoutChange); this._layout.removeEventListener('onLayoutDone', this._onLayoutDone); this._layout.removeEventListener('onLayoutError', this._onLayoutError); + + this._clearPendingLayoutChange(); }; resume = () => this._layout.resume(); @@ -186,6 +258,7 @@ export class GraphEngine extends EventTarget { _updateLayout = () => { log.log(0, 'GraphEngine: layout update')(); + this._lastLayoutChangeTs = 0; this._layout.updateGraph(this._graph); this._layout.update(); this._layoutDirty = false; diff --git a/modules/graph-layers/src/layers/edge-layer.ts b/modules/graph-layers/src/layers/edge-layer.ts index e16d0b182..9f17ab161 100644 --- a/modules/graph-layers/src/layers/edge-layer.ts +++ b/modules/graph-layers/src/layers/edge-layer.ts @@ -55,7 +55,8 @@ export class EdgeLayer extends CompositeLayer { } renderLayers() { - const {getLayoutInfo, pickable, positionUpdateTrigger, stylesheet, id} = this.props as any; + const {getLayoutInfo, pickable, positionUpdateTrigger, stylesheet, id, transitions} = + this.props as any; const {typedEdgeData} = this.state; @@ -67,21 +68,24 @@ export class EdgeLayer extends CompositeLayer { if (!Layer) { return null; } - return new Layer({ - id: `${id}-${idx}`, - data: edgeData, - getLayoutInfo, - getColor: stylesheet.getDeckGLAccessor('getColor'), - getWidth: stylesheet.getDeckGLAccessor('getWidth'), - colorUpdateTrigger: stylesheet.getDeckGLAccessorUpdateTrigger('getColor'), - widthUpdateTrigger: stylesheet.getDeckGLAccessorUpdateTrigger('getWidth'), - positionUpdateTrigger, - pickable, - coordinateSystem: COORDINATE_SYSTEM.CARTESIAN, - parameters: { - depthCompare: 'always' - } - } as any); + return new Layer( + this.getSubLayerProps({ + id: `${id}-${idx}`, + data: edgeData, + getLayoutInfo, + getColor: stylesheet.getDeckGLAccessor('getColor'), + getWidth: stylesheet.getDeckGLAccessor('getWidth'), + colorUpdateTrigger: stylesheet.getDeckGLAccessorUpdateTrigger('getColor'), + widthUpdateTrigger: stylesheet.getDeckGLAccessorUpdateTrigger('getWidth'), + positionUpdateTrigger, + pickable, + transitions, + coordinateSystem: COORDINATE_SYSTEM.CARTESIAN, + parameters: { + depthCompare: 'always' + } + }) + ); }); } } diff --git a/modules/graph-layers/src/layers/edge-layers/edge-arrow-layer.ts b/modules/graph-layers/src/layers/edge-layers/edge-arrow-layer.ts index 262cbbc03..14a8ef1e5 100644 --- a/modules/graph-layers/src/layers/edge-layers/edge-arrow-layer.ts +++ b/modules/graph-layers/src/layers/edge-layers/edge-arrow-layer.ts @@ -116,7 +116,8 @@ export class EdgeArrowLayer extends CompositeLayer { static layerName = 'EdgeArrowLayer'; renderLayers() { - const {data, getLayoutInfo, positionUpdateTrigger = 0, stylesheet} = this.props as any; + const {data, getLayoutInfo, positionUpdateTrigger = 0, stylesheet, transitions} = + this.props as any; const directedEdges = (data || []).filter(isEdgeDirected); if (!directedEdges.length) { @@ -154,6 +155,7 @@ export class EdgeArrowLayer extends CompositeLayer { parameters: { depthTest: false }, + transitions, updateTriggers: { getColor: updateTriggers.getColor, getScale: updateTriggers.getSize, diff --git a/modules/graph-layers/src/layers/edge-layers/edge-label-layer.ts b/modules/graph-layers/src/layers/edge-layers/edge-label-layer.ts index d9a46f716..d0fe45dcb 100644 --- a/modules/graph-layers/src/layers/edge-layers/edge-label-layer.ts +++ b/modules/graph-layers/src/layers/edge-layers/edge-label-layer.ts @@ -9,7 +9,8 @@ export class EdgeLabelLayer extends CompositeLayer { static layerName = 'EdgeLabelLayer'; renderLayers() { - const {data, getLayoutInfo, positionUpdateTrigger = 0, stylesheet} = this.props as any; + const {data, getLayoutInfo, positionUpdateTrigger = 0, stylesheet, transitions} = + this.props as any; return [ new ZoomableTextLayer( this.getSubLayerProps({ @@ -37,6 +38,7 @@ export class EdgeLabelLayer extends CompositeLayer { return (Math.atan2(deltaY, deltaX) * -180) / Math.PI; }, ...stylesheet.getDeckGLAccessors(), + transitions, updateTriggers: { ...stylesheet.getDeckGLUpdateTriggers(), getPosition: positionUpdateTrigger diff --git a/modules/graph-layers/src/layers/edge-layers/flow-layer.ts b/modules/graph-layers/src/layers/edge-layers/flow-layer.ts index 6a9e882cb..a58b4b74f 100644 --- a/modules/graph-layers/src/layers/edge-layers/flow-layer.ts +++ b/modules/graph-layers/src/layers/edge-layers/flow-layer.ts @@ -10,7 +10,8 @@ export class FlowLayer extends CompositeLayer { static layerName = 'FlowLayer'; renderLayers() { - const {data, getLayoutInfo, positionUpdateTrigger = 0, stylesheet} = this.props as any; + const {data, getLayoutInfo, positionUpdateTrigger = 0, stylesheet, transitions} = + this.props as any; return [ new FlowPathLayer( this.getSubLayerProps({ @@ -22,6 +23,7 @@ export class FlowLayer extends CompositeLayer { parameters: { depthTest: false }, + transitions, updateTriggers: { ...stylesheet.getDeckGLUpdateTriggers(), getSourcePosition: positionUpdateTrigger, diff --git a/modules/graph-layers/src/layers/graph-layer.ts b/modules/graph-layers/src/layers/graph-layer.ts index 4d972c61b..7f43eff48 100644 --- a/modules/graph-layers/src/layers/graph-layer.ts +++ b/modules/graph-layers/src/layers/graph-layer.ts @@ -62,6 +62,12 @@ export type _GraphLayerProps = { layout?: GraphLayout; graphLoader?: (opts: {json: any}) => Graph; engine?: GraphEngine; + /** Minimum time (in milliseconds) between layout updates emitted by the internal engine. */ + layoutUpdateInterval?: number; + /** Whether to enable deck.gl transitions when layout updates are throttled. */ + layoutTransitions?: boolean; + /** Delay before emitting layout updates when the layer is (re)initialized. */ + layoutUpdateDelay?: number; // an array of styles for layers nodeStyle?: any[]; @@ -95,6 +101,9 @@ export class GraphLayer extends CompositeLayer { // Graph props graphLoader: JSONLoader, + layoutUpdateInterval: 300, + layoutTransitions: false, + layoutUpdateDelay: 1000, nodeStyle: [], nodeEvents: { @@ -120,7 +129,18 @@ export class GraphLayer extends CompositeLayer { // @ts-expect-error Some typescript confusion due to override of base class state state!: CompositeLayer['state'] & { interactionManager: InteractionManager; - graphEngine?: GraphEngine; + graphEngine?: GraphEngine | null; + }; + + private _layoutDelayActive = false; + private _shouldApplyLayoutDelay = false; + + private _handleLayoutChange = () => { + if (this._layoutDelayActive) { + this._shouldApplyLayoutDelay = true; + this._layoutDelayActive = false; + } + this.forceUpdate(); }; forceUpdate = () => { @@ -153,14 +173,14 @@ export class GraphLayer extends CompositeLayer { !(Array.isArray(props.data) && props.data.length === 0) ) { // console.log(props.data); - const graph = this.props.graphLoader({json: props.data}); - const layout = this.props.layout; - const graphEngine = new GraphEngine({graph, layout}); + const graph = props.graphLoader({json: props.data}); + const layout = props.layout; + const graphEngine = this._createGraphEngine(graph, layout, props); this._setGraphEngine(graphEngine); this.state.interactionManager.updateProps(props); this.forceUpdate(); } else if (changeFlags.propsChanged && props.graph !== oldProps.graph) { - const graphEngine = new GraphEngine({graph: props.graph, layout: props.layout}); + const graphEngine = this._createGraphEngine(props.graph, props.layout, props); this._setGraphEngine(graphEngine); this.state.interactionManager.updateProps(props); this.forceUpdate(); @@ -169,13 +189,18 @@ export class GraphLayer extends CompositeLayer { this.state.interactionManager.updateProps(props); this.forceUpdate(); } + + if (changeFlags.propsChanged && props.layoutUpdateDelay !== oldProps.layoutUpdateDelay) { + this._layoutDelayActive = (props.layoutUpdateDelay ?? 0) > 0; + this._shouldApplyLayoutDelay = false; + } } finalize() { this._removeGraphEngine(); } - _setGraphEngine(graphEngine: GraphEngine) { + _setGraphEngine(graphEngine: GraphEngine | null) { if (graphEngine === this.state.graphEngine) { return; } @@ -183,27 +208,87 @@ export class GraphLayer extends CompositeLayer { this._removeGraphEngine(); if (graphEngine) { this.state.graphEngine = graphEngine; + this._layoutDelayActive = (this.props.layoutUpdateDelay ?? 0) > 0; + this._shouldApplyLayoutDelay = false; + this.state.graphEngine.addEventListener('onLayoutChange', this._handleLayoutChange); this.state.graphEngine.run(); // added or removed a node, or in general something layout related changed - this.state.graphEngine.addEventListener('onLayoutChange', this.forceUpdate); } } _removeGraphEngine() { if (this.state.graphEngine) { - this.state.graphEngine.removeEventListener('onLayoutChange', this.forceUpdate); + this.state.graphEngine.removeEventListener('onLayoutChange', this._handleLayoutChange); this.state.graphEngine.clear(); this.state.graphEngine = null; } + this._layoutDelayActive = false; + this._shouldApplyLayoutDelay = false; + } + + private _createGraphEngine( + graph?: Graph, + layout?: GraphLayout, + props: GraphLayerProps = this.props + ): GraphEngine | null { + if (!graph || !layout) { + return null; + } + return new GraphEngine({ + graph, + layout, + layoutUpdateThrottleMs: props.layoutUpdateInterval + }); + } + + private _getLayoutTransitionSettings(): any { + const {layoutUpdateInterval, layoutTransitions, layoutUpdateDelay} = this.props; + if (!layoutTransitions || !layoutUpdateInterval || layoutUpdateInterval <= 0) { + this._shouldApplyLayoutDelay = false; + return undefined; + } + const transition: any = {duration: layoutUpdateInterval}; + if (this._shouldApplyLayoutDelay && layoutUpdateDelay && layoutUpdateDelay > 0) { + transition.delay = layoutUpdateDelay; + } + this._shouldApplyLayoutDelay = false; + return transition; } - createNodeLayers() { + private _getNodeTransitions(layoutTransition?: any): any { + const transition = layoutTransition; + if (!transition) { + return undefined; + } + return { + getPosition: transition + }; + } + + private _getEdgeTransitions(layoutTransition?: any): any { + const transition = layoutTransition; + if (!transition) { + return undefined; + } + return { + getSourcePosition: transition, + getTargetPosition: transition, + getControlPoints: transition, + getPath: transition, + getPosition: transition, + getOrientation: transition + }; + } + + createNodeLayers(layoutTransition?: any) { const engine = this.state.graphEngine; const {nodeStyle} = this.props; if (!engine || !nodeStyle || !Array.isArray(nodeStyle) || nodeStyle.length === 0) { return []; } + const nodeTransitions = this._getNodeTransitions(layoutTransition); + return nodeStyle.filter(Boolean).map((style, idx) => { const {pickable = true, visible = true, data = (nodes) => nodes, ...restStyle} = style; const LayerType = NODE_LAYER_MAP[style.type]; @@ -226,13 +311,14 @@ export class GraphLayer extends CompositeLayer { engine.getLayoutState(), stylesheet.getDeckGLAccessorUpdateTrigger('getOffset') ].join(), + transitions: nodeTransitions, stylesheet, visible } as any); }); } - createEdgeLayers() { + createEdgeLayers(layoutTransition?: any) { const engine = this.state.graphEngine; const {edgeStyle} = this.props; @@ -240,6 +326,8 @@ export class GraphLayer extends CompositeLayer { return []; } + const edgeTransitions = this._getEdgeTransitions(layoutTransition); + return (Array.isArray(edgeStyle) ? edgeStyle : [edgeStyle]) .filter(Boolean) .flatMap((style, idx) => { @@ -261,6 +349,7 @@ export class GraphLayer extends CompositeLayer { getLayoutInfo: engine.getEdgePosition, pickable: true, positionUpdateTrigger: [engine.getLayoutLastUpdate(), engine.getLayoutState()].join(), + transitions: edgeTransitions, stylesheet, visible } as any); @@ -268,25 +357,29 @@ export class GraphLayer extends CompositeLayer { if (!decorators || !Array.isArray(decorators) || decorators.length === 0) { return edgeLayer; } - const decoratorLayers = decorators.filter(Boolean).flatMap((decoratorStyle, idx2) => { - const DecoratorLayer = EDGE_DECORATOR_LAYER_MAP[decoratorStyle.type]; - if (!DecoratorLayer) { - log.error(`Invalid edge decorator type: ${decoratorStyle.type}`)(); - throw new Error(`Invalid edge decorator type: ${decoratorStyle.type}`); - } - const decoratorStylesheet = new Stylesheet(decoratorStyle, { - stateUpdateTrigger: (this.state.interactionManager as any).getLastInteraction() + + const decoratorLayers = decorators + .filter(Boolean) + .flatMap((decoratorStyle, idx2) => { + const DecoratorLayer = EDGE_DECORATOR_LAYER_MAP[decoratorStyle.type]; + if (!DecoratorLayer) { + log.error(`Invalid edge decorator type: ${decoratorStyle.type}`)(); + throw new Error(`Invalid edge decorator type: ${decoratorStyle.type}`); + } + const decoratorStylesheet = new Stylesheet(decoratorStyle, { + stateUpdateTrigger: (this.state.interactionManager as any).getLastInteraction() + }); + return new DecoratorLayer({ + ...SHARED_LAYER_PROPS, + id: `edge-decorator-${idx2}`, + data: data(engine.getEdges()), + getLayoutInfo: engine.getEdgePosition, + pickable: true, + positionUpdateTrigger: [engine.getLayoutLastUpdate(), engine.getLayoutState()].join(), + transitions: edgeTransitions, + stylesheet: decoratorStylesheet + } as any); }); - return new DecoratorLayer({ - ...SHARED_LAYER_PROPS, - id: `edge-decorator-${idx2}`, - data: data(engine.getEdges()), - getLayoutInfo: engine.getEdgePosition, - pickable: true, - positionUpdateTrigger: [engine.getLayoutLastUpdate(), engine.getLayoutState()].join(), - stylesheet: decoratorStylesheet - } as any); - }); return [edgeLayer, decoratorLayers]; }); } @@ -312,6 +405,7 @@ export class GraphLayer extends CompositeLayer { } renderLayers() { - return [this.createEdgeLayers(), this.createNodeLayers()]; + const layoutTransition = this._getLayoutTransitionSettings(); + return [this.createEdgeLayers(layoutTransition), this.createNodeLayers(layoutTransition)]; } } diff --git a/modules/graph-layers/src/layers/node-layers/circle-layer.ts b/modules/graph-layers/src/layers/node-layers/circle-layer.ts index 6a01904df..8fd4e9588 100644 --- a/modules/graph-layers/src/layers/node-layers/circle-layer.ts +++ b/modules/graph-layers/src/layers/node-layers/circle-layer.ts @@ -9,7 +9,8 @@ export class CircleLayer extends CompositeLayer { static layerName = 'CircleLayer'; renderLayers() { - const {data, getPosition, stylesheet, positionUpdateTrigger = 0} = this.props as any; + const {data, getPosition, stylesheet, positionUpdateTrigger = 0, transitions} = + this.props as any; return [ new ScatterplotLayer( @@ -18,6 +19,7 @@ export class CircleLayer extends CompositeLayer { data, getPosition, ...stylesheet.getDeckGLAccessors(), + transitions, updateTriggers: { getPosition: positionUpdateTrigger, ...stylesheet.getDeckGLUpdateTriggers() diff --git a/modules/graph-layers/src/layers/node-layers/image-layer.ts b/modules/graph-layers/src/layers/node-layers/image-layer.ts index 904c82c50..df0e9b61f 100644 --- a/modules/graph-layers/src/layers/node-layers/image-layer.ts +++ b/modules/graph-layers/src/layers/node-layers/image-layer.ts @@ -9,7 +9,8 @@ export class ImageLayer extends CompositeLayer { static layerName = 'ImageLayer'; renderLayers() { - const {data, getPosition, stylesheet, positionUpdateTrigger = 0} = this.props as any; + const {data, getPosition, stylesheet, positionUpdateTrigger = 0, transitions} = + this.props as any; return [ new IconLayer( @@ -18,6 +19,7 @@ export class ImageLayer extends CompositeLayer { data, getPosition, ...stylesheet.getDeckGLAccessors(), + transitions, updateTriggers: { getPosition: positionUpdateTrigger, ...stylesheet.getDeckGLUpdateTriggers() diff --git a/modules/graph-layers/src/layers/node-layers/label-layer.ts b/modules/graph-layers/src/layers/node-layers/label-layer.ts index 4741a2da4..0b6d8d6ac 100644 --- a/modules/graph-layers/src/layers/node-layers/label-layer.ts +++ b/modules/graph-layers/src/layers/node-layers/label-layer.ts @@ -9,7 +9,8 @@ export class LabelLayer extends CompositeLayer { static layerName = 'LabelLayer'; renderLayers() { - const {data, getPosition, stylesheet, positionUpdateTrigger = 0} = this.props as any; + const {data, getPosition, stylesheet, positionUpdateTrigger = 0, transitions} = + this.props as any; return [ new ZoomableTextLayer( @@ -18,6 +19,7 @@ export class LabelLayer extends CompositeLayer { data, getPosition, ...stylesheet.getDeckGLAccessors(), + transitions, updateTriggers: { ...stylesheet.getDeckGLUpdateTriggers(), getPosition: positionUpdateTrigger diff --git a/modules/graph-layers/src/layers/node-layers/path-rounded-rectangle-layer.ts b/modules/graph-layers/src/layers/node-layers/path-rounded-rectangle-layer.ts index 9d2d5f230..7094b0699 100644 --- a/modules/graph-layers/src/layers/node-layers/path-rounded-rectangle-layer.ts +++ b/modules/graph-layers/src/layers/node-layers/path-rounded-rectangle-layer.ts @@ -20,7 +20,8 @@ export class PathBasedRoundedRectangleLayer extends CompositeLayer { static layerName = 'PathBasedRoundedRectangleLayer'; renderLayers() { - const {data, getPosition, stylesheet, positionUpdateTrigger = 0} = this.props as any; + const {data, getPosition, stylesheet, positionUpdateTrigger = 0, transitions} = + this.props as any; const getFillColor = stylesheet.getDeckGLAccessor('getFillColor'); const getLineWidth = stylesheet.getDeckGLAccessor('getLineWidth'); @@ -40,6 +41,7 @@ export class PathBasedRoundedRectangleLayer extends CompositeLayer { filled: Boolean(getFillColor), stroked: Boolean(getLineWidth), ...stylesheet.getDeckGLAccessors(), + transitions, updateTriggers: { getPolygon: [ positionUpdateTrigger, diff --git a/modules/graph-layers/src/layers/node-layers/rectangle-layer.ts b/modules/graph-layers/src/layers/node-layers/rectangle-layer.ts index 1ff6f1403..e4a02b828 100644 --- a/modules/graph-layers/src/layers/node-layers/rectangle-layer.ts +++ b/modules/graph-layers/src/layers/node-layers/rectangle-layer.ts @@ -23,7 +23,8 @@ export class RectangleLayer extends CompositeLayer { static layerName = 'RectangleLayer'; renderLayers() { - const {data, getPosition, stylesheet, positionUpdateTrigger = 0} = this.props as any; + const {data, getPosition, stylesheet, positionUpdateTrigger = 0, transitions} = + this.props as any; const getFillColor = stylesheet.getDeckGLAccessor('getFillColor'); const getLineWidth = stylesheet.getDeckGLAccessor('getLineWidth'); @@ -43,6 +44,7 @@ export class RectangleLayer extends CompositeLayer { jointRounded: true, stroked: Boolean(getLineWidth), ...stylesheet.getDeckGLAccessors(), + transitions, updateTriggers: { getPolygon: [ positionUpdateTrigger, diff --git a/modules/graph-layers/src/layers/node-layers/zoomable-marker-layer.ts b/modules/graph-layers/src/layers/node-layers/zoomable-marker-layer.ts index 0584843bc..10586074f 100644 --- a/modules/graph-layers/src/layers/node-layers/zoomable-marker-layer.ts +++ b/modules/graph-layers/src/layers/node-layers/zoomable-marker-layer.ts @@ -18,7 +18,8 @@ export class ZoomableMarkerLayer extends CompositeLayer { } renderLayers() { - const {data, getPosition, stylesheet, positionUpdateTrigger = 0} = this.props as any; + const {data, getPosition, stylesheet, positionUpdateTrigger = 0, transitions} = + this.props as any; const getSize = stylesheet.getDeckGLAccessor('getSize'); const scaleWithZoom = stylesheet.getDeckGLAccessor('scaleWithZoom'); @@ -37,6 +38,7 @@ export class ZoomableMarkerLayer extends CompositeLayer { sizeScale: scaleWithZoom ? Math.max(0, this.context.viewport.zoom) : 1, ...stylesheet.getDeckGLAccessors(), getMarker, + transitions, updateTriggers: { ...stylesheet.getDeckGLUpdateTriggers(), getPosition: positionUpdateTrigger,