svgscalesplinecubic-splineanimatetransform

SVG animateTransform scaling keySplines to match Y component of rotating vector


I need to simulate a rotating arrow in 2D. It has to keep pace with the rotating blue arrow.

I started with keySplines shown by the red arrow that gave a nice quadrant when viewed using http://franzheidl.github.io/keysplines/

But it didn't match the rotating vector very well.

I've fudged numerous attempts and the best I've managed so far is shown in green but it still doesn't match the rotating arrow.

<!DOCTYPE html>
    <body>
    
    <svg width="800" height="400" viewBox="0 0 800 400" version="1.1" id="svg5">
    
      <defs>
         <g id="layer1a">
          <path id="vect1a"
            style="stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
            d="m 0,0 h -10 v -80 h -10 l 20,-20 20,20 h -10 v 80 z" />
          <animateTransform additive=sum attributeName="transform" type="rotate"
           begin="0s" dur="12s"
           from="0 0 0" to="360 0 0"
           repeatCount="2.125" fill="freeze" />
        </g>
        <g id="line1">
          <line x1="0" y1="0" x2="800" y2="0">
        </g>
      </defs>
     
      <g id="layer3" transform="translate(0,200)">
        <path
          style="fill:#ffffff;stroke:#0000ff;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
          d="m 0,0 h 800"
          id="line"
          />
      </g>
      <use href="#layer1a" style="fill:#0000ff" transform="translate(150,200)" />
      <use href="#line1" style="stroke:rgb(255,0,0);stroke-width:2" transform="translate(0,200)">
        <animateTransform
            additive="sum"
            id="line1at0"
            attributeName="transform"
            type="translate"
            calcMode="spline"
            begin="0s"
            dur="12s"
            values="0 -100 ; 0 0 ; 0 100 ;0 0 ; 0 -100"
            keyTimes="0 ; 0.25 ; 0.5 ; 0.75 ; 1" 
            keySplines="0.5 0 1 .5 ; 0 0.5 0.5 1 ; 0.5 0 1 .5 ; 0 0.5 0.5 1"
            repeatCount="2.125"
            fill="freeze" />
      </use>
      <use xlink:href="#vect1a" style="fill:#ff0000" transform="translate(300,200)" >
          <animateTransform
            additive="sum"
            id="arrow1at0"
            attributeName="transform"
            type="scale"
            calcMode="spline"
            begin="0s"
            dur="12s"
            values="1 1 ; 1 0 ; 1 -1 ; 1 0 ; 1 1"
            keyTimes="0 ; 0.25 ; 0.5 ; 0.75 ; 1" 
            keySplines="0.5 0 1 .5 ; 0 0.5 0.5 1 ; 0.5 0 1 .5 ; 0 0.5 0.5 1"
            repeatCount="2.125"
            fill="freeze" />
      </use>
      <use href="#line1" style="stroke:rgb(255,0,0);stroke-width:2" transform="translate(0,200)">
        <animateTransform
            additive="sum"
            id="line1at0"
            attributeName="transform"
            type="translate"
            calcMode="spline"
            begin="0s"
            dur="12s"
            values="0 -100 ; 0 0 ; 0 100 ;0 0 ; 0 -100"
            keyTimes="0 ; 0.25 ; 0.5 ; 0.75 ; 1" 
            keySplines="0.5 0 1 .5 ; 0 0.5 0.5 1 ; 0.5 0 1 .5 ; 0 0.5 0.5 1"
            repeatCount="2.125"
            fill="freeze" />
      </use>
      <use xlink:href="#vect1a" style="fill:#00ff00" transform="translate(450,200)" >
          <animateTransform
            additive="sum"
            id="arrow1bt0"
            attributeName="transform"
            type="scale"
            calcMode="spline"
            begin="0s"
            dur="12s"
            values="1 1 ; 1 0 ; 1 -1 ; 1 0 ; 1 1"
            keyTimes="0 ; 0.25 ; 0.5 ; 0.75 ; 1" 
            keySplines="1 .75 .25 0 ; 0 .25 .75 1 ; 1 .75 .25 0 ; 0 .25 .75 1"
            repeatCount="2.125"
            fill="freeze" />
      </use>
      <use href="#line1" style="stroke:rgb(0,255,0);stroke-width:2" transform="translate(0,200)">
        <animateTransform
            additive="sum"
            id="line1bt0"
            attributeName="transform"
            type="translate"
            calcMode="spline"
            begin="0s"
            dur="12s"
            values="0 -100 ; 0 0 ; 0 100 ;0 0 ; 0 -100"
            keyTimes="0 ; 0.25 ; 0.5 ; 0.75 ; 1" 
            keySplines="1 .75 .25 0 ; 0 .25 .75 1 ; 1 .75 .25 0 ; 0 .25 .75 1"
            repeatCount="2.125"
            fill="freeze" />
      </use>
     
    </svg>
    
    </body>
    </html>

Anyone got any insights on how to set keySplines to get a desired result?


Solution

  • Instead of using SMIL you will need to use some other kind of animation that is allowing you to calculate the y value of the tip of the arrow. Since you have a rotation around point {0,0} the y = 100 * Math.sin(rad) where 100 is the length of the arrow and rad is the rotation angle in radians.

    In this case the value for the scale will be y/100. Also you will need to account for the fact that the arrow has an initial angle (-90)

    In the next example I'm using javascript for the calculation:

    let a = 0;//the angle 
    
    function anim() {
      a++;//increasing the angle with each frame
      use1.setAttribute("transform", `rotate(${a})`);
      let rad = (a + 90) * (Math.PI / 180);//the angle in radians
      use2.setAttribute("transform", `scale(1,${Math.sin(rad)})`);
      use3.setAttribute("transform", `translate(0,${100 * Math.sin(-rad)})`);
      window.requestAnimationFrame(anim);
    }
    
    window.requestAnimationFrame(anim);
    <svg width="800" height="400" viewBox="-120 -101 800 400" version="1.1" id="svg5">
    
      <defs>
        <path id="vect1a" d="m 0,0 h -10 v -80 h -10 l 20,-20 20,20 h -10 v 80 z" />
      </defs>
    
      <line id="line" x1="-120" x2="800" stroke="black" />
    
      <use id="use1" xlink:href="#vect1a" />
      <g transform="translate(200,0)">
        <use id="use2" xlink:href="#vect1a" />
      </g>
      <use id="use3" xlink:href="#line" />
    
    </svg>