Home > Mobile >  SVG animateTransform scaling keySplines to match Y component of rotating vector
SVG animateTransform scaling keySplines to match Y component of rotating vector

Time:04-18

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.

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

<!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>

CodePudding user response:

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>

  • Related