添加定时器,让图像在 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);
      }