Mengapa meninggalkan 0ms tidur mematahkan peralihan css saya?
P粉659516906
P粉659516906 2024-04-06 16:29:50
0
2
735

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?

P粉659516906
P粉659516906

membalas semua(2)
P粉022285768

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"}
); }
P粉106711425

Dalam sleep 期间,浏览器可能有时间重新计算 CSSOM 框(也称为“执行回流”)。没有它,您的 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;
}


Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan