如何创建具有不同 :root 变量值的 React 组件的多个实例(具有无限动画)

How can I create multiple instances of a React component (having an infinite animation) with different values for the :root variables

我对使用 Javascript + CSS + React 很陌生,而且卡得很厉害。感谢任何帮助!下面描述的是一个精简版,突出关键要求

我的反应组件的描述:

在屏幕上从A点垂直无限移动到B点的点。 A 点和 B 点是我屏幕平面上随机分配的点。这两个点具有相同的 x 坐标但不同的 y 坐标以启用垂直运动。所以每次从 A 到 B 的旅程都有三个随机参数:x、y1 和 y2。一旦到达 B,它会在 A 重新开始。

期望状态:

问 1 - 一旦点到达终点 B,我希望能够为我的组件重新随机化 x、y1 和 y2。

问 2 - 我需要这个组件的多个实例(比如 20 个),每个实例都有自己独立运行的随机事件。

我目前的做法:

第1步:在组件的css文件中,我用--x--y1--y2作为变量编写动画代码在:root class:

:root {                 /* Values assigned are just fallback values */
  --x: 100px;
  --y1: 10px;
  --y2: 400px;
}

.spark div {            /* spark is the `className` for the div that contains the dot (shown in Step 2) */
  height: 20px;
  width: 20px;
  border-radius: 50%;
  position: absolute;
  left: var(--x);
  top: var(--y2);
  background: rgba(0, 0, 0, 1);
  animation: myOrbit 15s linear infinite;
}

@keyframes myOrbit {
  0% {
    transform: translateY(var(--y1));
  }
  100% {
    transform: translateY(var(--y2));
  }
}

第 2 步:我在组件的 jsx 代码中使用 document.documentElement.style.setProperty("--x", x + "vw");,如下所示:

const Spark = () => {
  document.documentElement.style.setProperty("--x", Math.random()*500 + "px");
  document.documentElement.style.setProperty("--y1", Math.random()*500 + "px");
  document.documentElement.style.setProperty("--y2", Math.random()*500 + "px");

  return (
    <div className="spark">
      <div />
    </div>
  );
};

export default Spark;

第 3 步:我使用以下代码在 App.js 中调用此组件:

import Spark from "./components/Spark.js";

const App = () => {
  return (
      <div className="sparks">
        <Spark />
        <Spark />
        <Spark />
        <Spark />
        <Spark />
      </div>
  );
};

export default App;

第 4 步:我使用 index.js 和 index.html 将所有内容组合在一起:

src/index.js

import React from "react";
import ReactDOM from "react-dom";
import App from "./App.js";

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById("root")
);

public/index.html:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>visor</title>
  </head>
  <body style="margin: 0%; padding: 0%">
    <div id="root"></div>
  </body>
</html>

问题:

  1. Spark 组件的最后一个实例生成的随机 x、y1 和 y2 值将应用于所有先前的实例。所以我只得到 5 个重叠点而不是 5 个不同的点
  2. 我无法在后续的 A-B 循环之间更改 x、y1、y2。我什至不知道从哪里开始这样做

非常感谢您的帮助

让我们首先说明 React(开箱即用)没有直接的方法来添加从 React 组件获取变量的动画。
如果您打算做任何比这更复杂的事情,我强烈建议您查看有助于此的库。

您似乎面临的主要问题是如何为每个点应用动画。 这不会干扰其他点的逻辑。
为此,我们将为每个 Spark 创建一个单独的动画,并提供在组件中创建的随机 x、y1 和 y2。
您似乎面临的第二个问题是如何在动画完成后分配新的随机值,反应为此提供了钩子,我们可以使用 onAnimationIteration 触发一些代码来处理它。
In this codepen 你会发现整个作品一起工作。

import React from "react";

const Spark = () => {
  const name = `spark_${Math.random()}`.replace(".", ""); // create a random name
  const [x, setX] = React.useState(Math.random() * 500); // keep state for x
  const [y1, setY1] = React.useState(Math.random() * 500); // y1
  const [y2, setY2] = React.useState(Math.random() * 500); // and y2
  // build the animation string
  const animationStyle = `{ 0% {transform: translate(${x}px,${y1}px);} 100% {transform: translate(${x}px,${y2}px);} }`; 

  React.useEffect(() => { // useEffect runs whenever the provided dependancies change. in the effect well create the stylesheet and add it to the dom
    // Creating a style element, to add the keyframes
    const styleSheet = document.createElement("style");
    styleSheet.type = "text/css";
    styleSheet.id = x;
    document.head.appendChild(styleSheet);
    // Adding The Keyframes
    styleSheet.sheet.insertRule(
      `@keyframes ${name} ${animationStyle}`,
      styleSheet.length
    );
    // return a cleanup function for when the element unmounts
    return () => styleSheet.remove(); // this cleanup will remove the stylesheet when it's not needed anymore
  }, [animationStyle, name, x]);

  return (
    <div className="spark">
      <div
        style={{ //you can pass css styles to the dom via react,
          animation: `${name} 1s linear infinite`, // here we set the animation
          transform: `translate(${x}px, ${y1}px)` // here we provide some initial values. 
        }}
        onAnimationIteration={() => { // react can listen to dom event,  here we trigger some code eacht time the animation starts a new itteration
          setX(Math.random() * 500);  // set new values 
          setY1(Math.random() * 500);
          setY2(Math.random() * 500);
        }}
      />
    </div>
  );
};

export default Spark;

您可以通过返回一个组件数组来呈现倍数

import "./styles.css";
import Spark from "./spark";

export default function App() {
  return (
    <div className="sparks">
      {Array(20)
        .fill(0)
        .map((_, i) => (
          <Spark key={i} />
        ))}
    </div>
  );
}