<div id="root" />
.container {
display: flex;
justify-content: space-between;
}
.square {
width: 100px;
height: 100px;
display: flex;
justify-content: center;
align-items: center;
font-size: 20px;
color: white;
background-color: black;
user-select: none;
}
const Flipper = () => {
const [ids, setIds] = React.useState(["square-1", "square-2"]);
const rects = React.useRef(new Map()).current;
const swap = ([a, b]) => [b, a];
React.useEffect(() => {
const squares = document.querySelectorAll(".square");
// Cache position and size once on initial render
for (const square of squares) {
rects.set(square.id, square.getBoundingClientRect());
}
}, []);
React.useLayoutEffect(() => {
const squares = document.querySelectorAll(".square");
for (const square of squares) {
// Get previous size and position from cache
const cachedRect = rects.get(square.id);
if (cachedRect) {
const nextRect = square.getBoundingClientRect();
// Invert
const translateX = cachedRect.x - nextRect.x;
// Cache the next size and position
rects.set(square.id, nextRect);
// Play
square.animate(
[
{ transform: `translateX(${translateX}px)` },
{ transform: `translateX(0px)` }
],
1000
);
}
}
}, ids);
return (
<div className="container">
{ids.map((id, i) => {
return (
<div id={id} onClick={() => setIds(swap(ids))} className={`square`}>
{id}
</div>
);
})}
</div>
);
};
ReactDOM.render(<Flipper />, document.querySelector("#root"));
View Compiled
This Pen doesn't use any external CSS resources.