diff --git a/packages/dev/src/examples/misc/donut/donut-full-height/index.tsx b/packages/dev/src/examples/misc/donut/donut-full-height/index.tsx new file mode 100644 index 000000000..b984204be --- /dev/null +++ b/packages/dev/src/examples/misc/donut/donut-full-height/index.tsx @@ -0,0 +1,22 @@ +import React from 'react' +import { VisSingleContainer, VisDonut } from '@unovis/react' +import { ExampleViewerDurationProps } from '@src/components/ExampleViewer/index' + +export const title = 'Donut: Full Height' +export const subTitle = 'Testing the resize behavior' + +export const component = (props: ExampleViewerDurationProps): JSX.Element => { + const data = [3, 2, 5, 4, 0, 1] + return ( + + d} + data={data} + padAngle={0.02} + duration={props.duration} + arcWidth={80} + /> + + ) +} + diff --git a/packages/ts/src/containers/single-container/index.ts b/packages/ts/src/containers/single-container/index.ts index 11aba2ba7..3752f9a9a 100644 --- a/packages/ts/src/containers/single-container/index.ts +++ b/packages/ts/src/containers/single-container/index.ts @@ -147,8 +147,8 @@ export class SingleContainer extends ContainerCore { if (!this._resizeObserver) this._setUpResizeObserver() // Schedule the actual rendering in the next frame - cancelAnimationFrame(this._requestedAnimationFrame) - this._requestedAnimationFrame = requestAnimationFrame(() => { + cancelAnimationFrame(this._renderAnimationFrameId) + this._renderAnimationFrameId = requestAnimationFrame(() => { this._preRender() this._render(duration) }) diff --git a/packages/ts/src/core/container/index.ts b/packages/ts/src/core/container/index.ts index 7c4c78346..9e828cc0c 100644 --- a/packages/ts/src/core/container/index.ts +++ b/packages/ts/src/core/container/index.ts @@ -18,9 +18,10 @@ export class ContainerCore { protected _defaultConfig: ContainerConfigInterface = ContainerDefaultConfig protected _container: HTMLElement - protected _requestedAnimationFrame: number + protected _renderAnimationFrameId: number protected _isFirstRender = true protected _resizeObserver: ResizeObserver | undefined + protected _resizeObserverAnimationFrameId: number protected _svgDefs: Selection protected _svgDefsExternal: Selection private _containerSize: { width: number; height: number } @@ -29,7 +30,7 @@ export class ContainerCore { static DEFAULT_CONTAINER_HEIGHT = 300 constructor (element: HTMLElement) { - this._requestedAnimationFrame = null + this._renderAnimationFrameId = null this._container = element // Setting `role` attribute to `image` to make the container accessible @@ -96,8 +97,8 @@ export class ContainerCore { if (!this._resizeObserver) this._setUpResizeObserver() // Schedule the actual rendering in the next frame - cancelAnimationFrame(this._requestedAnimationFrame) - this._requestedAnimationFrame = requestAnimationFrame(() => { + cancelAnimationFrame(this._renderAnimationFrameId) + this._renderAnimationFrameId = requestAnimationFrame(() => { this._preRender() this._render(duration) }) @@ -137,25 +138,32 @@ export class ContainerCore { protected _setUpResizeObserver (): void { if (this._resizeObserver) return + const containerRect = this._container.getBoundingClientRect() this._containerSize = { width: containerRect.width, height: containerRect.height } this._resizeObserver = new ResizeObserver((entries, observer) => { - const resizedContainerRect = this._container.getBoundingClientRect() - const resizedContainerSize = { width: resizedContainerRect.width, height: resizedContainerRect.height } - const hasSizeChanged = !isEqual(this._containerSize, resizedContainerSize) - // Do resize only if element is attached to the DOM - // will come in useful when some ancestor of container becomes detached - if (hasSizeChanged && resizedContainerSize.width && resizedContainerSize.height) { - this._containerSize = resizedContainerSize - this._onResize() - } + // Using request animation frame to avoid multiple resize events when scrollbars appear/disappear + // See more: https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver#observation_errors + cancelAnimationFrame(this._resizeObserverAnimationFrameId) + this._resizeObserverAnimationFrameId = requestAnimationFrame(() => { + const resizedContainerRect = this._container.getBoundingClientRect() + const resizedContainerSize = { width: resizedContainerRect.width, height: resizedContainerRect.height } + const hasSizeChanged = !isEqual(this._containerSize, resizedContainerSize) + // Do resize only if element is attached to the DOM + // will come in useful when some ancestor of container becomes detached + if (hasSizeChanged && resizedContainerSize.width && resizedContainerSize.height) { + this._containerSize = resizedContainerSize + this._onResize() + } + }) }) this._resizeObserver.observe(this._container) } public destroy (): void { - cancelAnimationFrame(this._requestedAnimationFrame) + cancelAnimationFrame(this._renderAnimationFrameId) + cancelAnimationFrame(this._resizeObserverAnimationFrameId) this._resizeObserver?.disconnect() this.svg.remove() }