Saya cuba melaksanakan animasi FLIP untuk melihat sama ada saya memahaminya dengan betul.
Dalam codepen ini (maafkan kod buruk, saya hanya mengacau), jika saya mengulas tidur, peralihan yang lancar tidak lagi berfungsi. Div bertukar kedudukan secara tiba-tiba. Ini pelik kerana masa tidur ialah 0ms.
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"))
Saya mengesyaki ini adalah sihir gelung peristiwa yang saya tidak faham. Bolehkah seseorang menjelaskan perkara ini kepada saya?
Anda menggunakan penyelesaian JavaScript biasa untuk masalah ini, tetapi React menggunakan DOM maya dan menjangkakan elemen DOM akan dipaparkan semula apabila keadaan berubah. Oleh itu, saya mengesyorkan memanfaatkan keadaan React untuk mengemas kini kedudukan XY elemen dalam DOM maya, tetapi masih menggunakan CSS.
Demo kerja di siniatau kod boleh didapati di sini:
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"}
Dalam sleep
期間,瀏覽器可能有時間重新計算 CSSOM 框(也稱為“執(zhí)行回流”)。沒有它,您的 transform
peraturan tidak benar-benar terpakai.
Malah, penyemak imbas akan menunggu sehingga ia benar-benar diperlukan untuk menggunakan perubahan anda dan mengemas kini keseluruhan model kotak halaman, kerana berbuat demikian boleh menjadi sangat mahal.
Apabila anda melakukan sesuatu seperti ini
element.style.color = "red"; element.style.color = "yellow"; element.style.color = "green";
Semua CSSOM akan melihat status terkini, “綠色”
. Dua lagi dibuang.
Jadi dalam kod anda, apabila anda tidak membiarkan gelung acara benar-benar gelung, anda tidak akan melihat nilai transStr
sama ada.
Namun, bergantung pada 0ms setTimeout
adalah panggilan masalah, tiada apa-apa untuk memastikan gaya dikira semula pada masa itu. Sebaliknya, lebih baik untuk memaksa pengiraan semula secara manual. Beberapa kaedah/sifat DOM melakukan ini secara serentak. Tetapi perlu diingat bahawa aliran semula boleh menjadi operasi yang sangat mahal, jadi pastikan anda menggunakannya sekali-sekala, dan jika terdapat berbilang tempat dalam kod anda yang memerlukan operasi ini, pastikan anda menyambung semuanya supaya aliran semula tunggal boleh dilakukan. 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; }