所有分类
  • 所有分类
  • Html5资源
  • React资源
  • Vue资源
  • Php资源
  • ‌小程序资源
  • Python资源

Anime.js超级炫酷的网页动画库之SVG路径动画

简介

Anime.js 是一个强大且易用的 JavaScript 动画库,支持对 DOM、SVG、CSS 属性等多种对象进行高性能动画处理。它拥有丰富的缓动函数、时间线控制、序列动画、SVG 路径动画等特性,适用于网页交互动效、数据可视化、图标动画等多种场景。通过 Anime.js,你可以用极简的代码实现复杂的动画效果,提升网页的视觉表现力和用户体验。

安装

npm install animejs

Anime.js 也可以非常方便地为 SVG 元素添加动画,支持路径、形状、颜色等多种属性的动画。下面是一个 SVG 路径动画的示例:

示例

SVG 方 → 星

import { useEffect, useRef } from "react";
import { animate } from "animejs";

const points = 16;
const radius = 70;
const center = 100;

// 生成圆
function getCircle() {
  return Array.from({ length: points }, (_, i) => {
    const angle = (2 * Math.PI * i) / points - Math.PI / 2;
    return [
      center + radius * Math.cos(angle),
      center + radius * Math.sin(angle),
    ];
  });
}

// 生成“圆角方形”,点分布和圆一致,只是半径在四个角缩小
function getSquare() {
  return Array.from({ length: points }, (_, i) => {
    const angle = (2 * Math.PI * i) / points - Math.PI / 2;
    // 0,4,8,12是边的中点,其他是角
    const isCorner = i % 4 !== 0;
    const r = isCorner ? radius * 0.55 : radius;
    return [center + r * Math.cos(angle), center + r * Math.sin(angle)];
  });
}

// 生成星形
function getStar() {
  const r1 = radius;
  const r2 = radius * 0.45;
  return Array.from({ length: points }, (_, i) => {
    const angle = (2 * Math.PI * i) / points - Math.PI / 2;
    const r = i % 2 === 0 ? r1 : r2;
    return [center + r * Math.cos(angle), center + r * Math.sin(angle)];
  });
}

// 转换为 path 字符串
function toPath(pointsArr) {
  return (
    "M" +
    pointsArr.map(([x, y]) => `${x.toFixed(2)},${y.toFixed(2)}`).join(" L ") +
    " Z"
  );
}

const shapes = [toPath(getCircle()), toPath(getSquare()), toPath(getStar())];

export default function ShapeMorphDemo() {
  const shapeRef = useRef(null);
  const current = useRef(0);

  useEffect(() => {
    let running = true;

    const morph = () => {
      if (!running) return;
      const from = shapes[current.current % shapes.length];
      const to = shapes[(current.current + 1) % shapes.length];
      animate(shapeRef.current, {
        d: [from, to],
        duration: 2200,
        ease: "outElastic(1, .7)",
        complete: () => {
          current.current = (current.current + 1) % shapes.length;
          setTimeout(morph, 600);
        },
      });
    };

    morph();

    return () => {
      running = false;
    };
  }, []);

  return (
    <div className="flex flex-col items-center justify-center min-h-[60vh]">
      <svg width="200" height="200" viewBox="0 0 200 200">
        <path
          ref={shapeRef}
          d={shapes[0]}
          fill="#60a5fa"
          stroke="#1e3a8a"
          strokeWidth="4"
        />
      </svg>
      <div className="mt-6 text-lg text-gray-700">方 → 星</div>
    </div>
  );
}

SVG 圆 ↔ 心形

import { useEffect, useRef, useState } from "react";
import { animate } from "animejs";

const points = 80;
const radius = 70;
const center = 100;

// 标准圆
function getCircle() {
  return Array.from({ length: points }, (_, i) => {
    const angle = (2 * Math.PI * i) / points;
    return [
      center + radius * Math.cos(angle),
      center + radius * Math.sin(angle),
    ];
  });
}

// 标准心形
function getHeart() {
  return Array.from({ length: points }, (_, i) => {
    const t = (2 * Math.PI * i) / points;
    const x0 = (16 * Math.pow(Math.sin(t), 3)) / 17;
    const y0 =
      (13 * Math.cos(t) -
        5 * Math.cos(2 * t) -
        2 * Math.cos(3 * t) -
        Math.cos(4 * t)) /
      17;
    return [center + radius * x0, center - radius * y0];
  });
}

// 转换为 path 字符串
function toPath(pointsArr) {
  return (
    "M" +
    pointsArr.map(([x, y]) => `${x.toFixed(2)},${y.toFixed(2)}`).join(" L ") +
    " Z"
  );
}

// 渐变色组
const gradients = [
  // 圆:蓝紫渐变
  [
    { offset: "0%", color: "#6EE7F9" },
    { offset: "50%", color: "#A7F3D0" },
    { offset: "100%", color: "#818CF8" },
  ],
  // 心形:粉橙渐变
  [
    { offset: "0%", color: "#FDE68A" },
    { offset: "50%", color: "#FCA5A5" },
    { offset: "100%", color: "#F472B6" },
  ],
];

const shapes = [toPath(getCircle()), toPath(getHeart())];

export default function ShapeMorphHeartDemo() {
  const shapeRef = useRef(null);
  const [gradientIndex, setGradientIndex] = useState(0);
  const current = useRef(0);

  useEffect(() => {
    let running = true;

    const morph = () => {
      if (!running) return;
      const from = shapes[current.current % shapes.length];
      const to = shapes[(current.current + 1) % shapes.length];
      // 动态切换渐变色
      setGradientIndex((current.current + 1) % gradients.length);
      animate(shapeRef.current, {
        d: [from, to],
        duration: 1800,
        ease: "outElastic(1, .7)",
        complete: () => {
          current.current = (current.current + 1) % shapes.length;
          setTimeout(morph, 900);
        },
      });
    };

    morph();

    return () => {
      running = false;
    };
  }, []);

  // 当前渐变色
  const stops = gradients[gradientIndex];

  return (
    <div className="flex flex-col items-center justify-center min-h-[60vh]">
      <svg width="220" height="220" viewBox="0 0 200 200">
        <defs>
          <linearGradient
            id="morphGradient"
            x1="0%"
            y1="0%"
            x2="100%"
            y2="100%"
          >
            {stops.map((stop, i) => (
              <stop
                key={i}
                offset={stop.offset}
                stopColor={stop.color}
                stopOpacity="1"
              />
            ))}
          </linearGradient>
        </defs>
        <path
          ref={shapeRef}
          d={shapes[0]}
          fill="url(#morphGradient)"
          stroke="#2226"
          strokeWidth="5"
          style={{
            filter: "drop-shadow(0 4px 24px #818cf855)",
            transition: "filter 0.3s",
          }}
        />
      </svg>
      <div className="mt-6 text-lg text-gray-700 font-semibold">圆 ↔ 心形</div>
    </div>
  );
}

文字动效

import { useEffect, useRef } from "react";
import { animate, stagger } from "animejs";

const text = "ifrontend.net".split("");

export default function SVGTextAnim() {
  const letterRefs = useRef([]);

  useEffect(() => {
    // 依次弹跳出现
    animate(letterRefs.current, {
      translateY: [40, 0],
      scale: [0.5, 1.1, 1],
      opacity: [0, 1],
      fill: [
        "#818CF8",
        "#F472B6",
        "#FBBF24",
        "#34D399",
        "#60A5FA",
        "#F472B6",
        "#FBBF24",
        "#34D399",
        "#60A5FA",
        "#F472B6",
        "#FBBF24",
        "#34D399",
      ],
      duration: 1200,
      delay: stagger(120),
      ease: "outElastic(1, .7)",
    });
  }, []);

  return (
    <div className="flex flex-col items-center justify-center min-h-[60vh]">
      <svg
        width="380"
        height="100"
        viewBox="0 0 380 100"
        style={{ display: "block" }}
      >
        <defs>
          <linearGradient id="textGradient" x1="0%" y1="0%" x2="100%" y2="0%">
            <stop offset="0%" stopColor="#818CF8" />
            <stop offset="50%" stopColor="#F472B6" />
            <stop offset="100%" stopColor="#FBBF24" />
          </linearGradient>
        </defs>
        {text.map((char, i) => (
          <text
            key={i}
            ref={(el) => (letterRefs.current[i] = el)}
            x={24 + i * 28} // 步进从40改为28,起始点24
            y={68} // y略微上移
            fontSize="38" // 字体略小
            fontFamily="Fira Mono, Menlo, monospace"
            fontWeight="bold"
            fill="url(#textGradient)"
            stroke="#2226"
            strokeWidth="1.2"
            opacity="0"
            style={{
              filter: "drop-shadow(0 2px 8px #818cf855)",
              transition: "filter 0.3s",
              cursor: "default",
              userSelect: "none",
            }}
          >
            {char}
          </text>
        ))}
      </svg>
      <div className="mt-6 text-lg text-gray-700">文字动效</div>
    </div>
  );
}

说明

  • 通过 getTotalLength() 获取 SVG 路径的总长度。
  • 设置 strokeDasharraystrokeDashoffset 实现路径描边动画。
  • 使用 animejs 的 animate 方法让路径从无到有地描绘出来。

你还可以为 SVG 的 fillstroketransform 等属性添加动画,打造更丰富的 SVG 动效。

资源下载
下载价格免费
注意:本网站资源属于虚拟产品,不支持退款。请谨慎购买! 购买后资源无法下载,请联系客服QQ:844475003,微信号:th844475003。
原文链接:https://code.ifrontend.net/archives/817,转载请注明出处。
0

评论0

显示验证码
没有账号?注册  忘记密码?