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

Add useScaleCanvas: Web Animations API for zoom in/out animation #66917

Merged
merged 64 commits into from
Nov 26, 2024
Merged
Changes from 3 commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
af08ecc
Midway commit. Scaling out is broken
jeryj Oct 30, 2024
c83c99f
Fix zoom in animation
jeryj Oct 30, 2024
ec58902
Fix scaling by preserving CSS properites
jeryj Oct 30, 2024
6582cce
Prevent reflow from removing and re adding scrollbar in animation
jeryj Oct 30, 2024
ce9497e
Only rerun zoom out use effects if zoom out has changed.
jeryj Oct 31, 2024
26d9e3a
Allow all CSS vars to update when scale changes
jeryj Oct 31, 2024
3947ab0
Midway commit. Math is wrong for addressing top/bottom exceptions
jeryj Oct 31, 2024
cb47e82
Remove usePrevious usage
ajlende Oct 31, 2024
bc8cca6
Add prefers-reduced-motion to setTimeout delay
ajlende Nov 5, 2024
578c923
WIP Working zoom without frame size
ajlende Nov 5, 2024
d2c16a6
Account for changes to client height when determining edge threshold
jeryj Nov 6, 2024
0faf418
Zoom to center unless it will reveal top or bottom
jeryj Nov 6, 2024
ab8038e
Account for a top threshold when zooming in and out
jeryj Nov 6, 2024
ba32f99
Clean up math and add comments
ajlende Nov 6, 2024
0b640ee
Add event listener instead of timeout
ajlende Nov 6, 2024
6a3f7a0
Fix reduced motion
ajlende Nov 6, 2024
55df6fe
Remove timeout ref
ajlende Nov 6, 2024
7ba67b2
Refactor callback as separate effect
ajlende Nov 6, 2024
2dc137f
Try to add back useEffect cleanups
ajlende Nov 6, 2024
3eabcf5
Initialize prevClientHeight in the useEffect
ajlende Nov 7, 2024
8b8c206
use useReducedMotion
jeryj Nov 7, 2024
23bd453
Add test for zoom in/out location
jeryj Nov 7, 2024
8cbe2be
Hack to fix reduced motion
ajlende Nov 8, 2024
4c9543e
Clean up the frameSizeValue and scaleValue calculations
ajlende Nov 8, 2024
4e87729
Replace TODO comments with HACK comments
ajlende Nov 8, 2024
17f50d5
Add cleanup for raf
ajlende Nov 8, 2024
b049f4b
Simplify CSS diff for 6.7 review
ajlende Nov 8, 2024
61a0de9
Add one more HACK comment
ajlende Nov 8, 2024
ac52f2d
Do not allow scrollTopNext to be smaller than 0
jeryj Nov 11, 2024
6b23a30
Fix zoom-out.spec.js
ajlende Nov 19, 2024
fd74dc4
Move 6.7 iframe scaling work to trunk
jeryj Nov 13, 2024
bfaf7e4
Move calculations to useScaleCanvas hook
jeryj Nov 13, 2024
cc28155
Rename scaleValue to scale
jeryj Nov 13, 2024
d407ee6
Rename frameSizeValue to frameSize
jeryj Nov 13, 2024
c8f2cc7
Remove iframeWindowInnerHeight
jeryj Nov 13, 2024
7903702
Remove unused window resize ref
jeryj Nov 13, 2024
72f5785
Refactor CSS
jeryj Nov 13, 2024
53d8f6f
Move animation to state
jeryj Nov 13, 2024
a25f7da
Fix scaling the canvas when sidebars open/close
jeryj Nov 13, 2024
5fcd9c1
Recalculate scale before exiting scaled canvas to prevent snapping ef…
jeryj Nov 14, 2024
1c5d7d5
CSS linting
jeryj Nov 14, 2024
96d62ab
Move code into useScaleCanvas that isn't necessary in iframe index
jeryj Nov 14, 2024
ec37f26
Move resize listeners into useScaledCanvas
jeryj Nov 14, 2024
28cff48
Combine into one useEffect
jeryj Nov 15, 2024
28ded6a
Add useEffect cleanup
jeryj Nov 15, 2024
1dbdc82
Fix accidental setting prevScaleRef to scale instead of scaleValue
jeryj Nov 19, 2024
cf8ffde
Switch to animations api
jeryj Nov 19, 2024
f338821
Reduce rerenders by removing isAnimatingZoomOut state and relying jus…
jeryj Nov 19, 2024
5c93399
Refactor to set scale and frame size on transition end
jeryj Nov 19, 2024
ff03e1e
Add reverse animation if zoom state is toggled quickly
jeryj Nov 20, 2024
65bd82e
Refactor step 1 of reverse animation with transitionTo and transition…
jeryj Nov 20, 2024
f236641
Finish refactor of transitionTo/From refs
jeryj Nov 20, 2024
83ee4c5
Refactor: computeScrollTopNext
jeryj Nov 20, 2024
32b24e5
Refactor: getAnimationKeyframes
jeryj Nov 20, 2024
1407f8f
Add missing dependency
jeryj Nov 20, 2024
d5402bc
Refactor: create startZoomOutAnimation and rename scrollTopNext to tr…
jeryj Nov 20, 2024
fc6b4ee
Remove unneeded variables
jeryj Nov 20, 2024
2325135
Move CSS back to original location, as it no longer needs to be moved
jeryj Nov 21, 2024
b41d282
Add comments
jeryj Nov 21, 2024
0ff92b5
Fix eslint error
ajlende Nov 25, 2024
92e0e52
Move scrollHeight to animateFrom state
ajlende Nov 25, 2024
fb16c4f
Update JSDoc
ajlende Nov 25, 2024
4076a7b
Rename ref variables to use "Ref" suffix
ajlende Nov 26, 2024
798549e
Comment style guide
ajlende Nov 26, 2024
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
73 changes: 47 additions & 26 deletions packages/block-editor/src/components/iframe/use-scale-canvas.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,24 @@
import { useEffect, useRef, useCallback } from '@wordpress/element';
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.
*/

/**
* Calculate the scale of the canvas.
*
* @param {Object} root0 Object of options
* @param {number} root0.frameSize The size of the frame/offset around the canvas
* @param {number} root0.containerWidth Actual width of the canvas container
* @param {number} root0.maxContainerWidth Maximum width of the container to use for the scale calculation. This locks the canvas to a maximum width when zooming out.
* @param {number} root0.scaleContainerWidth Width the of the container wrapping the canvas container
* @return {number} scale value between 0 and/or equal to 1
* @param {Object} options Object of options
* @param {number} options.frameSize Size of the frame/offset around the canvas
* @param {number} options.containerWidth Actual width of the canvas container
* @param {number} options.maxContainerWidth Maximum width of the container to use for the scale calculation. This locks the canvas to a maximum width when zooming out.
* @param {number} options.scaleContainerWidth Width the of the container wrapping the canvas container
* @return {number} Scale value between 0 and/or equal to 1
*/
function calculateScale( {
frameSize,
Expand All @@ -29,17 +38,17 @@ function calculateScale( {
/**
* Compute the next scrollTop position after scaling the iframe content.
*
* @param {number} scrollHeight Scaled height of the current iframe content.
* @param {Object} transitionFrom The starting point of the transition
* @param {Object} transitionTo The ending state of the transition
* @return {number} The next scrollTop position after scaling the iframe content.
* @param {TransitionState} transitionFrom Starting point of the transition
* @param {TransitionState} transitionTo Ending state of the transition
* @return {number} Next scrollTop position after scaling the iframe content.
*/
function computeScrollTopNext( scrollHeight, transitionFrom, transitionTo ) {
jeryj marked this conversation as resolved.
Show resolved Hide resolved
function computeScrollTopNext( transitionFrom, transitionTo ) {
const {
clientHeight: prevClientHeight,
frameSize: prevFrameSize,
scaleValue: prevScale,
scrollTop: scrollTop,
scrollTop,
scrollHeight,
} = transitionFrom;
const { clientHeight, frameSize, scaleValue } = transitionTo;
// Step 0: Start with the current scrollTop.
Expand Down Expand Up @@ -82,9 +91,9 @@ function computeScrollTopNext( scrollHeight, transitionFrom, transitionTo ) {
/**
* Generate the keyframes to use for the zoom out animation.
*
* @param {Object} transitionFrom Object of the starting transition state
* @param {Object} transitionTo Object of the ending transition state
* @return {Object[]} An array of keyframes to use for the animation
* @param {TransitionState} transitionFrom Starting transition state.
* @param {TransitionState} transitionTo Ending transition state.
* @return {Object[]} An array of keyframes to use for the animation.
*/
function getAnimationKeyframes( transitionFrom, transitionTo ) {
const {
Expand All @@ -110,20 +119,24 @@ function getAnimationKeyframes( transitionFrom, transitionTo ) {
];
}

/**
* @typedef {Object} ScaleCanvasResult
* @property {boolean} isZoomedOut A boolean indicating if the canvas is zoomed out.
* @property {number} scaleContainerWidth The width of the container used to calculate the scale.
* @property {Object} contentResizeListener A resize observer for the content.
* @property {Object} containerResizeListener A resize observer for the container.
*/

/**
* Handles scaling the canvas for the zoom out mode and animating between
* the states.
*
* @param {Object} root0
* @param {number} root0.frameSize The size of the frame around the content.
* @param {Document} root0.iframeDocument The document of the iframe.
* @param {number} root0.maxContainerWidth The max width of the canvas to use as the starting scale point. Defaults to 750.
* @param {number|string} root0.scale The scale of the canvas. Can be an decimal between 0 and 1, 1, or 'auto-scaled'.
* @return {Object} An object containing the following properties:
* isZoomedOut: A boolean indicating if the canvas is zoomed out.
* scaleContainerWidth: The width of the container used to calculate the scale.
* contentResizeListener: A resize observer for the content.
* containerResizeListener: A resize observer for the container.
* @param {Object} options Object of options.
* @param {number} options.frameSize Size of the frame around the content.
* @param {Document} options.iframeDocument Document of the iframe.
* @param {number} options.maxContainerWidth Max width of the canvas to use as the starting scale point. Defaults to 750.
* @param {number|string} options.scale Scale of the canvas. Can be an decimal between 0 and 1, 1, or 'auto-scaled'.
* @return {ScaleCanvasResult} Properties of the result.
*/
export function useScaleCanvas( {
frameSize,
Expand Down Expand Up @@ -168,22 +181,26 @@ export function useScaleCanvas( {

/**
* The starting transition state for the zoom out animation.
* @type {import('react').RefObject<TransitionState>}
*/
const transitionFrom = useRef( {
scaleValue,
frameSize,
clientHeight: 0,
scrollTop: 0,
scrollHeight: 0,
} );

/**
* The ending transition state for the zoom out animation.
* @type {import('react').RefObject<TransitionState>}
*/
const transitionTo = useRef( {
scaleValue,
frameSize,
clientHeight: 0,
scrollTop: 0,
scrollHeight: 0,
} );

/**
Expand Down Expand Up @@ -246,6 +263,9 @@ export function useScaleCanvas( {
iframeDocument.documentElement.classList.remove( 'zoom-out-animation' );

// Set the final scroll position that was just animated to.
// Disable reason: Eslint isn't smart enough to know that this is a
// DOM element. https://github.com/facebook/react/issues/31483
// eslint-disable-next-line react-compiler/react-compiler
iframeDocument.documentElement.scrollTop =
transitionTo.current.scrollTop;

Expand Down Expand Up @@ -381,14 +401,15 @@ export function useScaleCanvas( {
transitionFrom.current.clientHeight ?? clientHeight;
transitionFrom.current.scrollTop =
iframeDocument.documentElement.scrollTop;
transitionFrom.current.scrollHeight =
iframeDocument.documentElement.scrollHeight;

transitionTo.current = {
scaleValue,
frameSize,
clientHeight,
};
transitionTo.current.scrollTop = computeScrollTopNext(
iframeDocument.documentElement.scrollHeight,
transitionFrom.current,
transitionTo.current
);
Expand Down
Loading