Pourquoi l'omission de 0 ms de veille interrompt-elle mes transitions CSS ?
P粉659516906
P粉659516906 2024-04-06 16:29:50
0
2
737

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粉659516906
P粉659516906

répondre à tous(2)
P粉022285768

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粉106711425

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


Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal