如何创建具有不同 :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>
问题:
- Spark 组件的最后一个实例生成的随机 x、y1 和 y2 值将应用于所有先前的实例。所以我只得到 5 个重叠点而不是 5 个不同的点
- 我无法在后续的 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>
);
}
我对使用 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>
问题:
- Spark 组件的最后一个实例生成的随机 x、y1 和 y2 值将应用于所有先前的实例。所以我只得到 5 个重叠点而不是 5 个不同的点
- 我无法在后续的 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>
);
}