Search code examples
javascripthtmlsvgsvg-animate

SVG3d: Controlling the rotation of a path with an easing function


I'm trying to emulate a 3d rotation among the y-axis for some paths I have in an SVG. I've found the library SVG3D https://code.google.com/p/svg3d/ and I've given it a try.

The problem is that you can only control the ticking interval duration and the increment (in radians) of each "tick" (there's a setInterval timing function that calls the transform() function which executes the rotation).

There's a toggleRotation function, that works like a play-pause command, but it doesn't give you any control of the timing and rotation of the path.

I have the following example code for my path:

<g id="anim_container" 
    z:xOrigin="0"
    z:yOrigin="0"
    z:zRatio="1000"
    z:sortAlgo="none" 
    z:rotationTime="60"
    onload="init(this);">
    <path
    d="M 460.2203,600.93365 248.25964,263.59573 C 248.63437,272.77915 249.10289,281.86852 249.66521,290.86388 250.03994,298.54799 250.32105,306.70032 250.50856,315.32088 250.88328,323.75461 251.07069,331.251 251.07079,337.81007 L 251.07079,600.93365 203.28125,600.93365 203.28125,204.84271 265.68876,204.84271 479.89835,544.42955 C 479.33579,535.24653 478.77356,526.06345 478.21166,516.88029 477.83651,509.00916 477.4617,500.48202 477.0872,491.29883 476.71206,481.92845 476.52465,473.02649 476.52497,464.59291 L 476.52497,204.84271 524.87674,204.84271 524.87674,600.93365 460.2203,600.93365"
    id="path_n">
        <z:translation x="364.0790100097656" y="402.88818359375"/>
        <z:rotation incRotY="0.5"/>
        <z:translation x="-364.0790100097656" y="-402.88818359375"/>
    </path>
</g>

So, how could you control the spinning and speed of the objects? Is it possible to include an easing function to control the speed of each rotation?


Solution

  • Well I have found a workaround for this issue. It requires three steps

    1. First, it needs you to alter the Svg3d library code.

      Open the file "svg3d_parsing.js" and comment the following line within the "init" function

      function init(g) {
          .......
          //clockRotation = window.setInterval("transform()", rotationTime);
          .......
      }
      

      What I'm doing here is avoiding the use of the "ticking" function included in the library, so we will be able to call transform() (the function that performs the rotation, which is available as a global function) with our own ticking function (and only when we need to). Then, in the same file locate the "getAttrValue" function and comment the line shown here:

      function getAttrValue(......
      .....
      if (incAttValue) {
         var incValue = parseFloat(incAttValue);
         //node.setAttribute(tag, returnedValue + incValue);
      }
      return returnedValue;
      

      This is the core of the control of the rotation. When you execute init(this) and on every call to transform(), attributes rotX and/or rotY (depending if you set incRotX, incRotY, or both) are included to the <z:rotation incRotY="0.5"/> tag, resulting in <z:rotation incRotY="0.5" rotY="0.5"/> (for this example). The function transform() then reads these values, and calculates the transform matrices using them as the angles of rotation for each axis (in radians).

      It happens, as well, that on each call to transform(), there's an increment of rotX and/or rotY by incRotX and/or incRotY, respectively. So, by commenting this line (and following the next steps) we will be able to control the angle of rotation as we wish.

    2. Second, edit the <z:rotation> tag, by including an ID (I use one of the functions in the svg3d library file "dom_utils.js", but for the purpose of this solution we will just set an ID for easy accesing) --- (You could even remove the incRotY attribute) So, we get

      <z:rotation id="rot" incRotY="0.5"/>
      
    3. Last, get a tweening and ticking function to set the rotY (and/or rotX depending on your case) to the z:rotation tag. This will be the angle of rotation that the function transform will apply to the path.

      For this example I use easeljs (for ticking) and tweenjs (for tweening) from createjs suite: http://www.createjs.com/

      Append the following code to the file before the svg closing tag.

      <!--Attaching easeljs and tweenjs libraries-->
      <script
      xlink:href="http://code.createjs.com/easeljs-0.7.0.min.js"
      id="easeljs" />
      <script
      xlink:href="http://code.createjs.com/tweenjs-0.5.0.min.js"
      id="tweenjs" />
      <script id="rotate">
         var v = document.getElementById("rot");         //Get z:rotation tag element
         var numberSpins = 3.5;                          //Set number of spins
         //Setting object with variables to tween.
         //deg will be the current angle value, finalDeg the final value (both in degrees)
         degrees = {deg: 0, finalDeg: numberSpins*360};  
         createjs.Tween.get( degrees, { loop: false } )  //apply tweening of degrees object
         .wait(1800)
         .to( { deg: degrees.finalDeg }, 3000, createjs.Ease.quadInOut )  //Here we set the variable to tween within the object (deg), the final value (finalDeg), the duration and the easing function
         .call(function(){createjs.Ticker.removeEventListener('tick',update)}); //Once we finish spinning, remove listener
         //Then set the update function to be called for each tick
         var update = function(){
            var rad = degrees.deg*Math.PI/180;            //Transform the current angle to radians
            v.setAttribute("rotY",rad);                   //Setting the rotation attribute to be read by transform()
            transform();                                  //Apply rotation with the values we've set in z:rotation tag element
         }
         createjs.Ticker.setFPS( 60 );                   //Just create a ticker. See TweenJs Documentation
         createjs.Ticker.addEventListener( 'tick', update ); //For each tick execute the update function
      </script>
      

      And now we can control the number of spins, the duration of the animation and incorporate an easing function.

    Probably there must be a tidier solution.

    I've posted the whole solution in http://pastebin.com/fjPV4Gb4

    And remember: Don't use the toggleTransform function!

    Comment: You could uncomment the clockRotation timing function that calls "transform()" function in the "init" function ("svg3d_parsing.js"), and remove the call to transform() in the "update" function, resulting in the same functionality. But, the calls to transform() would result unlimited.