Vjay_Rav
Vjay_Rav

Reputation: 25

Animating SVG Path using CSS instead of <animateMotion>

I am trying to place an arrow on the midpoint of the bezier curve. The solution using <animateMotion> in the question How properly shift arrow head on cubic bezier in SVG to its center , which moves a <path> which is the arrow and freezes it at the middle of the bezier curve, works only in Firefox. As the curve's points keep changing frequently in my case, I didn't want to use marker-mid as it is costly for me to calculate the midpoint of the bezier curve everytime.

<svg viewBox="0 0 500 500">
<g>
    <path id="path1" d="M291.698 268.340 C321.698 268.340, 411.904 93.133 441.904 93.133"></path>
    <path class="path_arrow" d="M0,0 L6,6 L0,12" transform="translate(-3,-6)">
        <animateMotion dur="0s" rotate="auto" fill="freeze" keyTimes="0;1" keyPoints="0.5;0.5">
            <mpath xlink:href="#path1"></mpath>
        </animateMotion>
    </path> 
</g>
<g transform="translate(166.698,243.340)">
    <circle r="5" class="p1"></circle>
</g>
<g transform="translate(441.904,68.133)" >
 <circle r="5" class="p2"></circle>
</g>
</svg>

Is there any way to do this using CSS Animations so as to avoid using <animateMotion> ?

EDIT 1:

The endpoints of the curve here is draggable and so the points of the curve tend to change frequently. The animation is to move the arrow to the center of the curve without calculating the midpoints. enter image description here

EDIT 2:

Thanks to Kaiido's comment, I added calcMode="linear" and the arrow is now placed on the path as expected. But When I reposition the end point by dragging, the arrow stays in its initial position(as shown) in Chrome but it is expected to move along the parent path. In Firefox this is working fine as before.

enter image description here

Upvotes: 0

Views: 544

Answers (2)

Kaiido
Kaiido

Reputation: 136708

You could achieve the same with CSS offset-path, offset-distance and offset-rotate properties:

#path1 {
  fill: none;
  stroke: black;
}
.path_arrow {
  transform: translate( -3px, -6px );
  offset-path: path("M220 104C220 144,400 180,400 224");
  offset-rotate: auto;
  offset-distance: 50%;
}
body { background: white; }
<svg width="500" height="500" >
  <path id="path1" d="M 220 104 C 220 144 400 180 400 224"
        fill="none" stroke-width="2" stroke="black" />
  <path class="path_arrow" d="M0,0 L0,12 L12,6 z" />
</svg>

But their browser support is far lower than the one of SMIL, so I wouldn't recommend it.

Note that I did fix the answer there where they were missing a calcMode="linear" attribute to make Blink browsers happy.

If you need IE support, you may want to try this js implementation which seems to support <animateMotion> and rotate, keeping in mind I didn't test it myself.



Regarding the question's "EDIT 2":

Chrome indeed seems to need an explicit call to update the <mpath>. This can be done by calling the beginElement() method of the <animationMotion> element after each update:

document.querySelector('svg').onmousemove = function(e) {
  const rect = this.getBoundingClientRect();
  const x = e.clientX - rect.left;
  const y = e.clientY - rect.top;
  path1.setAttribute( 'd', `M ${x} ${y} C 220 144 400 180 400 224` );
  // Chrome requires an explicit update
  document.querySelector('animateMotion').beginElement();
}
<pre style="position: absolute;pointer-events:none">move your mouse to change the path</pre>
<svg width="500" height="500" >
  <path id="path1" d="M 220 104 C 220 144 400 180 400 224"
        fill="none" stroke-width="2" stroke="black" />
  <path d="M0,0 L0,12 L12,6 z" transform="translate(-3,-6)">
    <animateMotion dur="0s" rotate="auto" fill="freeze"
                   keyTimes="0;1" keyPoints="0.5;0.5" calcMode="linear" >
       <mpath xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#path1"/>
    </animateMotion>
  </path>
</svg>

Upvotes: 2

Charlie Robles
Charlie Robles

Reputation: 39

You have to use webkit animation inside the css. Also i would recommend https://codepen.io/collection/yivpx/. Is an open source project for SVG optimization. I also encourage you to name all your classes within your SVG in order to do cool CSS stuff with animate, like:

animation: kaboom 5s ease alternate infinite;

Upvotes: 0

Related Questions