@awsui/components-react
Version:
On July 19th, 2022, we launched [Cloudscape Design System](https://cloudscape.design). Cloudscape is an evolution of AWS-UI. It consists of user interface guidelines, front-end components, design resources, and development tools for building intuitive, en
99 lines (97 loc) • 4.63 kB
JavaScript
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
export function getDOMRects(elements) {
const rects = {};
for (const id in elements) {
const element = elements[id];
if (element) {
rects[id] = element.getBoundingClientRect();
}
}
return rects;
}
/*
Animate DOM elements based on the FLIP technique
- https://aerotwist.com/blog/flip-your-animations/
- https://css-tricks.com/animating-layouts-with-the-flip-technique/
This can be useful when the initial dimensions or position of the element is not known,
so the initial offset or scaling needs to be retrieved via JS.
Caveat: this currently does not support elements having CSS transforms in the end state.
These would be overridden in the animation instead of combined.
*/
export function animate({ oldState, elements, onTransitionsEnd, newElementInitialState, }) {
// First, apply the transform that will make the elements "look like" in the start position
for (const id in elements) {
const element = elements[id];
const oldRect = oldState[id];
if (element) {
const newRect = element.getBoundingClientRect();
const noOpTransform = { scale: 1, x: 0, y: 0 };
// Calculate initial position.
// If the element didn't exist previously, use the newElementInitialState function if provided.
// If not, default to no transitions (scale: 1, y: 0)
const calculatedInverseTransform = oldRect
? {
scale: oldRect.width / newRect.width,
x: (oldRect.left + oldRect.right) / 2 - (newRect.left + newRect.right) / 2,
y: (oldRect.top + oldRect.bottom) / 2 - (newRect.top + newRect.bottom) / 2,
}
: newElementInitialState
? newElementInitialState(newRect)
: {};
const inverseTransform = Object.assign(Object.assign({}, noOpTransform), calculatedInverseTransform);
// Apply this initial change, without animating
element.style.transitionProperty = 'none';
element.style.transform = `scale(${inverseTransform.scale}) translate(${inverseTransform.x}px, ${inverseTransform.y}px)`;
if (!oldRect) {
// If the element didn't exist, then fade it in
// (besides any other possibly defined transitions based on `newElementInitialState`)
element.style.opacity = '0';
}
}
}
// Animate from the initial state to the end state
requestAnimationFrame(() => {
const ongoingAnimations = new Set();
for (const id in elements) {
const element = elements[id];
if (element) {
const oldRect = oldState[id];
if (oldRect) {
// Animate from here on
element.style.transitionProperty = `transform`;
// Unset inline CSS transforms so that the final state is applied
element.style.transform = '';
}
else {
// If the element didn't exist previously, fade in as well
element.style.transitionProperty = `transform, opacity`;
element.style.transform = '';
element.style.opacity = '';
}
const onTransitionStart = (event) => {
if (event.target === element) {
ongoingAnimations.add(id);
element.removeEventListener('transitionstart', onTransitionStart);
}
};
const onTransitionEnd = (event) => {
if (event.target === element) {
// Clean up remaining inline styles
element.style.transitionProperty = '';
element.removeEventListener('transitionend', onTransitionEnd);
if (onTransitionsEnd) {
ongoingAnimations.delete(id);
if (ongoingAnimations.size === 0) {
onTransitionsEnd();
}
}
}
};
element.addEventListener('transitionstart', onTransitionStart);
element.addEventListener('transitionend', onTransitionEnd);
}
}
});
}
//# sourceMappingURL=animate.js.map