Skip to content

Commit

Permalink
Only animate the blocks that changed index (#17573)
Browse files Browse the repository at this point in the history
  • Loading branch information
youknowriad committed Sep 30, 2019
1 parent 0c9adfb commit 41228ed
Show file tree
Hide file tree
Showing 5 changed files with 48 additions and 89 deletions.
2 changes: 1 addition & 1 deletion packages/block-editor/src/components/block-list/block.js
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ function BlockListBlock( {
}, [ isFirstMultiSelected ] );

// Block Reordering animation
const animationStyle = useMovingAnimation( wrapper, isSelected || isPartOfMultiSelection, enableAnimation, animateOnChange );
const animationStyle = useMovingAnimation( wrapper, isSelected || isPartOfMultiSelection, isSelected || isFirstMultiSelected, enableAnimation, animateOnChange );

// Focus the breadcrumb if the wrapper is focused on navigation mode.
// Focus the first editable or the wrapper if edit mode.
Expand Down
4 changes: 2 additions & 2 deletions packages/block-editor/src/components/block-list/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ class BlockList extends Component {
className
)
}>
{ blockClientIds.map( ( clientId ) => {
{ blockClientIds.map( ( clientId, index ) => {
const isBlockInSelection = hasMultiSelection ?
multiSelectedBlockClientIds.includes( clientId ) :
selectedBlockClientId === clientId;
Expand All @@ -231,7 +231,7 @@ class BlockList extends Component {
// This prop is explicitely computed and passed down
// to avoid being impacted by the async mode
// otherwise there might be a small delay to trigger the animation.
animateOnChange={ blockClientIds }
animateOnChange={ index }
enableAnimation={ enableAnimation }
/>
</BlockAsyncModeProvider>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ import { useSpring, interpolate } from 'react-spring/web.cjs';
/**
* WordPress dependencies
*/
import { useState, useLayoutEffect, useReducer } from '@wordpress/element';
import { useState, useLayoutEffect, useReducer, useMemo } from '@wordpress/element';
import { useReducedMotion } from '@wordpress/compose';
import { getScrollContainer } from '@wordpress/dom';

/**
* Simple reducer used to increment a counter.
Expand All @@ -17,6 +18,13 @@ import { useReducedMotion } from '@wordpress/compose';
*/
const counterReducer = ( state ) => state + 1;

const getAbsolutePosition = ( element ) => {
return {
top: element.offsetTop,
left: element.offsetLeft,
};
};

/**
* Hook used to compute the styles required to move a div into a new position.
*
Expand All @@ -30,18 +38,25 @@ const counterReducer = ( state ) => state + 1;
*
* @param {Object} ref Reference to the element to animate.
* @param {boolean} isSelected Whether it's the current block or not.
* @param {boolean} adjustScrolling Adjust the scroll position to the current block.
* @param {boolean} enableAnimation Enable/Disable animation.
* @param {*} triggerAnimationOnChange Variable used to trigger the animation if it changes.
*
* @return {Object} Style object.
*/
function useMovingAnimation( ref, isSelected, enableAnimation, triggerAnimationOnChange ) {
function useMovingAnimation( ref, isSelected, adjustScrolling, enableAnimation, triggerAnimationOnChange ) {
const prefersReducedMotion = useReducedMotion() || ! enableAnimation;
const [ triggeredAnimation, triggerAnimation ] = useReducer( counterReducer, 0 );
const [ finishedAnimation, endAnimation ] = useReducer( counterReducer, 0 );
const [ transform, setTransform ] = useState( { x: 0, y: 0 } );
const [ transform, setTransform ] = useState( { x: 0, y: 0, scrollTop: 0 } );

const previous = ref.current ? ref.current.getBoundingClientRect() : null;
const previous = ref.current ? getAbsolutePosition( ref.current ) : null;
const scrollContainer = useMemo( () => {
if ( ! adjustScrolling ) {
return false;
}
return getScrollContainer( ref.current );
}, [ adjustScrolling ] );

useLayoutEffect( () => {
if ( triggeredAnimation ) {
Expand All @@ -50,29 +65,47 @@ function useMovingAnimation( ref, isSelected, enableAnimation, triggerAnimationO
}, [ triggeredAnimation ] );
useLayoutEffect( () => {
if ( prefersReducedMotion ) {
if ( adjustScrolling && scrollContainer ) {
// if the animation is disabled and the scroll needs to be adjusted,
// just move directly to the final scroll position
ref.current.style.transform = 'none';
const destination = getAbsolutePosition( ref.current );
scrollContainer.scrollTop = scrollContainer.scrollTop - previous.top + destination.top;
}

return;
}
ref.current.style.transform = 'none';
const destination = ref.current.getBoundingClientRect();
const destination = getAbsolutePosition( ref.current );
const newTransform = {
x: previous ? previous.left - destination.left : 0,
y: previous ? previous.top - destination.top : 0,
scrollTop: previous && scrollContainer ? scrollContainer.scrollTop - previous.top + destination.top : 0,
};
ref.current.style.transform = newTransform.x === 0 && newTransform.y === 0 ?
undefined :
`translate3d(${ newTransform.x }px,${ newTransform.y }px,0)`;
triggerAnimation();
setTransform( newTransform );
}, [ triggerAnimationOnChange ] );

const animationProps = useSpring( {
from: transform,
from: {
x: transform.x,
y: transform.y,
},
to: {
x: 0,
y: 0,
},
reset: triggeredAnimation !== finishedAnimation,
config: { mass: 5, tension: 2000, friction: 200 },
immediate: prefersReducedMotion,
onFrame: ( props ) => {
if ( adjustScrolling && scrollContainer && ! prefersReducedMotion && props.y ) {
scrollContainer.scrollTop = transform.scrollTop + props.y;
}
},
} );

// Dismiss animations if disabled.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,83 +1,11 @@
/**
* WordPress dependencies
*/
import { Component } from '@wordpress/element';
import { withSelect } from '@wordpress/data';
import { getScrollContainer } from '@wordpress/dom';
import deprecated from '@wordpress/deprecated';

/**
* Internal dependencies
*/
import { getBlockDOMNode } from '../../utils/dom';

/**
* Non-visual component which preserves offset of selected block within nearest
* scrollable container while reordering.
*
* @example
*
* ```jsx
* <PreserveScrollInReorder />
* ```
*/
class PreserveScrollInReorder extends Component {
getSnapshotBeforeUpdate( prevProps ) {
const { blockOrder, selectionStart } = this.props;
if ( blockOrder !== prevProps.blockOrder && selectionStart ) {
return this.getOffset( selectionStart );
}

return null;
}

componentDidUpdate( prevProps, prevState, snapshot ) {
if ( snapshot ) {
this.restorePreviousOffset( snapshot );
}
}

/**
* Given the block client ID of the start of the selection, saves the
* block's top offset as an instance property before a reorder is to occur.
*
* @param {string} selectionStart Client ID of selected block.
*
* @return {number?} The scroll offset.
*/
getOffset( selectionStart ) {
const blockNode = getBlockDOMNode( selectionStart );
if ( ! blockNode ) {
return null;
}

return blockNode.getBoundingClientRect().top;
}

/**
* After a block reordering, restores the previous viewport top offset.
*
* @param {number} offset The scroll offset.
*/
restorePreviousOffset( offset ) {
const { selectionStart } = this.props;
const blockNode = getBlockDOMNode( selectionStart );
if ( blockNode ) {
const scrollContainer = getScrollContainer( blockNode );
if ( scrollContainer ) {
scrollContainer.scrollTop = scrollContainer.scrollTop +
blockNode.getBoundingClientRect().top - offset;
}
}
}

render() {
return null;
}
export default function PreserveScrollInReorder() {
deprecated( 'PreserveScrollInReorder component', {
hint: 'This behavior is now built-in the block list',
} );
return null;
}

export default withSelect( ( select ) => {
return {
blockOrder: select( 'core/block-editor' ).getBlockOrder(),
selectionStart: select( 'core/block-editor' ).getBlockSelectionStart(),
};
} )( PreserveScrollInReorder );
2 changes: 0 additions & 2 deletions packages/edit-post/src/components/layout/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import {
navigateRegions,
} from '@wordpress/components';
import { __ } from '@wordpress/i18n';
import { PreserveScrollInReorder } from '@wordpress/block-editor';
import {
AutosaveMonitor,
LocalAutosaveMonitor,
Expand Down Expand Up @@ -88,7 +87,6 @@ function Layout( {
tabIndex="-1"
>
<EditorNotices />
<PreserveScrollInReorder />
<EditorModeKeyboardShortcuts />
<KeyboardShortcutHelpModal />
<ManageBlocksModal />
Expand Down

0 comments on commit 41228ed

Please sign in to comment.