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;
当您按下圆圈按钮时,方框会向右移动并从屏幕上消失。
附加信息(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;