[英]Animate SVG along path with Framer Motion
有没有办法使用 Framer Motion 沿路径(最好是 SVG 路径本身)为 SVG 元素设置动画? 此外,动画SVG元素是否可能轮流改变其旋转? 下面显示了我想要实现的示例(不完全是,只是一个示例):
https://tympanus.net/codrops/2019/12/03/motion-paths-past-present-and-future/
对于 Framer Motion 文档中给出的示例,我没有在声明性方式中找到任何类似的东西,但我想知道这是否可以通过 MotionValues、onUpdate 方法或命令式 AnimationControls 以某种方式实现?
您可以使用motion.path
为pathLength
属性设置动画。 然后将其与跟随路径的元素上的offsetDistance
配对。
import React from "react"
import { motion } from "framer-motion"
import "./styles.css"
const transition = { duration: 4, yoyo: Infinity, ease: "easeInOut" }
export default function App() {
return (
<div className="container">
<svg xmlns="http://www.w3.org/2000/svg" width="451" height="437">
<motion.path
d="M 239 17 C 142 17 48.5 103 48.5 213.5 C 48.5 324 126 408 244 408 C 362 408 412 319 412 213.5 C 412 108 334 68.5 244 68.5 C 154 68.5 102.68 135.079 99 213.5 C 95.32 291.921 157 350 231 345.5 C 305 341 357.5 290 357.5 219.5 C 357.5 149 314 121 244 121 C 174 121 151.5 167 151.5 213.5 C 151.5 260 176 286.5 224.5 286.5 C 273 286.5 296.5 253 296.5 218.5 C 296.5 184 270 177 244 177 C 218 177 197 198 197 218.5 C 197 239 206 250.5 225.5 250.5 C 245 250.5 253 242 253 218.5"
fill="transparent"
strokeWidth="12"
stroke="rgba(255, 255, 255, 0.69)"
strokeLinecap="round"
initial={{ pathLength: 0 }}
animate={{ pathLength: 1 }}
transition={transition}
/>
</svg>
<motion.div
className="box"
initial={{ offsetDistance: "0%", scale: 2.5 }}
animate={{ offsetDistance: "100%", scale: 1 }}
transition={transition}
/>
</div>
)
}
示例来源:Matt Perry: https://codesandbox.io/s/framer-motion-motion-along-a-path-41i3v
我不认为offset-path
在 Safari 上有效,所以我能够使用Framer Motion 的useMotionValue
钩子在没有offset-path
的情况下实现它。
首先是获取我们的进度路径的可能总长度。 然后,我们从运动侦听器中获取起始路径长度。 这是0开始。
现在我们将起始长度 (0) 乘以总路径长度以获得圆的起始 X 和 Y 坐标。
ProgressX 和 ProgressY 与圆心相连。
在这种情况下,它应该将我们的圆圈恰好放在路径的起点。
这样做允许圆圈通过修改运动侦听器的初始值从起点的任何距离开始。
然后,只听 progressLength 的更新,并在它发生变化时更新圆心。
import { motion, useMotionValue } from "framer-motion";
import { useRef, useEffect } from "react";
export default function App() {
const pathRefForeground = useRef(null);
const progressLength = useMotionValue(0);
const progressX = useMotionValue(0);
const progressY = useMotionValue(0);
useEffect(() => {
const pathElementForeground = pathRefForeground.current;
const totalPathLength = pathElementForeground.getTotalLength();
const initialProgress = progressLength.get();
const initialCoords = pathElementForeground.getPointAtLength(
initialProgress * totalPathLength
);
progressX.set(initialCoords.x);
progressY.set(initialCoords.y);
const unsubscribe = progressLength.onChange((latestPercent) => {
const latestPathProgress = pathElementForeground.getPointAtLength(
latestPercent * totalPathLength
);
progressX.set(latestPathProgress.x);
progressY.set(latestPathProgress.y);
});
return unsubscribe;
}, []);
const transition = {
repeat: Infinity,
bounce: 0.75,
type: "spring",
duration: 2
};
const progress = 50;
return (
<div
className="App"
style={{
minHeight: 500,
display: "flex",
flexDirection: "column",
justifyContent: "center",
alignItems: "center",
background: "#d9eefd"
}}
>
<motion.svg width="500" height="50" viewBox="0 0 500 30">
<path
stroke="white"
strokeWidth="10"
strokeLinecap="round"
d="M15 15 H490"
/>
<motion.path
d="M15 15 H490"
stroke="#1f88eb"
strokeWidth="10"
strokeLinecap="round"
ref={pathRefForeground}
pathLength={progressLength}
initial={{ pathLength: 0 }}
animate={{ pathLength: progress / 100 }}
transition={transition}
/>
<motion.circle cx={progressX} cy={progressY} r="15" fill="#1f88eb" />
<motion.circle cx={progressX} cy={progressY} r="5" fill="white" />
</motion.svg>
</div>
);
}
这是一个现场演示: https ://codesandbox.io/s/patient-sea-nbhs5u?file=/src/App.js
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.