UNPKG

@wordpress/block-editor

Version:
268 lines (267 loc) 9.23 kB
// packages/block-editor/src/components/iframe/use-scale-canvas.js import { useEffect, useRef, useCallback } from "@wordpress/element"; import { useReducedMotion, useResizeObserver } from "@wordpress/compose"; function calculateScale({ frameSize, containerWidth, maxContainerWidth, scaleContainerWidth }) { return (Math.min(containerWidth, maxContainerWidth) - frameSize * 2) / scaleContainerWidth; } function computeScrollHeightNext(transitionFrom, transitionTo) { const { scaleValue: prevScale, scrollHeight: prevScrollHeight } = transitionFrom; const { frameSize, scaleValue } = transitionTo; return prevScrollHeight * (scaleValue / prevScale) + frameSize * 2; } function computeScrollTopNext(transitionFrom, transitionTo) { const { containerHeight: prevContainerHeight, frameSize: prevFrameSize, scaleValue: prevScale, scrollTop: prevScrollTop } = transitionFrom; const { containerHeight, frameSize, scaleValue, scrollHeight } = transitionTo; let scrollTopNext = prevScrollTop; scrollTopNext = (scrollTopNext + prevContainerHeight / 2 - prevFrameSize) / prevScale - prevContainerHeight / 2; scrollTopNext = (scrollTopNext + containerHeight / 2) * scaleValue + frameSize - containerHeight / 2; scrollTopNext = prevScrollTop <= prevFrameSize ? 0 : scrollTopNext; const maxScrollTop = scrollHeight - containerHeight; return Math.round( Math.min(Math.max(0, scrollTopNext), Math.max(0, maxScrollTop)) ); } function getAnimationKeyframes(transitionFrom, transitionTo) { const { scaleValue: prevScale, frameSize: prevFrameSize, scrollTop } = transitionFrom; const { scaleValue, frameSize, scrollTop: scrollTopNext } = transitionTo; return [ { translate: `0 0`, scale: prevScale, paddingTop: `${prevFrameSize / prevScale}px`, paddingBottom: `${prevFrameSize / prevScale}px` }, { translate: `0 ${scrollTop - scrollTopNext}px`, scale: scaleValue, paddingTop: `${frameSize / scaleValue}px`, paddingBottom: `${frameSize / scaleValue}px` } ]; } function useScaleCanvas({ frameSize, iframeDocument, maxContainerWidth = 750, scale }) { const [contentResizeListener, { height: contentHeight }] = useResizeObserver(); const [ containerResizeListener, { width: containerWidth, height: containerHeight } ] = useResizeObserver(); const initialContainerWidthRef = useRef(0); const isZoomedOut = scale !== 1; const prefersReducedMotion = useReducedMotion(); const isAutoScaled = scale === "auto-scaled"; const startAnimationRef = useRef(false); const animationRef = useRef(null); useEffect(() => { if (!isZoomedOut) { initialContainerWidthRef.current = containerWidth; } }, [containerWidth, isZoomedOut]); const scaleContainerWidth = Math.max( initialContainerWidthRef.current, containerWidth ); const scaleValue = isAutoScaled ? calculateScale({ frameSize, containerWidth, maxContainerWidth, scaleContainerWidth }) : scale; const transitionFromRef = useRef({ scaleValue, frameSize, containerHeight: 0, scrollTop: 0, scrollHeight: 0 }); const transitionToRef = useRef({ scaleValue, frameSize, containerHeight: 0, scrollTop: 0, scrollHeight: 0 }); const startZoomOutAnimation = useCallback(() => { const { scrollTop } = transitionFromRef.current; const { scrollTop: scrollTopNext } = transitionToRef.current; iframeDocument.documentElement.style.setProperty( "--wp-block-editor-iframe-zoom-out-scroll-top", `${scrollTop}px` ); iframeDocument.documentElement.style.setProperty( "--wp-block-editor-iframe-zoom-out-scroll-top-next", `${scrollTopNext}px` ); iframeDocument.documentElement.style.setProperty( "--wp-block-editor-iframe-zoom-out-overflow-behavior", transitionFromRef.current.scrollHeight === transitionFromRef.current.containerHeight ? "auto" : "scroll" ); iframeDocument.documentElement.classList.add("zoom-out-animation"); return iframeDocument.documentElement.animate( getAnimationKeyframes( transitionFromRef.current, transitionToRef.current ), { easing: "cubic-bezier(0.46, 0.03, 0.52, 0.96)", duration: 400 } ); }, [iframeDocument]); const finishZoomOutAnimation = useCallback(() => { startAnimationRef.current = false; animationRef.current = null; iframeDocument.documentElement.style.setProperty( "--wp-block-editor-iframe-zoom-out-scale", transitionToRef.current.scaleValue ); iframeDocument.documentElement.style.setProperty( "--wp-block-editor-iframe-zoom-out-frame-size", `${transitionToRef.current.frameSize}px` ); iframeDocument.documentElement.classList.remove("zoom-out-animation"); iframeDocument.documentElement.scrollTop = transitionToRef.current.scrollTop; iframeDocument.documentElement.style.removeProperty( "--wp-block-editor-iframe-zoom-out-scroll-top" ); iframeDocument.documentElement.style.removeProperty( "--wp-block-editor-iframe-zoom-out-scroll-top-next" ); iframeDocument.documentElement.style.removeProperty( "--wp-block-editor-iframe-zoom-out-overflow-behavior" ); transitionFromRef.current = transitionToRef.current; }, [iframeDocument]); const previousIsZoomedOut = useRef(false); useEffect(() => { const trigger = iframeDocument && previousIsZoomedOut.current !== isZoomedOut; previousIsZoomedOut.current = isZoomedOut; if (!trigger) { return; } startAnimationRef.current = true; if (!isZoomedOut) { return; } iframeDocument.documentElement.classList.add("is-zoomed-out"); return () => { iframeDocument.documentElement.classList.remove("is-zoomed-out"); }; }, [iframeDocument, isZoomedOut]); useEffect(() => { if (!iframeDocument) { return; } if (isAutoScaled && transitionFromRef.current.scaleValue !== 1) { transitionFromRef.current.scaleValue = calculateScale({ frameSize: transitionFromRef.current.frameSize, containerWidth, maxContainerWidth, scaleContainerWidth: containerWidth }); } if (scaleValue < 1) { if (!startAnimationRef.current) { iframeDocument.documentElement.style.setProperty( "--wp-block-editor-iframe-zoom-out-scale", scaleValue ); iframeDocument.documentElement.style.setProperty( "--wp-block-editor-iframe-zoom-out-frame-size", `${frameSize}px` ); } iframeDocument.documentElement.style.setProperty( "--wp-block-editor-iframe-zoom-out-content-height", `${contentHeight}px` ); iframeDocument.documentElement.style.setProperty( "--wp-block-editor-iframe-zoom-out-inner-height", `${containerHeight}px` ); iframeDocument.documentElement.style.setProperty( "--wp-block-editor-iframe-zoom-out-container-width", `${containerWidth}px` ); iframeDocument.documentElement.style.setProperty( "--wp-block-editor-iframe-zoom-out-scale-container-width", `${scaleContainerWidth}px` ); } if (startAnimationRef.current) { startAnimationRef.current = false; if (animationRef.current) { animationRef.current.reverse(); const tempTransitionFrom = transitionFromRef.current; const tempTransitionTo = transitionToRef.current; transitionFromRef.current = tempTransitionTo; transitionToRef.current = tempTransitionFrom; } else { transitionFromRef.current.scrollTop = iframeDocument.documentElement.scrollTop; transitionFromRef.current.scrollHeight = iframeDocument.documentElement.scrollHeight; transitionFromRef.current.containerHeight = containerHeight; transitionToRef.current = { scaleValue, frameSize, containerHeight: iframeDocument.documentElement.clientHeight // use clientHeight to get the actual height of the new container after zoom state changes have rendered, as it will be the most up-to-date. }; transitionToRef.current.scrollHeight = computeScrollHeightNext( transitionFromRef.current, transitionToRef.current ); transitionToRef.current.scrollTop = computeScrollTopNext( transitionFromRef.current, transitionToRef.current ); animationRef.current = startZoomOutAnimation(); if (prefersReducedMotion) { finishZoomOutAnimation(); } else { animationRef.current.onfinish = finishZoomOutAnimation; } } } }, [ startZoomOutAnimation, finishZoomOutAnimation, prefersReducedMotion, isAutoScaled, scaleValue, frameSize, iframeDocument, contentHeight, containerWidth, containerHeight, maxContainerWidth, scaleContainerWidth ]); return { isZoomedOut, scaleContainerWidth, contentResizeListener, containerResizeListener }; } export { useScaleCanvas }; //# sourceMappingURL=use-scale-canvas.mjs.map