Search code examples
javascriptsvgsvg-animate

SVG animation using transform is offsetting the element


I am trying to create a halo effect by rotating a circular element in SVG using transform rotate. I am using getBox to get the center point of the element to be rotated. When the rotation happens, the center of rotation is accurate, the total picture is getting offset and is not aligned with the rectangle frame. I tried another example with a spiral path. The path object is also getting offset:

SVG without animation

SVG with animation

How can I make sure that animation does not affect the position of the animated element?


Solution

  • <svg xmlns="http://www.w3.org/2000/svg" width="250" height="250" onload="init(evt)" >
    <script type="text/ecmascript">
    var svgNS = "http://www.w3.org/2000/svg";
    function init(evt){
      if ( window.svgDocument == null ) svgDocument = evt.target.ownerDocument;
      addRotateTransform('spiral', 30, 1);
    }
    function addRotateTransform(target_id, speed, direction){
      var element_to_rotate = svgDocument.getElementById(target_id);
      var my_transform = svgDocument.createElementNS(svgNS, "animateTransform");
      
      var cx = 315.1676;
      var cy = 387.68182;
      
      my_transform.setAttributeNS(null, "attributeName", "transform");
      my_transform.setAttributeNS(null, "attributeType", "XML");
      my_transform.setAttributeNS(null, "type", "rotate");
      my_transform.setAttributeNS(null, "dur", speed + "s");
      my_transform.setAttributeNS(null, "repeatCount", "indefinite");
      my_transform.setAttributeNS(null, "from", "0 "+cx+" "+cy);
      my_transform.setAttributeNS(null, "to", 360*direction+" "+cx+" "+cy);
      
      element_to_rotate.appendChild(my_transform);
      my_transform.beginElement();
    }
    </script>
    <g transform="scale(.5) translate(-100,-180)">
    <path id="spiral" style="fill:none;stroke:black"
           d="m 315.1676,387.68182 c 8.40748,6.17899 -4.1812,14.38161 -10.26988,13.97378 -16.49996,-1.10519 -22.18834,-21.15425 -17.67767,-34.51355 8.06853,-23.89666 37.41077,-31.03457 58.75721,-21.38155 31.32677,14.1662 40.10076,53.87255 25.08544,83.00087 -20.01306,38.82347 -70.4052,49.25812 -107.24453,28.78933 -46.35854,-25.75789 -58.46213,-86.97114 -32.49322,-131.48819 31.45184,-53.91612 103.55573,-67.69306 155.73186,-36.19711 61.48776,37.11689 76.94087,120.15192 39.90099,179.97551 -42.764,69.06871 -136.75587,86.19995 -204.21917,43.60489 C 146.08254,465.04658 127.27172,360.08053 175.42985,284.98296 229.456,200.73484 345.4085,180.24337 428.13635,233.9703 c 91.84359,59.64708 114.01657,186.59502 54.71655,276.95016"
           />
    </g>
    </svg>

    In the above example I have reduced your original svg to just the rotating spiral. Taking the centre coordinates of the spiral for the animation script does the trick. You might have noticed that I had taken the transform attribute out of the <path> element and placed it in a group (<g>) surrounding the spiral path.

    The inclusion of the <script>-tag into SVG was new to me, therefore an interesting read(!), but you can also do it without, like:

    <svg xmlns="http://www.w3.org/2000/svg" width="250" height="250" oonload="init(evt)" >
    <g transform="scale(.5) translate(-100,-180)">
    <path id="spiral" style="fill:none;stroke:black"
           d="m 315.1676,387.68182 c 8.40748,6.17899 -4.1812,14.38161 -10.26988,13.97378 -16.49996,-1.10519 -22.18834,-21.15425 -17.67767,-34.51355 8.06853,-23.89666 37.41077,-31.03457 58.75721,-21.38155 31.32677,14.1662 40.10076,53.87255 25.08544,83.00087 -20.01306,38.82347 -70.4052,49.25812 -107.24453,28.78933 -46.35854,-25.75789 -58.46213,-86.97114 -32.49322,-131.48819 31.45184,-53.91612 103.55573,-67.69306 155.73186,-36.19711 61.48776,37.11689 76.94087,120.15192 39.90099,179.97551 -42.764,69.06871 -136.75587,86.19995 -204.21917,43.60489 C 146.08254,465.04658 127.27172,360.08053 175.42985,284.98296 229.456,200.73484 345.4085,180.24337 428.13635,233.9703 c 91.84359,59.64708 114.01657,186.59502 54.71655,276.95016">
      <animateTransform attributeName="transform" attributeType="XML" type="rotate" 
       from="0 315.1676 387.68182" to="360 315.1676 387.68182" dur="30s" repeatCount="indefinite"/>
    </path>
    </g>
    </svg>