I have a question to svg animation. Please find my code here. Using javascript I am adding animateMotion element to red arrow symbol. I would like to achieve the following thing
I can partially meet this requirement. Moving arrow along the given path is quite simple. JS script is setting path attribute for animateMotion element. I wanted to use rotate attribute to fit rotate changes to the path. Please have a look that when you uncomment this line
animateMotion.setAttribute("rotate", "auto");
in JS code something strange happens. Instead rotating 90 deg after reaching each corner arrow unexpected disappear. Do you know why?
Thank you very much in advance.
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>
Your problem is that your arrow has a large offset from the origin of the SVG. So when it hits the end of the first curve and rotates around the corner, it is disappearing off screen.
Here is a simplified example that hopefully shows what is happening.
<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>
To fix your SVG, you have to make sure that your arrow rotates around its centre. Not the origin.
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>