【问题标题】:SVG line endpoints animate independentlySVG 线端点独立动画
【发布时间】:2020-01-31 11:58:13
【问题描述】:

我正在尝试为 SVG 线端点之一设置动画以沿特定路径移动,而另一个端点静止不动,因此线在保持笔直的同时拉伸和收缩。

到目前为止,我所做的是让我的整条线沿着路径移动,其中一个端点绑定到它:

<svg viewBox="0 0 500 500">
  <path stroke="grey" fill="none" id="route" d="M50,25 l25,30 l-40,20 z" />
  <g>
  <line x1="0" y1="0" x2="150" y2="50" stroke="blue" />
  <circle r=5 fill="blue" />
  <text x="-5" y="-10">A</text>
  <circle cx="150" cy="50" r="5" fill="blue" />
  <text x="145" y="40">B</text>
    <animateMotion dur="5s" repeatCount="indefinite" >
      <mpath xlink:href="#route" />
    </animateMotion>
  </g>
</svg>

我想要的是A点沿着路径移动,B点静止。

我很乐意考虑 CSS/JavaScript 解决方案,但库不是一个选项

请给我指出正确的方向好吗?

【问题讨论】:

    标签: javascript css svg


    【解决方案1】:

    对于您的具体示例,我们可以使用&lt;animate/&gt; 标记的values="..." 属性来实现。您的路径是一个非常简单的示例,因此创建此 values="..." 列表非常简单。

    如果您想更一般地执行此操作,对于任何路径,您可能需要构建 d 路径的 JavaScript 解析器并将其转换为 x 和 y 值列表(对于弯曲路径,这将是非常困难,但并非不可能:https://stackoverflow.com/a/17096947/9792594)

    这是演示:https://codepen.io/Alexander9111/pen/Jjogbbe

    HTML:

    <svg viewBox="0 0 500 500">
        <path stroke="grey" fill="none" id="route" d="M50,25 l25,30 l-40,20 z" />
        <circle cx="50" cy="25" r=5 fill="blue">
            <animate attributeName="cx" values="50;75;35;50" dur="5s" repeatCount="indefinite" />
            <animate attributeName="cy" values="25;55;75;25" dur="5s" repeatCount="indefinite" />
        </circle>
        <text x="50" y="25" text-anchor="middle" transform="translate(0,-7)">A
            <animate attributeName="x" values="50;75;35;50" dur="5s" repeatCount="indefinite" />
            <animate attributeName="y" values="25;55;75;25" dur="5s" repeatCount="indefinite" />
        </text>
        <circle cx="150" cy="50" r="5" fill="blue"> </circle>
        <text x="145" y="40">B</text>
        <line x1="50" y1="25" x2="150" y2="50" stroke="blue">
            <animate attributeName="x1" values="50;75;35;50" dur="5s" repeatCount="indefinite" />
            <animate attributeName="y1" values="25;55;75;25" dur="5s" repeatCount="indefinite" />
        </line>
    </svg>
    

    或者,我们可以考虑使用 JavaScript 对其进行动画处理。

    更新 - 使用 JavaScript 跟踪圆圈的位置,因为它使用 &lt;animateMotion/&gt; 标记制作动画:

    演示:https://codepen.io/Alexander9111/pen/NWPQbma

    HTML:

    <svg viewBox="0 0 500 500">
        <path stroke="grey" fill="none" id="route" d="M50,25 l25,30 l-40,20 z" />
        <circle id="circle_motion" r=5 fill="blue">
            <animateMotion dur="5s" fill="freeze">
                <mpath xlink:href="#route" />
            </animateMotion>
        </circle>
        <rect id="BBox" x="" y="" width="" height=""></rect>
        <text id="text_motion" x="50" y="25" text-anchor="middle" transform="translate(0,-7)">A
        </text>
        <circle cx="150" cy="50" r="5" fill="blue"> </circle>
        <text x="150" y="50" text-anchor="middle" transform="translate(0,-7)">B</text>
        <line id="line_motion" x1="50" y1="25" x2="150" y2="50" stroke="blue">
        </line>
    </svg>
    

    JS:

    const svg = document.querySelector('svg');
    const animateElem = document.querySelector('animateMotion');
    const circle_motion = document.querySelector('#circle_motion');
    const text_motion = document.querySelector('#text_motion');
    const line_motion = document.querySelector('#line_motion');
    const BBox = document.querySelector('#BBox');
    var myInterval;
    
    function startFunction() {
      const box = circle_motion.getBoundingClientRect();
      var pt = svg.createSVGPoint();
      pt.x = (box.left + box.right) / 2;
      pt.y = (box.top + box.bottom) / 2;
      var svgP = pt.matrixTransform(svg.getScreenCTM().inverse());
      //console.log(svgP.x,svgP.y)
      text_motion.setAttribute('x', (svgP.x) );
      text_motion.setAttribute('y', (svgP.y) );
      line_motion.setAttribute('x1', (svgP.x) );
      line_motion.setAttribute('y1', (svgP.y) );
    }
    
    function endFunction() {
      clearInterval(myInterval)
    }
    
    animateElem.addEventListener('beginEvent', () => {
      console.log('beginEvent fired');
      myInterval = setInterval(startFunction, 10);
    })
    
    animateElem.addEventListener('endEvent', () => {
      console.log('endEvent fired');
      endFunction();
    })
    

    这更加灵活,我们可以将我们的 amimate 路径更改为:&lt;path stroke="grey" fill="none" id="route" d="M50,25 75,55 Q75,75 35,75 z" /&gt;,我们也可以遵循这条非线性路径:

    【讨论】:

    • 很好,很有帮助的答案
    猜你喜欢
    • 1970-01-01
    • 2016-09-08
    • 2023-03-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多