Maison > Questions et réponses > le corps du texte
J'essaie d'implémenter l'animation FLIP pour voir si je la comprends bien.
Dans ce codepen (excusez le mauvais code, je ne fais que déconner), si je commente le sommeil, la transition en douceur ne fonctionne plus. Le div change brusquement de position. C'est étrange car le temps de veille est de 0 ms.
import React, { useRef, useState } from "https://esm.sh/react@18"; import ReactDOM from "https://esm.sh/react-dom@18"; let first = {} let second = {} const sleep = async (ms) => new Promise((resolve) => setTimeout(resolve, ms)); const App = () => { const [start, setStart] = useState(true); const boxRefCb = async el => { if (!el) return; el.style.transition = ""; const x = parseInt(el?.getBoundingClientRect().x, 10); const y = parseInt(el?.getBoundingClientRect().y, 10); first = { x: second.x, y: second.y }; second = { x, y }; const dx = first.x - second.x; const dy = first.y - second.y; const transStr = `translate(${dx}px, ${dy}px)`; el.style.transform = transStr; await sleep(0); // comment me out el.style.transition = "transform .5s"; el.style.transform = ""; } return ( <> <div style={{ display: "flex", gap: "1rem", padding: "3rem"}}> <div ref={ start ? boxRefCb : null } style={{ visibility: start ? "" : "hidden", width: 100, height: 100, border: "solid 1px grey" }}></div> <div ref={ !start ? boxRefCb : null } style={{ visibility: !start ? "" : "hidden", width: 100, height: 100, border: "solid 1px grey" }}></div> </div> <button style={{ marginLeft: "3rem"}} onClick={() => setStart(start => !start)}>start | {start.toString()}</button> </> ); } ReactDOM.render(<App />, document.getElementById("root"))
Je soupçonne qu'il s'agit d'une magie de boucle d'événements que je ne comprends pas. Quelqu'un peut m'expliquer cela?
P粉0222857682024-04-07 14:25:14
Vous utilisez une solution JavaScript normale à ce problème, mais React utilise un DOM virtuel et s'attend à ce que les éléments du DOM soient restitués lorsque l'état change. Par conséquent, je recommande d'utiliser l'état React pour mettre à jour la position XY des éléments dans le DOM virtuel, tout en utilisant CSS.
Démo fonctionnelle iciou le code peut être trouvé ici :
import { useState, useRef, useLayoutEffect } from "react"; import "./styles.css"; type BoxXYPosition = { x: number; y: number }; export default function App() { const startBox = useRef(null); const startBoxPosition = useRef ({ x: 0, y: 0 }); const endBox = useRef (null); const [boxPosition, setBoxPosition] = useState ({ x: 0, y: 0 }); const { x, y } = boxPosition; const hasMoved = Boolean(x || y); const updatePosition = () => { if (!endBox.current) return; const { x: endX, y: endY } = endBox.current.getBoundingClientRect(); const { x: startX, y: startY } = startBoxPosition.current; // "LAST" - calculate end position const moveXPosition = endX - startX; const moveYPosition = endY - startY; // "INVERT" - recalculate position based upon current x,y coords setBoxPosition((prevState) => ({ x: prevState.x !== moveXPosition ? moveXPosition : 0, y: prevState.y !== moveYPosition ? moveYPosition : 0 })); }; useLayoutEffect(() => { // "FIRST" - save starting position if (startBox.current) { const { x, y } = startBox.current.getBoundingClientRect(); startBoxPosition.current = { x, y }; } }, []); // "PLAY" - switch between start and end animation via the x,y state and a style property return ( ); } Transition Between Points
{hasMoved ? "End" : "Start"}
P粉1067114252024-04-07 09:16:56
En sleep
期间,浏览器可能有时间重新计算 CSSOM 框(也称为“执行回流”)。没有它,您的 transform
, les règles ne s’appliquent pas vraiment.
En fait, le navigateur attendra jusqu'à ce qu'il soit vraiment nécessaire pour appliquer vos modifications et mettre à jour l'intégralité du modèle de zone de page, car cela peut être très coûteux.
Quand tu fais quelque chose comme ça
element.style.color = "red"; element.style.color = "yellow"; element.style.color = "green";
Tous les CSSOM verront le dernier statut, “绿色”
. Les deux autres ont été écartés.
Donc, dans votre code, lorsque vous ne laissez pas la boucle de l'événement se boucler, vous ne verrez jamais non plus la valeur transStr
.
Cependant, s'appuyer sur 0ms setTimeout
est un appel problématique, rien ne garantit que le style est recalculé à ce moment-là. Au lieu de cela, il est préférable de forcer un recalcul manuellement. Certaines méthodes/propriétés DOM le font de manière synchrone. Mais gardez à l'esprit que la redistribution peut être une opération très coûteuse, alors assurez-vous de l'utiliser de temps en temps, et s'il y a plusieurs endroits dans votre code qui nécessitent cette opération, assurez-vous de tous les connecter afin qu'une seule redistribution puisse être effectuée. p>
const el = document.querySelector(".elem"); const move = () => { el.style.transition = ""; const transStr = `translate(150px, 0px)`; el.style.transform = transStr; const forceReflow = document.querySelector("input").checked; if (forceReflow) { el.offsetWidth; } el.style.transition = "transform .5s"; el.style.transform = ""; } document.querySelector("button").onclick = move;
.elem { width: 100px; height: 100px; border: 1px solid grey; } .parent { display: flex; padding: 3rem; }