首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何用SVG动画沿运动路径移动/动画多个圆圈

如何用SVG动画沿运动路径移动/动画多个圆圈
EN

Stack Overflow用户
提问于 2022-07-27 09:11:33
回答 1查看 246关注 0票数 1

如何使用SVG SMIL动画<mpath> (运动路径)移动多个圆圈。

问题:关于前3、4圈,一切都很好。

一些圆圈“出了轨道”--所以它们与运动路径不正确地对齐。

代码语言:javascript
复制
.planePath {
    stroke: red;
    stroke-width: .1%;
    stroke-width: .5%;
    stroke-dasharray: 1% 2%;
    stroke-linecap: round;
    fill: none;
    z-index: 99;
}
代码语言:javascript
复制
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="index.css">
    <!-- CSS only -->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/css/bootstrap.min.css" rel="stylesheet"
        integrity="sha384-gH2yIJqKdNHPEq0n4Mqa/HGKIhSkIHeL5AyhkYV8i59U5AR6csBvApHHNl/vI1Bx" crossorigin="anonymous">
    <title>Document</title>
</head>

<body>
    <div class="container-fluid center" style="z-index: 99">
        <svg viewBox="-300 -150 3387 1270" align="center" class="svg-animation">
            <path id="planePath" class="planePath"
            d="M1.50024 430C58.2002 -111.6 853.699 -156.741 889.5 430C925.5 1020 1754 1007.5 1785 430C1816 -147.5 2665.5 -132 2665.5 430C2665.5 1010.27 1847 948 1785 453C1841.5 -83.5 930.282 -187.244 889.5 389C851 933 35 1017.5 1.50024 430Z"/>
            />
            <path style="position:absolute" id="circle2" class="planePath "
            d="M1.50024 430C58.2002 -111.6 853.699 -156.741 889.5 430C925.5 1020 1754 1007.5 1785 430C1816 -147.5 2665.5 -132 2665.5 430C2665.5 1010.27 1847 948 1785 453C1841.5 -83.5 930.282 -187.244 889.5 389C851 933 35 1017.5 1.50024 430Z"/>
            />
            <defs>
                <filter id="filter0_d_0_1" x="0" y="17" width="897" height="847" filterUnits="userSpaceOnUse"
                    color-interpolation-filters="sRGB">
                    <feFlood flood-opacity="0" result="BackgroundImageFix" />
                    <feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"
                        result="hardAlpha" />
                    <feOffset dy="4" />
                    <feGaussianBlur stdDeviation="2" />
                    <feComposite in2="hardAlpha" operator="out" />
                    <feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0" />
                    <feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_0_1" />
                    <feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_0_1" result="shape" />
                </filter>
                <filter id="filter1_d_0_1" x="926" y="33" width="897" height="847" filterUnits="userSpaceOnUse"
                    color-interpolation-filters="sRGB">
                    <feFlood flood-opacity="0" result="BackgroundImageFix" />
                    <feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"
                        result="hardAlpha" />
                    <feOffset dy="4" />
                    <feGaussianBlur stdDeviation="2" />
                    <feComposite in2="hardAlpha" operator="out" />
                    <feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0" />
                    <feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_0_1" />
                    <feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_0_1" result="shape" />
                </filter>
                <filter id="filter2_d_0_1" x="1884" y="33" width="897" height="847" filterUnits="userSpaceOnUse"
                    color-interpolation-filters="sRGB">
                    <feFlood flood-opacity="0" result="BackgroundImageFix" />
                    <feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"
                        result="hardAlpha" />
                    <feOffset dy="4" />
                    <feGaussianBlur stdDeviation="2" />
                    <feComposite in2="hardAlpha" operator="out" />
                    <feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0" />
                    <feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_0_1" />
                    <feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_0_1" result="shape" />
                </filter>
            </defs>

            <g id="plane">
                <circle cx="80" cy="0" r="20" fill="black" />
            </g>
            <g id="point">
                <circle cx="-50" cy="0" r="20" fill="black"/>
            </g>
            <g id="point-2">
                <circle cx="20" cy="0" r="20" fill="black" />
            </g>
            <g id="point-3">
                <circle cx="-120" cy="0" r="20" fill="black" />
            </g>
            <g id="point-4">
                <circle cx="140" cy="0" r="20" fill="black" />
            </g>
            <g id="point-5">
                <circle cx="180" cy="20" r="20" fill="orange" />
            </g>
            <g id="point-6">
                <circle cx="-180" cy="0" r="20" fill="black" />
            </g>
            <g id="point-7">
                <circle cx="-200" cy="0" r="20" fill="black" />
            </g>
            <g id="point-8">
                <circle cx="-220" cy="0" r="20" fill="black" />
            </g>
            <g id="point-9">
                <circle cx="-240" cy="0" r="20" fill="black" />
            </g>
            <g id="point-10">
                <circle cx="-260" cy="0" r="20" fill="black" />
            </g>
            <g id="point-11">
                <circle cx="-280" cy="0" r="20" fill="black" />
            </g>

            <g id="point-12">
                <circle cx="-300" cy="0" r="20" fill="black"/>
            </g>
            <g id="point-13">
                <circle cx="320" cy="0" r="20" fill="black" />
            </g>
            <g id="point-14">
                <circle cx="-340" cy="0" r="20" fill="black" />
            </g>
            <g id="point-15">
                <circle cx="-360" cy="0" r="20" fill="black" />
            </g>
            <g id="point-16">
                <circle cx="-380" cy="0" r="20" fill="black" />
            </g>
            <g id="point-17">
                <circle cx="-400" cy="0" r="20" fill="black" />
            </g>
            <g id="point-18">
                <circle cx="-420" cy="0" r="20" fill="black" />
            </g>
            <g id="point-19">
                <circle cx="-430" cy="0" r="20" fill="black" />
            </g>
            <g id="point-20">
                <circle cx="-440" cy="0" r="20" fill="black" />
            </g>
            <g id="point-21">
                <circle cx="-460" cy="0" r="20" fill="black" />
            </g>
            <g id="point-22">
                <circle cx="-480" cy="0" r="20" fill="black" />
            </g>
            <!-- Define the motion path animation -->
            <animateMotion xlink:href="#plane" dur="20s" repeatCount="indefinite" rotate="auto">
                <mpath xlink:href="#planePath" />
            </animateMotion>
            <animateMotion xlink:href="#point" dur="20s" repeatCount="indefinite" rotate="auto">
                <mpath xlink:href="#planePath" />
            </animateMotion>
            <animateMotion xlink:href="#point-2" dur="20s" repeatCount="indefinite" rotate="auto">
                <mpath xlink:href="#planePath" />
            </animateMotion>
            <animateMotion xlink:href="#point-3" dur="20s" repeatCount="indefinite" rotate="auto">
                <mpath xlink:href="#planePath" />
            </animateMotion>


            <animateMotion xlink:href="#point-4" dur="20s" repeatCount="indefinite" rotate="auto">
                <mpath xlink:href="#planePath" />
            </animateMotion>
            <animateMotion xlink:href="#point-5" dur="20s" repeatCount="indefinite" rotate="auto">
                <mpath xlink:href="#planePath" />
            </animateMotion>
            <!-- <animateMotion xlink:href="#point-6" dur="20s" repeatCount="indefinite" rotate="auto">
                <mpath xlink:href="#planePath" />
            </animateMotion>
            <animateMotion xlink:href="#point-7" dur="20s" repeatCount="indefinite" rotate="auto">
                <mpath xlink:href="#planePath" />
            </animateMotion>
            <animateMotion xlink:href="#point-8" dur="20s" repeatCount="indefinite" rotate="auto">
                <mpath xlink:href="#planePath" />
            </animateMotion>
            <animateMotion xlink:href="#point-9" dur="20s" repeatCount="indefinite" rotate="auto">
                <mpath xlink:href="#planePath" />
            </animateMotion> -->

        </svg>
    </div>
    <!-- <div class="box">
        <div class="circle">
        </div>
        <div class="circle">
        </div>
        <div class="circle">
        </div>
    </div> -->

    <script src="index.js"></script>
</body>

</html>

<!-- begin snippet: js hide: false console: true babel: false -->

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-07-27 18:19:08

你的动画圆圈(沿着运动路径移动)

应该放在cx/cy =0

这里由@Paul LeBeau:跟踪svg运动路径时的偏移量解释

否则,它们的初始位置将被添加到当前的运动路径位置。

这就是为什么你的圆圈在沿着小径直线移动的原因。

基于动画延迟的路径偏移

实际上,所有的圆圈都有相同的cx="0" cy="0"的中心位置,因此它们在没有动画的情况下是重叠的。

通过添加增量begin值,我们模拟了如下所示的路径偏移:

代码语言:javascript
复制
<animateMotion xlink:href="#plane" dur="10s" begin="0s"repeatCount="indefinite" rotate="auto">
    <mpath xlink:href="#planePath" />
</animateMotion>
<animateMotion xlink:href="#point-1" dur="10s" begin="0.1s" repeatCount="indefinite" rotate="auto">
    <mpath xlink:href="#planePath" />
</animateMotion> 

begin="0.1s"值越高,圆之间的距离越大。

简化示例

代码语言:javascript
复制
<svg viewBox="-300 -150 3387 1270" align="center" class="svg-animation">
            <path id="planePath" fill="none" stroke="red" stroke-width="0.5%" stroke-dasharray="1% 2%"
            stroke-linecap="round"  class="planePath"
                d="M1.50024 430C58.2002 -111.6 853.699 -156.741 889.5 430C925.5 1020 1754 1007.5 1785 430C1816 -147.5 2665.5 -132 2665.5 430C2665.5 1010.27 1847 948 1785 453C1841.5 -83.5 930.282 -187.244 889.5 389C851 933 35 1017.5 1.50024 430Z" />
            />
            <g id="circles">
                <circle id="plane" class="plane" fill="green" cx="0" cy="0" r="20" fill="black" />
                <circle id="point-1" class="point-1" fill="magenta" cx="0" cy="0" r="20" fill="black" />
                <circle id="point-2" class="point-2" fill="purple" cx="0" cy="0" r="20" fill="black" />
                <circle id="point-3" class="point-3" fill="orange" cx="0" cy="0" r="20" fill="black" />
            </g>
            <animateMotion xlink:href="#plane" dur="10s" begin="0s"repeatCount="indefinite" rotate="auto">
                <mpath xlink:href="#planePath" />
            </animateMotion>
            <animateMotion xlink:href="#point-1" dur="10s" begin="0.1s" repeatCount="indefinite" rotate="auto">
                <mpath xlink:href="#planePath" />
            </animateMotion>
            <animateMotion xlink:href="#point-2" dur="10s" begin="0.2s" repeatCount="indefinite" rotate="auto">
                <mpath xlink:href="#planePath" />
            </animateMotion>
            <animateMotion xlink:href="#point-3" dur="10s" begin="0.3s" repeatCount="indefinite" rotate="auto">
                <mpath xlink:href="#planePath" />
            </animateMotion>

        </svg>

缺点:由于延迟,在开始时可以看到x/y=0位置的圆圈,因为它们还没有对齐到运动路径。

作为一种解决办法,我们可以隐藏所有的圆圈,直到它们都是动画/对齐:

代码语言:javascript
复制
.planePath {
  stroke: red;
  stroke-width: .1%;
  stroke-width: .5%;
  stroke-dasharray: 1% 2%;
  stroke-linecap: round;
  fill: none;
}
代码语言:javascript
复制
<svg viewBox="-300 -150 3387 1270" align="center" class="svg-animation">
            <path id="planePath" class="planePath"
                d="M1.50024 430C58.2002 -111.6 853.699 -156.741 889.5 430C925.5 1020 1754 1007.5 1785 430C1816 -147.5 2665.5 -132 2665.5 430C2665.5 1010.27 1847 948 1785 453C1841.5 -83.5 930.282 -187.244 889.5 389C851 933 35 1017.5 1.50024 430Z" />
            />
            <g id="circles" opacity="0">
                <circle id="plane" class="plane" fill="green" cx="0" cy="0" r="20" fill="black" />
                <circle id="point-1" class="point-1" fill="magenta" cx="0" cy="0" r="20" fill="black" />
                <circle id="point-2" class="point-2" fill="purple" cx="0" cy="0" r="20" fill="black" />
                <circle id="point-3" class="point-3" fill="orange" cx="0" cy="0" r="20" fill="black" />
            </g>
            <animateMotion xlink:href="#plane" dur="10s" begin="0s"repeatCount="indefinite" rotate="auto">
                <mpath xlink:href="#planePath" />
            </animateMotion>
            <animateMotion xlink:href="#point-1" dur="10s" begin="0.1s" repeatCount="indefinite" rotate="auto">
                <mpath xlink:href="#planePath" />
            </animateMotion>
            <animateMotion xlink:href="#point-2" dur="10s" begin="0.2s" repeatCount="indefinite" rotate="auto">
                <mpath xlink:href="#planePath" />
            </animateMotion>
            <animateMotion xlink:href="#point-3" dur="10s" begin="0.3s" repeatCount="indefinite" rotate="auto">
                <mpath xlink:href="#planePath" />
            </animateMotion>

            <!-- hide circles on start: otherwise circles show up at x/y=0 due to animation delay-->
            <animate attributeName="opacity"
            xlink:href="#circles"
            values="0; 1;" dur="0.1s" begin="0.4"
            data-id="circles"
            repeatCount="1"
            fill="freeze"
            />
        </svg>

动画中的淡入

代码语言:javascript
复制
    <animate attributeName="opacity"
    xlink:href="#circles"
    values="0; 1;" dur="0.1s" begin="0.4"
    data-id="circles"
    repeatCount="1"
    fill="freeze"
    />

begin值的计算如下:

延迟-增量: 0.1 *圆圈总数

初始运动路径偏移- css offset-path到救援

免责声明:浏览器支持可能仍然不稳定。

另见MDN文档

offset-path属性的主要优点是它能够实际定义一个起始偏移量--对于静态元素呈现来说也相当整洁。

(非常类似于svg的textPath相关startOffset属性)

代码语言:javascript
复制
const svg = document.querySelector('svg');
let dotsCount = 15;
let steps = 100 / dotsCount;
let duration = 10;
let circleRadius = 20;
let startOffset = 50;

//create css rules for animations
let circleMarkup = '';
let css = 
`.css-animate circle {
offset-path: path('M1.50024 430C58.2002 -111.6 853.699 -156.741 889.5 430C925.5 1020 1754 1007.5 1785 430C1816 -147.5 2665.5 -132 2665.5 430C2665.5 1010.27 1847 948 1785 453C1841.5 -83.5 930.282 -187.244 889.5 389C851 933 35 1017.5 1.50024 430Z');
offset-rotate: auto;
}`;

for (let i = 0; i < dotsCount; i++) {
    circleMarkup +=
        `<circle id="point${i}" class="point point${i}" fill="green" cx="0" cy="0" r="${circleRadius}" />`;
    css +=
`.point${i} {
offset-distance: ${steps*i+startOffset}%;
animation: followpath${i} ${duration}s linear infinite;
}
@keyframes followpath${i} {
to {
offset-distance: ${100+steps*i+startOffset}%;
}
}`;
}

svg.insertAdjacentHTML('afterbegin', '<style>'+css+'</style>');
svg.insertAdjacentHTML('beforeend', circleMarkup);
代码语言:javascript
复制
    <svg viewBox="-300 -150 3387 1270" class="css-animate">
        <path id="planePath" stroke="#ccc" stroke-width="1%" stroke-dasharray="1% 2%"
 stroke-linecap="round" fill="none" class="planePath"
            d="M1.50024 430C58.2002 -111.6 853.699 -156.741 889.5 430C925.5 1020 1754 1007.5 1785 430C1816 -147.5 2665.5 -132 2665.5 430C2665.5 1010.27 1847 948 1785 453C1841.5 -83.5 930.282 -187.244 889.5 389C851 933 35 1017.5 1.50024 430Z" />
    </svg>

在上面的示例中,初始运动路径偏移量由以下方法设置:

代码语言:javascript
复制
.point-1 {
    offset-distance: 25%;
    animation: followpath1 10s linear infinite;
}

还引用了@keyframe动画规则:

代码语言:javascript
复制
@keyframes followpath1 {
    to {
        offset-distance: 125%;
    }
}

当然,您需要根据所需的偏移量/时间来调整值。

备选方案:动画stroke-dashoffset

可能是最简单的方法--有点紧张。我使用pathLength将破折号数组的计算长度更改为100。

代码语言:javascript
复制
stroke-dasharray: 0 6.666;
stroke-linecap: round;

将第一个破折号数组值设置为0时,当与stroke-linecap: round组合时,将产生一条虚线(因此每个点都是完全圆形的)。

第二个值定义了差距或点的总数:

100 (路径长度)/ 15 (3圈-5点)= 0.666。

代码语言:javascript
复制
.planePath {
            stroke: red;
            stroke-width: 5%;
            stroke-dasharray: 0 6.666;
            stroke-linecap: round;
            fill: none;
             animation: animStroke 10s linear infinite; 
            stroke-dashoffset: 0;

        }

        .planePath2 {
            stroke: red;
            stroke-width: 5%;
            stroke-dasharray: 0 3.333;
            stroke-linecap: round;
            fill: none;
            animation: animStroke 10s linear infinite;
        }

    
        @keyframes animStroke {
            to {
                stroke-dashoffset: -100;
            }
        }
代码语言:javascript
复制
<div class="container-fluid center" style="z-index: 99">
        <svg viewBox="-300 -150 3387 1270" align="center" class="svg-animation">
            <path id="planePath" pathLength="100" class="planePath"
                d="M1.50024 430C58.2002 -111.6 853.699 -156.741 889.5 430C925.5 1020 1754 1007.5 1785 430C1816 -147.5 2665.5 -132 2665.5 430C2665.5 1010.27 1847 948 1785 453C1841.5 -83.5 930.282 -187.244 889.5 389C851 933 35 1017.5 1.50024 430Z" />
            />
        </svg>
    </div>


    <div class="container-fluid center" style="z-index: 99">
        <svg viewBox="-300 -150 3387 1270" align="center" class="svg-animation">
            <path id="planePath2" pathLength="100" class="planePath planePath2"
                d="M1.50024 430C58.2002 -111.6 853.699 -156.741 889.5 430C925.5 1020 1754 1007.5 1785 430C1816 -147.5 2665.5 -132 2665.5 430C2665.5 1010.27 1847 948 1785 453C1841.5 -83.5 930.282 -187.244 889.5 389C851 933 35 1017.5 1.50024 430Z" />
            />
        </svg>
    </div>

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/73135195

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档