Using an animation function written in native JavaScript on Alibaba Cloud

Collapse
X
 
  • Time
  • Show
Clear All
new posts
  • IvanH
    New Member
    • Nov 2017
    • 51

    Using an animation function written in native JavaScript on Alibaba Cloud

    Problems during numerical calculation using an animation function written in native JavaScript

    I tried to write a code when learning the MOOC online JavaScript animation effect. But I found several problems:
    Non-uniform animation velocity
    Animation duration was not able to be controlled
    Other actions started before the animation reached the terminating condition (especially during transparency changes. The super-long decimal point calculation – as many as eight to nine decimal places – of JavaScript just left me speechless.)
    Can somebody help me modify the code? Thanks so much.
    If somebody can write a concise native JavaScript animation function that enables interaction of multiple attributes for reference, that would be even more awesome. (Please do make the animation duration controllable and animation termination strictly following the given conditions.)
    Here is the code:
    Code:
    <!doctype html>
        <html lang="en">
         <head>
       <meta http-equiv='content-type' content='text/html;charset=utf-8' />
          <script>
        /*--Get the ID--*/
        function $id(id){
          return document.getElementById(id);
        }
        /*----Get the element style attributes--*/
        function $gs(ele,attr){
           if (ele.currentStyle)
             {
               return ele.currentStyle[attr];
             }else if (ele.style[attr]!==''){
                  return ele.style[attr];
             }else{
              if (window.getComputedStyle(ele,null)[attr]!=='auto')
               {
                 return window.getComputedStyle(ele,null)[attr];
               }else{
                 return 0;
               }
             }
        } 
        
            // Multi-attribute animation
            function animate(ele,time,json,fn){
               window.clearInterval(ele.timer);
               var fps=60/1000;
               ele.timer=window.setInterval(function(){
                  for (var a in json)
                   {
                     var curVal=(a.search('opacity')!==-1) ? parseFloat($gs(ele,a)) :parseInt($gs(ele,a)); // $gs 是自定义的获取元素某样式值德函数
                     var unit=(a.search('opacity')!==-1) ? '' : 'px';
                     var speed=(json[a]-curVal)/(fps*time);
                     if (speed>=0){
                       
                       if (curVal>=json[a]){
                         window.clearInterval(ele.timer);
                         ele.style[a]=json[a]+unit;
                         if (fn){
                           fn();
                         }
                       }else{
                         ele.style[a]=curVal+speed+unit;
                       }
                     }else{
                       if (curVal<=json[a]+0.05){
                         window.clearInterval(ele.timer);
                         ele.style[a]=json[a]+unit;
                         if (fn){
                           fn();
                         }
                       }else{
                         ele.style[a]=curVal+speed+unit;
                       }
                     }
                   }
               },1/fps);
            }
        
         </script>
         </head>
         <body>
           <style>
             #animate{width:200px;height:200px;background-color:blue;}
           </style>
           <div id='animate'></div>
           
           <button id='pos'>Start the multi-attribute interaction animation </button>
           <script>
             $id('pos').onclick=function(){  // $id(id) is the custom function for quick retrieval of elements
               animate($id('animate'),1000,{width:500,opacity:0},function(){
                 animate($id('animate'),1000,{width:200,opacity:1});
               });
             }
           </script>
         </body>
        </html>
    Last edited by gits; Apr 20 '18, 11:48 AM. Reason: added code tags
  • gits
    Recognized Expert Moderator Expert
    • May 2007
    • 5390

    #2
    well - i just reused the code that you posted to come up with a refactored version that would simply use the duration to determine and control the simultaneous animation of some properties - which i was thinking was what you were looking for. you can have a look at it and see if it fits to what you want it to behave. It has a linear animation speed and combines the properties all at once. I am sure i didn't code it for all cases that might happen - but its not intended - its just as an example how i would think that the duration would determine the animation. was hard enough to write it that 'function-like-way' anyway - since i usually would build classes for that. but that might have been confusing here - so i kept it same style as your posted code came.

    Code:
    <!doctype html>
    <html lang="en">
    <head>
        <meta http-equiv='content-type' content='text/html;charset=utf-8' />
        <script>
    
        function $id(id){
            return document.getElementById(id);
        }
    
        function getEleStyle(ele, prop) {
            return window.getComputedStyle(ele, null).getPropertyValue(prop);
        }
    
        /**
         * function animate
         *
         * @param ele dom-node
         * @param duration duration in ms
         * @param props js-obj with css props to set the values to
         * @param cb optional callback that is called when the animation is over
         */
        function animate(ele, duration, props, cb) {
            
            // calculate how many frames we need to process for this animation
            // note: i consider a frame as ONE interval cycle
            var fps = 60;
            var intervalStep = 1000/fps;
            var framesToRenderAtAll = duration/1000 * fps;
            var currFrame = 0;
            
            // scale the passed props according to the frames we have to 
            // process
            var propSteps = {};
            
            for (var prop in props) {
                propSteps[prop] = {};
                
                var propStart = getEleStyle(ele, prop);
                var pxTest = /px$/.test(propStart);
                
                propSteps[prop].unit = pxTest ? 'px' : '';
                propSteps[prop].curr = pxTest ? parseInt(propStart, 10) : parseFloat(propStart);
                
                var step = (props[prop] - propSteps[prop].curr)/framesToRenderAtAll;
                
                propSteps[prop].step = parseFloat(step.toFixed(2));
            }
            
            // function to set the props per animation-frame    
            var setProps = function() {
                currFrame++;
            
                for (var i in propSteps) {
                    propSteps[i].curr += propSteps[i].step;
                    ele.style[i] = propSteps[i].curr + propSteps[i].unit;
                }
            
                if (currFrame >= framesToRenderAtAll) {
                    window.clearInterval(t);
              
                    if (typeof cb != 'undefined') {
                        cb();
                    }
                }
            };
            
            // instantly call first step
            setProps();
            
            // setting the interval to start the animation
            var t = window.setInterval(setProps, intervalStep);
        }
        
        </script>
    </head>
    <body>
        <style>
            #animate{width:200px;height:200px;background-color:blue;}
        </style>
        <div id='animate'></div>
        <button id='pos'>Start the multi-attribute interaction animation</button>
        <script>
        
        $id('pos').onclick = function() {
            animate($id('animate'), 1000, {width:1000, opacity:0, height:100}, function() {
                animate($id('animate'), 1000, {width:200, opacity:1, height:200});
            });
        }
        
        </script>
    </body>
    </html>
    PS: it is not as exact as it could be in determining exact steps and such - but you can improve it by more rounding floats and such where necessary.
    Last edited by gits; Apr 20 '18, 03:07 PM. Reason: added PS note

    Comment

    • TimoHa
      New Member
      • Nov 2017
      • 22

      #3
      You set the default FPS value to 60. Speed indicates the rate at which each frame will change.
      However, the number of frames in a browser is not certain because of a variety of factors, and the time intervals between frames are also undefined.
      The actual animation process should be calculated by the time difference between the start time and the current time.
      When the animate function runs, it retrieves the start time from Date.now(). In the setInterval method, it retrieves the current time from Date.now() every time.
      Calculate the proportion of the time gap “elapsed” between the two values to the animation time variable “time”, and you can get the variation proportion of the style.
      I will post the code later today if I have the time.
      Here is the code.
      Multi-attribute animation
      Code:
      var interval = 1000 / 60;
      var animate = function animate(ele, time, json, fn) {
          if (ele.timer) return;
          var startTime = Date.now(),
              vals = [];
          for (var key in json) {
              var curVal = key.search('opacity') !== -1 ? parseFloat($gs(ele, key)) : parseInt($gs(ele, key)),
                  unit = key.search('opacity') !== -1 ? '' : 'px';
      
              vals.push({ style: key, startVal: curVal, endVal: json[key], unit: unit });
          }
      
          ele.timer = setInterval(function () {
              var elapsed = Date.now() - startTime;
              if (elapsed >= time) {
                  clearInterval(ele.timer);
                  ele.timer = null;
                  for (var i = 0; i < vals.length; i++) {
                      ele.style[vals[i].style] = vals[i].endVal + vals[i].unit;
                  }
                  
                  fn && fn();
              } else {
                  for (var i = 0; i < vals.length; i++) {
                      ele.style[vals[i].style] = vals[i].startVal + (vals[i].endVal - vals[i].startVal) * (elapsed / time) + vals[i].unit;
                  }
              }
          }, interval);
      };

      Comment

      • gits
        Recognized Expert Moderator Expert
        • May 2007
        • 5390

        #4
        well - seems viable solution as well - while i work with a constant number of frames TimoHa's solution assumes that the elapsed time isn't exactly the time of the interval - which is correct - so it can end up drawing a bit more or less calculated frames. Basically TimoHa's solution is better since it can smooth out some processing delays and such - it can be easily simulated by just placing a longer loop into the prop-setter-function - with my solution it will lead to a longer animation (constant number of frames) while the other solution determines the remaining time and adapts - so its a more constant duration.

        Comment

        • gits
          Recognized Expert Moderator Expert
          • May 2007
          • 5390

          #5
          here is something that can be very useful for such tasks since it explains in more detail what happens exactly and how things can be optimized especially with animations:

          THE EVENT LOOP

          cheers,
          gits

          Comment

          Working...