Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

useScaleCanvas performance improvements #67496

Merged
merged 4 commits into from
Dec 2, 2024
Merged
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 41 additions & 54 deletions packages/block-editor/src/components/iframe/use-scale-canvas.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,15 @@
* WordPress dependencies
*/
import { useEffect, useRef, useCallback } from '@wordpress/element';
import {
usePrevious,
useReducedMotion,
useResizeObserver,
} from '@wordpress/compose';
import { useReducedMotion, useResizeObserver } from '@wordpress/compose';

/**
* @typedef {Object} TransitionState
* @property {number} scaleValue Scale of the canvas.
* @property {number} frameSize Size of the frame/offset around the canvas.
* @property {number} clientHeight ClientHeight of the iframe.
* @property {number} scrollTop ScrollTop of the iframe.
* @property {number} scrollHeight ScrollHeight of the iframe.
* @property {number} scaleValue Scale of the canvas.
* @property {number} frameSize Size of the frame/offset around the canvas.
* @property {number} containerHeight containerHeight of the iframe.
* @property {number} scrollTop ScrollTop of the iframe.
* @property {number} scrollHeight ScrollHeight of the iframe.
*/

/**
Expand Down Expand Up @@ -48,27 +44,28 @@ function calculateScale( {
*/
function computeScrollTopNext( transitionFrom, transitionTo ) {
const {
clientHeight: prevClientHeight,
containerHeight: prevContainerHeight,
frameSize: prevFrameSize,
scaleValue: prevScale,
scrollTop,
scrollHeight,
} = transitionFrom;
const { clientHeight, frameSize, scaleValue } = transitionTo;
const { containerHeight, frameSize, scaleValue } = transitionTo;
// Step 0: Start with the current scrollTop.
let scrollTopNext = scrollTop;
// Step 1: Undo the effects of the previous scale and frame around the
// midpoint of the visible area.
scrollTopNext =
( scrollTopNext + prevClientHeight / 2 - prevFrameSize ) / prevScale -
prevClientHeight / 2;
( scrollTopNext + prevContainerHeight / 2 - prevFrameSize ) /
prevScale -
prevContainerHeight / 2;

// Step 2: Apply the new scale and frame around the midpoint of the
// visible area.
scrollTopNext =
( scrollTopNext + clientHeight / 2 ) * scaleValue +
( scrollTopNext + containerHeight / 2 ) * scaleValue +
frameSize -
clientHeight / 2;
containerHeight / 2;

// Step 3: Handle an edge case so that you scroll to the top of the
// iframe if the top of the iframe content is visible in the container.
Expand All @@ -82,7 +79,7 @@ function computeScrollTopNext( transitionFrom, transitionTo ) {
const maxScrollTop =
scrollHeight * ( scaleValue / prevScale ) +
frameSize * 2 -
clientHeight;
containerHeight;

// Step 4: Clamp the scrollTopNext between the minimum and maximum
// possible scrollTop positions. Round the value to avoid subpixel
Expand Down Expand Up @@ -150,8 +147,10 @@ export function useScaleCanvas( {
} ) {
const [ contentResizeListener, { height: contentHeight } ] =
useResizeObserver();
const [ containerResizeListener, { width: containerWidth } ] =
useResizeObserver();
const [
containerResizeListener,
{ width: containerWidth, height: containerHeight },
] = useResizeObserver();

const initialContainerWidthRef = useRef( 0 );
const isZoomedOut = scale !== 1;
Expand Down Expand Up @@ -190,7 +189,7 @@ export function useScaleCanvas( {
const transitionFromRef = useRef( {
scaleValue,
frameSize,
clientHeight: 0,
containerHeight: 0,
scrollTop: 0,
scrollHeight: 0,
} );
Expand All @@ -202,7 +201,7 @@ export function useScaleCanvas( {
const transitionToRef = useRef( {
scaleValue,
frameSize,
clientHeight: 0,
containerHeight: 0,
scrollTop: 0,
scrollHeight: 0,
} );
Expand Down Expand Up @@ -284,28 +283,35 @@ export function useScaleCanvas( {
transitionFromRef.current = transitionToRef.current;
}, [ iframeDocument ] );

const previousIsZoomedOut = usePrevious( isZoomedOut );
const previousIsZoomedOut = useRef( false );

/**
* Runs when zoom out mode is toggled, and sets the startAnimation flag
* so the animation will start when the next useEffect runs. We _only_
* want to animate when the zoom out mode is toggled, not when the scale
* changes due to the container resizing.
*/
useEffect( () => {
if ( ! iframeDocument || previousIsZoomedOut === isZoomedOut ) {
return;
}
const trigger =
iframeDocument && previousIsZoomedOut.current !== isZoomedOut;

if ( isZoomedOut ) {
iframeDocument.documentElement.classList.add( 'is-zoomed-out' );
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, previousIsZoomedOut ] );
}, [ iframeDocument, isZoomedOut ] );

/**
* This handles:
Expand Down Expand Up @@ -350,10 +356,9 @@ export function useScaleCanvas( {
`${ contentHeight }px`
);

const clientHeight = iframeDocument.documentElement.clientHeight;
iframeDocument.documentElement.style.setProperty(
'--wp-block-editor-iframe-zoom-out-inner-height',
`${ clientHeight }px`
`${ containerHeight }px`
);

iframeDocument.documentElement.style.setProperty(
Expand Down Expand Up @@ -402,8 +407,9 @@ export function useScaleCanvas( {
// the iframe at this point when we're about to animate the zoom out.
// The iframe scrollTop, scrollHeight, and clientHeight will all be
// the most accurate.
transitionFromRef.current.clientHeight =
transitionFromRef.current.clientHeight ?? clientHeight;
transitionFromRef.current.containerHeight =
transitionFromRef.current.containerHeight ??
containerHeight; // Use containerHeight, as it's the previous container height value if none was set.
transitionFromRef.current.scrollTop =
iframeDocument.documentElement.scrollTop;
transitionFromRef.current.scrollHeight =
Expand All @@ -412,7 +418,8 @@ export function useScaleCanvas( {
transitionToRef.current = {
scaleValue,
frameSize,
clientHeight,
containerHeight:
iframeDocument.documentElement.clientHeight, // use clientHeight to get the actual height of the new container, as it will be the most up-to-date.
};
transitionToRef.current.scrollTop = computeScrollTopNext(
transitionFromRef.current,
Expand All @@ -429,27 +436,6 @@ export function useScaleCanvas( {
}
}
}

return () => {
iframeDocument.documentElement.style.removeProperty(
'--wp-block-editor-iframe-zoom-out-scale'
);
iframeDocument.documentElement.style.removeProperty(
'--wp-block-editor-iframe-zoom-out-frame-size'
);
iframeDocument.documentElement.style.removeProperty(
'--wp-block-editor-iframe-zoom-out-content-height'
);
iframeDocument.documentElement.style.removeProperty(
'--wp-block-editor-iframe-zoom-out-inner-height'
);
iframeDocument.documentElement.style.removeProperty(
'--wp-block-editor-iframe-zoom-out-container-width'
);
iframeDocument.documentElement.style.removeProperty(
'--wp-block-editor-iframe-zoom-out-scale-container-width'
);
};
}, [
startZoomOutAnimation,
finishZoomOutAnimation,
Expand All @@ -460,6 +446,7 @@ export function useScaleCanvas( {
iframeDocument,
contentHeight,
containerWidth,
containerHeight,
maxContainerWidth,
scaleContainerWidth,
] );
Expand Down
Loading