Home > database >  Smoothly moving SVG element on event from current position to a given point
Smoothly moving SVG element on event from current position to a given point

Time:06-24

I'd like to move an SVG element (image) slowly from its current position (wherever it is) to a given point when an event is handled.

Since I don't know what the position is, I cannot prepare CSS animation based on keyframes.

So I tried doing the task via DOM manipulating:

var curX = parseFloat(image.getAttribute('x'));
var curY = parseFloat(image.getAttribute('y')); // Not used yet.
var curOpacity = parseFloat(image.getAttribute('opacity')); // Not used yet.

var animation = document.createElementNS(svgNS, 'animate');
animation.setAttribute('attributeName', 'x');
animation.setAttribute('from', curX);
animation.setAttribute('to', 0); // Move the image to the left border.
animation.setAttribute('dur', '2s'); // '3s', '5s'
animation.setAttribute('repeatCount', 1);

image.appendChild(animation);

If I set duration to 3s or 5s, it starts moving as soon as the animate element is appended, but when the animation is over the image is returned to the original position. (As I understand this is by design, but I'd like the image to keep its new position).

The second problem is when I set duration to 2s or less, the image stops moving smoothly, it just flickers to the left border and back (I don't know why).

Maybe, there is a better approach?

UPDATE

As it's answered below, the image keeps its position if fill is set to freeze. To reproduce the second problem it is enough to set timeout:

<svg width="100" height="100" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 500" >
    <image href="https://yari-demos.prod.mdn.mozit.cloud/en-US/docs/Web/SVG/Element/image/mdn_logo_only_color.png"
        height="200" width="200" x="200" y="200"/>

    <script>
        // <![CDATA[
        var startAnimation = function ()
        {
            const svgNS = "http://www.w3.org/2000/svg";

            var image = document.querySelector('image');
            var curX = parseFloat(image.getAttribute('x'));
            var curY = parseFloat(image.getAttribute('y')); // Not used yet.
            var curOpacity = parseFloat(image.getAttribute('opacity')); // Not used yet.

            var animation = document.createElementNS(svgNS, 'animate');
            animation.setAttribute('attributeName', 'x');
            animation.setAttribute('from', curX);
            animation.setAttribute('to', 0); // Move the image to the left border.
            animation.setAttribute('dur', '2s'); // '3s', '5s'
            animation.setAttribute('repeatCount', 1);
            animation.setAttribute('fill', 'freeze');

            image.appendChild(animation);
        };
        document.addEventListener('DOMContentLoaded', () => window.setTimeout(startAnimation, 1000));
        // ]]>
    </script>
</svg>

The image initially twitches and starts moving smoothly only after a half of the way. Chrome 102, x64. As for my computer performance, an animation based on window.requestAnimationFrame works with no glitches.

CodePudding user response:

Set the 'fill' attribute of your animation:

 animation.setAttribute('fill', 'freeze');

See here (MDN docs) for more details.

As for the jumpy animation behaviour, it hasn't happened in my tests, neither in the live example below nor on a standalone file.

Live example

<svg width="100" height="100" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 500" >
  <image href="https://yari-demos.prod.mdn.mozit.cloud/en-US/docs/Web/SVG/Element/image/mdn_logo_only_color.png" height="200" width="200" x="200" y="200"/>
  
  <script>
      // <![CDATA[
        document.addEventListener ( 'DOMContentLoaded', function () {
            const svgNS = "http://www.w3.org/2000/svg";
        
            var image = document.querySelector('image');
            var curX = parseFloat(image.getAttribute('x'));
            var curY = parseFloat(image.getAttribute('y')); // Not used yet.
            var curOpacity = parseFloat(image.getAttribute('opacity')); // Not used yet.

            var animation = document.createElementNS(svgNS, 'animate');
            animation.setAttribute('attributeName', 'x');
            animation.setAttribute('from', curX);
            animation.setAttribute('to', 0); // Move the image to the left border.
            animation.setAttribute('dur', '2s'); // '3s', '5s'
            animation.setAttribute('repeatCount', 1);
            animation.setAttribute('fill', 'freeze');

            image.appendChild(animation);
        });
      // ]]>
  </script>
</svg>

UPDATE

The OP's bumpy animation problem when starting the animation through a timeout callback can be remedied by setting the animation element's begin attribute to the timeout delay:

 animation.setAttribute('begin', '1s');
  • Related