react 滑动动画会执行两次

react The swipe animation will be performed twice

当您按下圆圈按钮时,方框会向右移动并从屏幕上消失。

附加信息(FW/tool版本等)
反应
scss
打字稿
成帧器运动

import "./style.scss";
import React, { FunctionComponent, useState } from "react";
import { useMotionValue, useTransform } from "framer-motion";
import { SwipeBox } from "./SwipeBox";
const App: FunctionComponent = () => {
  const [cards, setCards] = useState([
 



  const onClic = () => {
    animateCardSwipe({ x: -1400, y: 0 });
  };              <div
                style={{
                  width: "400px",
                  height: "300px",
                  background: `${card.background}`
                }}
              >
          ) : (
    </div>
  );
};

export default App;

问题是动画是在挂载时发生的,而您在 animateCardSwipe 函数中更新了两次状态:

const animateCardSwipe = (animation: { x: number, y: number }) => {
  setAnime({ ...anime, animation });

  setTimeout(() => {
    x.set(0);
    y.set(0);
    setCards([...cards.slice(0, cards.length - 1)]);
  }, 200);
};

我个人喜欢在这里使用更命令式的方法来启动动画 animate:

const animateCardSwipe = (animation: { x: number, y: number }) => {
  animate(x, animation.x, {
    duration: 1,
    onComplete: () => {
      x.set(0);
      y.set(0);
      setCards([...cards.slice(0, cards.length - 1)]);
    },
  });
};

此实现还在重新排列卡片之前等待动画完成。


完整示例:

import React, { FunctionComponent, useState } from "react";
import {
  animate,
  useAnimation,
  useMotionValue,
  useTransform,
  MotionValue,
  motion,
} from "framer-motion";

interface Props {
  animate?: { x: number; y: number };
  style: {
x?: MotionValue;
y?: MotionValue;
zIndex: number;
rotate?: MotionValue;
  };
  card: { text: string; background: string };
}

const SwipeBox: React.FunctionComponent<Props> = (props) => {
  return (
<motion.div
  animate={props.animate}
  className="card"
  style={{
    ...props.style,
    background: "white",
    borderRadius: "8px",
    display: "grid",
    top: 0,
    left: 0,
    placeItems: "center center",
    position: "absolute",
    width: "100%",
  }}
  transition={{ duration: 1 }}
>
  {props.children}
</motion.div>
  );
};

const App: FunctionComponent = () => {
  const controls = useAnimation();
  console.log(controls);
  const [cards, setCards] = useState([
{ text: "Up or down", background: "red" },
{ text: "Left or right", background: "green" },
{ text: "Swipe me!", background: "gray" },
{ text: "Swipe me!", background: "purple" },
{ text: "Swipe me!", background: "yellow" },
  ]);

  const x = useMotionValue(0);
  const y = useMotionValue(0);

  const animateCardSwipe = (animation: { x: number; y: number }) => {
animate(x, animation.x, {
  duration: 1,
  onComplete: () => {
    x.set(0);
    y.set(0);
    setCards([...cards.slice(0, cards.length - 1)]);
  },
});
  };

  const [anime, setAnime] = useState<{ animation: { x: number; y: number } }>({
animation: { x: 0, y: 0 },
  });

  const rotate = useTransform(x, [-950, 950], [-30, 30]);

  const onClickLeft = () => {
animateCardSwipe({ x: 1400, y: 0 });
  };

  const onClickRight = () => {
animateCardSwipe({ x: -1400, y: 0 });
  };

  return (
<div style={{ display: "flex", flexDirection: "column" }}>
  <div>
    {cards.map((card, index) =>
      index === cards.length - 1 ? (
        <SwipeBox
          animate={anime.animation}
          card={card}
          key={index}
          style={{ x, y, zIndex: index, rotate: rotate }}
        >
          <div
            style={{
              width: "400px",
              height: "300px",
              background: `${card.background}`,
            }}
          >
            {card.text}
          </div>
        </SwipeBox>
      ) : (
        <SwipeBox
          card={card}
          key={index}
          style={{
            zIndex: index,
          }}
        >
          <div
            style={{
              width: "400px",
              height: "300px",
              background: `${card.background}`,
            }}
          >
            {card.text}
          </div>
        </SwipeBox>
      )
    )}
  </div>
  <div style={{ zIndex: 999 }}>
    <button onClick={onClickRight}>✖️</button>
    <button onClick={onClickLeft}>○</button>
  </div>
</div>
  );
};

export default App;

Codesandbox Demo