添加定时器,让图像在 3 秒内自动更改
Add timer so that image changes automatically in 3 seconds
嘿伙计们,我想将计时器和这些手势一起添加到以下 react-spring 示例中。很长一段时间以来,我一直对此感到震惊。会有很大的帮助。此外,我正在使用来自 material ui 的 makestyles。如果您能告诉我如何将 css 转换为制作样式,那将是一个额外的帮助。 Link 到代码:https://codesandbox.io/embed/j0y0vpz59
import { render } from "react-dom";
import React, { useState, useEffect } from "react";
import { useSprings, animated, interpolate } from "react-spring";
import { useGesture } from "react-use-gesture";
import img1 from "./assets/slideImg1.jpg";
import img2 from "./assets/slideImg2.gif";
import img3 from "./assets/slideImg3.gif";
import img4 from "./assets/slideImg4.jpg";
import "./index.css";
const cards = [
img1,
img2,
img3,
img4,
"https://upload.wikimedia.org/wikipedia/en/thumb/8/88/RWS_Tarot_02_High_Priestess.jpg/690px-RWS_Tarot_02_High_Priestess.jpg",
"https://upload.wikimedia.org/wikipedia/en/d/de/RWS_Tarot_01_Magician.jpg",
];
// These two are just helpers, they curate spring data, values that are later being interpolated into css
const to = (i) => ({
x: 0,
y: i * -4,
scale: 1,
rot: -10 + Math.random() * 20,
delay: i * 100,
});
const from = (i) => ({ x: 0, rot: 0, scale: 1.5, y: -1000 });
// This is being used down there in the view, it interpolates rotation and scale into a css transform
const trans = (r, s) =>
`perspective(1500px) rotateX(30deg) rotateY(${
r / 10
}deg) rotateZ(${r}deg) scale(${s})`;
function Deck() {
const [gone] = useState(() => new Set()); // The set flags all the cards that are flicked out
const [fly, setFly] = useState(false);
const [props, set] = useSprings(cards.length, (i) => ({
...to(i),
from: from(i),
})); // Create a bunch of springs using the helpers above
// Create a gesture, we're interested in down-state, delta (current-pos - click-pos), direction and velocity
const bind = useGesture(
({
args: [index],
down,
delta: [xDelta],
distance,
direction: [xDir],
velocity,
}) => {
const trigger = velocity > 0.2; // If you flick hard enough it should trigger the card to fly out
const dir = xDir < 0 ? -1 : 1; // Direction should either point left or right
if (!down && trigger) gone.add(index); // If button/finger's up and trigger velocity is reached, we flag the card ready to fly out
set((i) => {
if (index !== i) return; // We're only interested in changing spring-data for the current spring
const isGone = gone.has(index);
const x = isGone ? (200 + window.innerWidth) * dir : down ? xDelta : 0; // When a card is gone it flys out left or right, otherwise goes back to zero
const rot = xDelta / 100 + (isGone ? dir * 10 * velocity : 0); // How much the card tilts, flicking it harder makes it rotate faster
const scale = down ? 1.1 : 1; // Active cards lift up a bit
return {
x,
rot,
scale,
delay: undefined,
config: { friction: 50, tension: down ? 800 : isGone ? 200 : 500 },
};
});
if (!down && gone.size === cards.length)
setTimeout(() => gone.clear() || set((i) => to(i)), 600);
}
);
useEffect(() => {
setFly(false);
const timerId = setInterval(() => {
setFly(true);
}, 1000);
return () => clearInterval(timerId);
}, [fly]);
// Now we're just mapping the animated values to our view, that's it. Btw, this component only renders once. :-)
return props.map(({ x, y, rot, scale }, i) => (
<animated.div
key={i}
style={{
transform: interpolate(
[x, y],
(x, y) => `translate3d(${x}px,${y}px,0)`
),
}}
>
{/* This is the card itself, we're binding our gesture to it (and inject its index so we know which is which) */}
<animated.div
{...bind(i)}
style={{
transform: interpolate([rot, scale], trans),
backgroundImage: `url(${cards[i]})`,
}}
/>
</animated.div>
));
}
render(<Deck />, document.getElementById("root"));
//css
* {
box-sizing: border-box;
}
html,
body {
overscroll-behavior-y: contain;
margin: 0;
padding: 0;
height: 100%;
width: 100%;
user-select: none;
font-family: -apple-system, BlinkMacSystemFont, avenir next, avenir,
helvetica neue, helvetica, ubuntu, roboto, noto, segoe ui, arial, sans-serif;
position: fixed;
overflow: hidden;
}
#root {
background: lightblue;
position: fixed;
overflow: hidden;
width: 100%;
height: 100%;
cursor: url("https://uploads.codesandbox.io/uploads/user/b3e56831-8b98-4fee-b941-0e27f39883ab/Ad1_-cursor.png")
39 39,
auto;
}
#root > div {
position: absolute;
width: 100vw;
height: 100vh;
will-change: transform;
display: flex;
align-items: center;
justify-content: center;
}
#root > div > div {
background-color: white;
background-size: auto 85%;
background-repeat: no-repeat;
background-position: center center;
width: 45vh;
max-width: 300px;
height: 85vh;
max-height: 570px;
will-change: transform;
border-radius: 10px;
box-shadow: 0 12.5px 100px -10px rgba(50, 50, 73, 0.4),
0 10px 10px -10px rgba(50, 50, 73, 0.3);
}
```[enter image description here][1]
[1]: https://i.stack.imgur.com/D77CT.png
这是一个很好的问题!
解决方案是添加一个 useEffect
挂钩调用 setInterval
定时器来更新弹簧。
一、代码沙箱:https://codesandbox.io/s/epic-mendeleev-w8zm7s?file=/src/index.js
这里是 useEffect 挂钩。请注意,回调 returns 是一个清除间隔和计时器的清理函数。
useEffect(() => {
const timers = [];
const interval = setInterval(() => {
const index = cards.length - gone.size - 1;
gone.add(index);
set((i) => {
if (index !== i) return;
const dir = i % 2 === 0 ? -1 : 1;
const velocity = 1;
return {
x: (200 + window.innerWidth) * dir,
rot: 0 / 100 + dir * 10 * velocity,
scale: 1.1,
delay: undefined,
config: { friction: 50, tension: 200 },
};
});
if (gone.size === cards.length) {
const timer = setTimeout(() => {
gone.clear();
set((i) => to(i));
}, 1000);
timers.push(timer);
}
}, 3000);
return () => {
clearInterval(interval);
timers.forEach((timer) => clearTimeout(timer));
};
}, []);
执行interval回调时,首先判断哪张卡片刷出屏幕。然后我们将其添加到 gone
集合中,该集合也与 useGesture
挂钩共享。这很重要,因为如果用户从屏幕上刷出一张卡片,我们要向前移动并刷下一张卡片。
const index = cards.length - gone.size - 1;
gone.add(index);
然后我们调用useSprings
给我们的set
回调。这就是动画的作用。我从 useGesture 中的设置回调中复制了大部分代码。
set((i) => {
if (index !== i) return;
const dir = i % 2 === 0 ? -1 : 1;
const velocity = 1;
return {
x: (200 + window.innerWidth) * dir,
rot: 0 / 100 + dir * 10 * velocity,
scale: 1.1,
delay: undefined,
config: { friction: 50, tension: 200 },
};
});
最后,如果我们到达列表的末尾,我们设置一个 1 秒的计时器,然后清除列表,重新开始。
if (gone.size === cards.length) {
const timer = setTimeout(() => {
gone.clear();
set((i) => to(i));
}, 1000);
timers.push(timer);
}
嘿伙计们,我想将计时器和这些手势一起添加到以下 react-spring 示例中。很长一段时间以来,我一直对此感到震惊。会有很大的帮助。此外,我正在使用来自 material ui 的 makestyles。如果您能告诉我如何将 css 转换为制作样式,那将是一个额外的帮助。 Link 到代码:https://codesandbox.io/embed/j0y0vpz59
import { render } from "react-dom";
import React, { useState, useEffect } from "react";
import { useSprings, animated, interpolate } from "react-spring";
import { useGesture } from "react-use-gesture";
import img1 from "./assets/slideImg1.jpg";
import img2 from "./assets/slideImg2.gif";
import img3 from "./assets/slideImg3.gif";
import img4 from "./assets/slideImg4.jpg";
import "./index.css";
const cards = [
img1,
img2,
img3,
img4,
"https://upload.wikimedia.org/wikipedia/en/thumb/8/88/RWS_Tarot_02_High_Priestess.jpg/690px-RWS_Tarot_02_High_Priestess.jpg",
"https://upload.wikimedia.org/wikipedia/en/d/de/RWS_Tarot_01_Magician.jpg",
];
// These two are just helpers, they curate spring data, values that are later being interpolated into css
const to = (i) => ({
x: 0,
y: i * -4,
scale: 1,
rot: -10 + Math.random() * 20,
delay: i * 100,
});
const from = (i) => ({ x: 0, rot: 0, scale: 1.5, y: -1000 });
// This is being used down there in the view, it interpolates rotation and scale into a css transform
const trans = (r, s) =>
`perspective(1500px) rotateX(30deg) rotateY(${
r / 10
}deg) rotateZ(${r}deg) scale(${s})`;
function Deck() {
const [gone] = useState(() => new Set()); // The set flags all the cards that are flicked out
const [fly, setFly] = useState(false);
const [props, set] = useSprings(cards.length, (i) => ({
...to(i),
from: from(i),
})); // Create a bunch of springs using the helpers above
// Create a gesture, we're interested in down-state, delta (current-pos - click-pos), direction and velocity
const bind = useGesture(
({
args: [index],
down,
delta: [xDelta],
distance,
direction: [xDir],
velocity,
}) => {
const trigger = velocity > 0.2; // If you flick hard enough it should trigger the card to fly out
const dir = xDir < 0 ? -1 : 1; // Direction should either point left or right
if (!down && trigger) gone.add(index); // If button/finger's up and trigger velocity is reached, we flag the card ready to fly out
set((i) => {
if (index !== i) return; // We're only interested in changing spring-data for the current spring
const isGone = gone.has(index);
const x = isGone ? (200 + window.innerWidth) * dir : down ? xDelta : 0; // When a card is gone it flys out left or right, otherwise goes back to zero
const rot = xDelta / 100 + (isGone ? dir * 10 * velocity : 0); // How much the card tilts, flicking it harder makes it rotate faster
const scale = down ? 1.1 : 1; // Active cards lift up a bit
return {
x,
rot,
scale,
delay: undefined,
config: { friction: 50, tension: down ? 800 : isGone ? 200 : 500 },
};
});
if (!down && gone.size === cards.length)
setTimeout(() => gone.clear() || set((i) => to(i)), 600);
}
);
useEffect(() => {
setFly(false);
const timerId = setInterval(() => {
setFly(true);
}, 1000);
return () => clearInterval(timerId);
}, [fly]);
// Now we're just mapping the animated values to our view, that's it. Btw, this component only renders once. :-)
return props.map(({ x, y, rot, scale }, i) => (
<animated.div
key={i}
style={{
transform: interpolate(
[x, y],
(x, y) => `translate3d(${x}px,${y}px,0)`
),
}}
>
{/* This is the card itself, we're binding our gesture to it (and inject its index so we know which is which) */}
<animated.div
{...bind(i)}
style={{
transform: interpolate([rot, scale], trans),
backgroundImage: `url(${cards[i]})`,
}}
/>
</animated.div>
));
}
render(<Deck />, document.getElementById("root"));
//css
* {
box-sizing: border-box;
}
html,
body {
overscroll-behavior-y: contain;
margin: 0;
padding: 0;
height: 100%;
width: 100%;
user-select: none;
font-family: -apple-system, BlinkMacSystemFont, avenir next, avenir,
helvetica neue, helvetica, ubuntu, roboto, noto, segoe ui, arial, sans-serif;
position: fixed;
overflow: hidden;
}
#root {
background: lightblue;
position: fixed;
overflow: hidden;
width: 100%;
height: 100%;
cursor: url("https://uploads.codesandbox.io/uploads/user/b3e56831-8b98-4fee-b941-0e27f39883ab/Ad1_-cursor.png")
39 39,
auto;
}
#root > div {
position: absolute;
width: 100vw;
height: 100vh;
will-change: transform;
display: flex;
align-items: center;
justify-content: center;
}
#root > div > div {
background-color: white;
background-size: auto 85%;
background-repeat: no-repeat;
background-position: center center;
width: 45vh;
max-width: 300px;
height: 85vh;
max-height: 570px;
will-change: transform;
border-radius: 10px;
box-shadow: 0 12.5px 100px -10px rgba(50, 50, 73, 0.4),
0 10px 10px -10px rgba(50, 50, 73, 0.3);
}
```[enter image description here][1]
[1]: https://i.stack.imgur.com/D77CT.png
这是一个很好的问题!
解决方案是添加一个 useEffect
挂钩调用 setInterval
定时器来更新弹簧。
一、代码沙箱:https://codesandbox.io/s/epic-mendeleev-w8zm7s?file=/src/index.js
这里是 useEffect 挂钩。请注意,回调 returns 是一个清除间隔和计时器的清理函数。
useEffect(() => {
const timers = [];
const interval = setInterval(() => {
const index = cards.length - gone.size - 1;
gone.add(index);
set((i) => {
if (index !== i) return;
const dir = i % 2 === 0 ? -1 : 1;
const velocity = 1;
return {
x: (200 + window.innerWidth) * dir,
rot: 0 / 100 + dir * 10 * velocity,
scale: 1.1,
delay: undefined,
config: { friction: 50, tension: 200 },
};
});
if (gone.size === cards.length) {
const timer = setTimeout(() => {
gone.clear();
set((i) => to(i));
}, 1000);
timers.push(timer);
}
}, 3000);
return () => {
clearInterval(interval);
timers.forEach((timer) => clearTimeout(timer));
};
}, []);
执行interval回调时,首先判断哪张卡片刷出屏幕。然后我们将其添加到 gone
集合中,该集合也与 useGesture
挂钩共享。这很重要,因为如果用户从屏幕上刷出一张卡片,我们要向前移动并刷下一张卡片。
const index = cards.length - gone.size - 1;
gone.add(index);
然后我们调用useSprings
给我们的set
回调。这就是动画的作用。我从 useGesture 中的设置回调中复制了大部分代码。
set((i) => {
if (index !== i) return;
const dir = i % 2 === 0 ? -1 : 1;
const velocity = 1;
return {
x: (200 + window.innerWidth) * dir,
rot: 0 / 100 + dir * 10 * velocity,
scale: 1.1,
delay: undefined,
config: { friction: 50, tension: 200 },
};
});
最后,如果我们到达列表的末尾,我们设置一个 1 秒的计时器,然后清除列表,重新开始。
if (gone.size === cards.length) {
const timer = setTimeout(() => {
gone.clear();
set((i) => to(i));
}, 1000);
timers.push(timer);
}