<animateMotion> 元素的 SVG 旋转属性设置为自动无法正常工作

SVG rotate attribute of the <animateMotion> element set to auto not working correctly

我对 svg 动画有疑问。请找到我的代码 here。使用 javascript 我将 animateMotion 元素添加到红色箭头符号。我想实现以下目标

我可以部分满足这个要求。沿给定路径移动箭头非常简单。 JS 脚本正在为 animateMotion 元素设置路径属性。我想使用旋转属性来适应路径的旋转变化。 请看看当你取消注释这一行时

animateMotion.setAttribute("rotate", "auto");

在 JS 代码中发生了一些奇怪的事情。而是在到达每个角箭头后旋转 90 度意外消失。 你知道为什么吗?

非常感谢您。

class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }
}

function getArrowSymbolXcoordinate(pathAttribute) {
  var commaIdx = pathAttribute.indexOf(',');
  return pathAttribute.substring(1,commaIdx);
}

function getArrowSymbolYcoordinate(pathAttribute) {
  var commaIdx = pathAttribute.indexOf(',');
  var lineCommandIdx = pathAttribute.indexOf('l');
  console.log(pathAttribute.substring(commaIdx, lineCommandIdx));
  return pathAttribute.substring(commaIdx+1, lineCommandIdx);
}


function getTopPathAttribute(element, xCoordinate) {
  var toTopRightCorrner = Math.abs(topRightCorrner.x - xCoordinate);
  var path = 'm0,0 ' + 'h' + toTopRightCorrner + ' v' + height + ' h-' + width + ' v-' + height + ' z';
  return path;
}


let topLeftCorrner = new Point(200,100);
let topRightCorrner = new Point(350,100);
let bottomLeftCorrner = new Point(200,150);
let bottomRightCorrner = new Point(350,150);

let topArrowSymbols = Array.from(document.getElementsByClassName('top'));

let width = Math.abs(topLeftCorrner.x - topRightCorrner.x);
let height = Math.abs(topLeftCorrner.y - bottomLeftCorrner.y);

topArrowSymbols.forEach( function(element) {
  var xCoordinate = getArrowSymbolXcoordinate(element.getAttribute('d'));
  var animateMotion = document.createElementNS('http://www.w3.org/2000/svg', "animateMotion");
  var pathAttribute = getTopPathAttribute(element, xCoordinate);
  animateMotion.setAttribute("dur", "7s");
  animateMotion.setAttribute("path", pathAttribute);
  //animateMotion.setAttribute("rotate", 'auto');
  element.appendChild(animateMotion);
});
body {
 height: 100vh;
}
<!DOCTYPE html>
<html>
<head>
 <meta charset="utf-8"/>
 <script src="main_home.js" defer="defer"></script>
</head>

<body>

  <figure >
  
    <svg xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" viewbox="120 80 300 200">
        <g>
            <rect fill="none" height="50" stroke="black" stroke-width="2" width="150" x="200" y="100"/>
            <path class="arrow_symbol top" d="m215.21876,99.72372l-2.49741,-2.5l1.64474,0l2.49741,2.5l-2.49741,2.5l-1.64474,0l2.49741,-2.5z" fill="#ffffff" stroke="#ff0000" stroke-width="2"/>
        </g>
    </svg>
</figure>

</body>
</html>

你的问题是你的箭头与 SVG 的原点有很大的偏移。所以当它到达第一条曲线的末端并绕着拐角旋转时,它就会消失在屏幕之外。

这是一个简化的示例,希望能说明正在发生的事情。

<svg viewbox="-100 -100 400 400" width="400">

  <!-- axes to show the origin -->
  <line x1="0" y1="-100" x2="0" y2="300" stroke="#cce" stroke-width="1"/>
  <line x1="-100" y1="0" x2="300" y2="0" stroke="#cce" stroke-width="1"/>
  
  <!-- the path that the arrow is actually following (ie the one in the animateMotion) -->
  <rect width="80" height="80" fill="none" stroke="#888" stroke-width="2"/>

  <!-- dashed line showing the offset from the animateMotionPath to the arrow -->
  <line x1="0" y1="0" x2="100" y2="100" fill="none" stroke="#ff0000" stroke-width="2" stroke-dasharray="10 10">
    <animateMotion dur="7s" rotate="auto"
                   path="M 0,0 L 80,0 L 80,80 L 0,80 Z"/>
  </line>

  <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->

  <!-- the pon-screen path that the arrow appears to be following -->
  <rect fill="none" x="100" y="100" width="80" height="80" stroke="black" stroke-width="2" />

  <!-- the arrow -->
  <path class="arrow_symbol top" d="M 110,100 L 100,110 L 100,90 Z" fill="#ffffff" stroke="#ff0000" stroke-width="2">
    <animateMotion dur="7s" rotate="auto"
                   path="M 0,0 L 80,0 L 80,80 L 0,80 Z"/>
  </path>

</svg>

要修复 SVG,您必须确保箭头围绕其中心旋转。不是原点。

let topArrowSymbols = Array.from(document.getElementsByClassName('top'));

topArrowSymbols.forEach( function(element) {
  var animateMotion = document.createElementNS('http://www.w3.org/2000/svg', "animateMotion");
  animateMotion.setAttribute("dur", "7s");
  animateMotion.setAttribute("path", 'm 220,100 h 130 v 50 h -150 v -50 z');
  animateMotion.setAttribute("rotate", 'auto');
  animateMotion.setAttribute("fill", "freeze");
  element.appendChild(animateMotion);
});
body {
 height: 100vh;
}

svg {
  overflow: visible;
  position: absolute;
  top: 400px;
  left: 400px;
}
<!DOCTYPE html>
<html>
<head>
 <meta charset="utf-8"/>
 <script src="main_home.js" defer="defer"></script>
</head>

<body>

  <figure >
  
    <svg xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" viewbox="120 80 300 200">
        <g>
            <rect id="my-path" fill="none" height="50" stroke="black" stroke-width="2" width="150" x="200" y="100"/>
            <path class="arrow_symbol top" d="m0.21876,0.72372l-2.49741,-2.5l1.64474,0l2.49741,2.5l-2.49741,2.5l-1.64474,0l2.49741,-2.5z" fill="#ffffff" stroke="#ff0000" stroke-width="2"/>
        </g>
    </svg>
</figure>

</body>
</html>