使用Framer Motion沿路径动画SVG

3
有没有一种方法可以使用Framer Motion沿着路径(最好是SVG路径本身)动画化SVG元素?此外,动画化的SVG元素是否可以在转弯时改变其旋转方向?以下是我想要实现的示例(不完全是这样,只是一个示例):

https://tympanus.net/codrops/2019/12/03/motion-paths-past-present-and-future/

在Framer Motion文档中给出的示例中,我没有找到类似于声明式方法的内容,但我想知道是否可以通过MotionValues、onUpdate方法或命令式AnimationControls来实现这一点?

我现在在工作和8个项目中都使用了这个库,最接近的东西我能想到的就是使用https://www.framer.com/api/motion/animation/#animate。虽然没有明确的路径跟随功能,也绝对没有声明式的东西。 - undefined
2个回答

8

通过使用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


1
使用viewbox怎么样? - undefined

0

我还不相信offset-path在Safari上能正常工作,所以我能够使用Framer Motion的useMotionValue hook来实现这个功能。

首先,我们获取进度路径的总长度。然后,我们从运动监听器中获取起始路径长度。这是从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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接