diff --git a/CHANGELOG.md b/CHANGELOG.md index e286d72a..e9ae9dd4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p #### 🐛 Fixed - Always display validation state for an entities and relations in case when the target does not have any authoring changes. - Display elliptical authoring state overlays for elliptically-shaped entity elements. +- Use provided `duration` in `CanvasApi.animateGraph()` for element transitions without the need to override the styles. #### ⏱ Performance - Optimize diagram loading time by avoiding unnecessary updates and separating a measuring element sizes step from applying the sizes to the rendering state. diff --git a/src/diagram/canvasApi.ts b/src/diagram/canvasApi.ts index a4c37645..26e33ce1 100644 --- a/src/diagram/canvasApi.ts +++ b/src/diagram/canvasApi.ts @@ -132,7 +132,7 @@ export interface CanvasApi { * Starts animation for graph elements and links. * * @param setupChanges immediately called function to perform animatable changes on graph - * @param duration animation duration in milliseconds (requires custom CSS to override) + * @param duration duration animation duration in milliseconds (default is `500`) * @returns promise which resolves when this animation ends * * **Example**: diff --git a/src/diagram/paperArea.tsx b/src/diagram/paperArea.tsx index a9d9a811..b251b859 100644 --- a/src/diagram/paperArea.tsx +++ b/src/diagram/paperArea.tsx @@ -46,6 +46,9 @@ interface State { readonly scale: number; readonly paddingX: number; readonly paddingY: number; + + readonly cssAnimations: number; + readonly cssAnimationDuration: number | undefined; } interface PointerMoveState { @@ -106,7 +109,6 @@ export class PaperArea extends React.Component implements private readonly canvasContext: CanvasContext; private viewportAnimation: ViewportAnimation | undefined; - private cssAnimations = 0; private movingState: PointerMoveState | undefined; @@ -134,6 +136,8 @@ export class PaperArea extends React.Component implements scale: 1, paddingX: 0, paddingY: 0, + cssAnimations: 0, + cssAnimationDuration: undefined, }; this.resizeObserver = new ResizeObserver(this.onResize); this.metrics = new (class extends BasePaperMetrics { @@ -188,6 +192,7 @@ export class PaperArea extends React.Component implements render() { const {model, renderingState, hideScrollBars, watermarkSvg, watermarkUrl} = this.props; + const {cssAnimationDuration} = this.state; const paperTransform = this.metrics.getTransform(); const className = cx( @@ -195,11 +200,16 @@ export class PaperArea extends React.Component implements hideScrollBars ? `${CLASS_NAME}--hide-scrollbars` : undefined, this.isAnimatingGraph() ? `${CLASS_NAME}--animated` : undefined ); + const style = { + '--reactodia-canvas-animation-duration': cssAnimationDuration === undefined + ? undefined : `${cssAnimationDuration}ms`, + } as React.CSSProperties; const renderedWidgets = Array.from(this.getAllWidgets()); return (
implements } isAnimatingGraph(): boolean { - return this.cssAnimations > 0; + return this.state.cssAnimations > 0; } animateGraph(setupChanges: () => void, duration?: number): Promise { - this.changeGraphAnimationCount(+1); - setupChanges(); - const timeout = typeof duration === 'number' ? duration : DEFAULT_ANIMATION_DURATION; - return delay(timeout).then(() => this.onGraphAnimationEnd()); - } - - private onGraphAnimationEnd() { - this.changeGraphAnimationCount(-1); + this.changeGraphAnimationCount(+1, timeout); + setupChanges(); + return delay(timeout).then(() => this.changeGraphAnimationCount(-1)); } - private changeGraphAnimationCount(change: number) { - const newValue = this.cssAnimations + change; - if (newValue < 0) { return; } - - const previous = this.isAnimatingGraph(); - this.cssAnimations = newValue; - - const current = this.isAnimatingGraph(); - if (previous !== current) { - this.forceUpdate(); - this.source.trigger('changeAnimatingGraph', {source: this, previous}); - } + private changeGraphAnimationCount(change: number, newDuration?: number) { + const beforeAnimating = this.isAnimatingGraph(); + this.setState( + previous => ({ + cssAnimations: previous.cssAnimations + change, + cssAnimationDuration: newDuration ?? previous.cssAnimationDuration, + }), + () => { + const afterAnimating = this.isAnimatingGraph(); + if (afterAnimating !== beforeAnimating) { + this.source.trigger('changeAnimatingGraph', {source: this, previous: beforeAnimating}); + } + } + ); } private get viewportState(): ViewportState { diff --git a/styles/diagram/_paperArea.scss b/styles/diagram/_paperArea.scss index 352855b4..42c38c0c 100644 --- a/styles/diagram/_paperArea.scss +++ b/styles/diagram/_paperArea.scss @@ -67,9 +67,10 @@ &--animated { .reactodia-overlaid-element, .reactodia-element-decorations { - transition: transform 0.5s ease-in-out; + transition: transform var(--reactodia-canvas-animation-duration) ease-in-out; } - .reactodia-link-layer { + .reactodia-link-layer, + .reactodia-label-layer { transition: none; opacity: 0; }