recherche

Maison  >  Questions et réponses  >  le corps du texte

Pourquoi l'omission de 0 ms de veille interrompt-elle mes transitions CSS ?

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粉659516906P粉659516906350 Il y a quelques jours927

répondre à tous(2)je répondrai

  • P粉022285768

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

    répondre
    0
  • P粉106711425

    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.

    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;
    }
    
    
    

    répondre
    0
  • Annulerrépondre