• javascript的缓动效果


    这部分对原先的缓动函数进行抽象化,并结合缓动公式进行强化。成品的效果非常惊人逆天。走过路过不要错过。

    好了,打诨到此为止。普通的加速减速是难以让人满意的,为了实现弹簧等让人眼花缭乱的效果必须动用缓动公式。我见过两套缓动公式,一套是早期Robert Penner大神的缓动公式,内置到tween类中,不过现在人们越来越推荐tweenlite这个新秀了。另一套是script.aculo.us与mootools里面的,由于mootools可称之为prototype的升级版,script.aculo.us则是基于prototype,我们就把它们并称为prototype流派。与flash流派最大的不同是,它们封装得更好,并只需传入一个参数(0~1的小数),并且拥有严密的队列机制来调用各种回调函数。如在回调函数设置元素的长宽,就弄成Scale特效,利用它我们进一步制作SlideUp,SlideDown,Squish等复合特效。

    我们先来看flash流派的缓动公式,它们基本都有如下四个参数。

    • t:timestamp,指缓动效果开始执行到当前帧开始执行时经过的时间段,单位ms
    • b:beginning position,起始位置
    • c:change,要移动的距离,就是终点位置减去起始位置。
    • d: duration ,缓和效果持续的时间。

    我们把这四个参数传入Robert Penner大神的缓动公式,它就会计算出当前帧物体移动的位置。我们对比原来的函数来改写。

    var transition = function(el){
      transition.linear = function(t,b,c,d){ return c*t/d + b; };//免费提供一个缓动公式(匀速运动公式)
      el.style.position = "absolute";
      var options = arguments[1] || {},
      begin =  getCoords(el).left,//开始位置
      change = parseFloat(getStyle(_("taxiway"),"width")) - parseFloat(getStyle(el,"width")),//要移动的距离
      duration = options.duration || 500,//缓动效果持续时间
      ease = options.ease || transition.linear,//要使用的缓动公式
      end = begin + change,//结束位置
      startTime = new Date().getTime();//开始执行的时间
      (function(){
        setTimeout(function(){
          var newTime = new Date().getTime(),//当前帧开始的时间
          timestamp = newTime - startTime;//逝去时间
          el.style.left = ease(timestamp,begin,change,duration) + "px";//移动
          if(duration <= timestamp){
            el.style.left = end + "px";
          }else{
            setTimeout(arguments.callee,25);//每移动一次停留25毫秒
          }
        },25)
      })()
    }

    接着是各种缓动公式大阅兵,共分为十一大类,除了linear。其他类又分为三种。

    • easeIn方法控制补间如何从开始到最高速度。
    • easeOut 方法控制补间减速并停在目标位置
    • easeInOut方法同时控制上述两者。

    具体公式见下面(共31种)。

    //***********@author:Robert Penner and cloudgamer*************
    //http://www.cnblogs.com/cloudgamer/archive/2009/01/06/Tween.html
      var Tween = {
        Linear: function(t,b,c,d){ return c*t/d + b; },
        Quad: {
          easeIn: function(t,b,c,d){
            return c*(t/=d)*t + b;
          },
          easeOut: function(t,b,c,d){
            return -c *(t/=d)*(t-2) + b;
          },
          easeInOut: function(t,b,c,d){
            if ((t/=d/2) < 1) return c/2*t*t + b;
            return -c/2 * ((--t)*(t-2) - 1) + b;
          }
        },
        Cubic: {
          easeIn: function(t,b,c,d){
            return c*(t/=d)*t*t + b;
          },
          easeOut: function(t,b,c,d){
            return c*((t=t/d-1)*t*t + 1) + b;
          },
          easeInOut: function(t,b,c,d){
            if ((t/=d/2) < 1) return c/2*t*t*t + b;
            return c/2*((t-=2)*t*t + 2) + b;
          }
        },
        Quart: {
          easeIn: function(t,b,c,d){
            return c*(t/=d)*t*t*t + b;
          },
          easeOut: function(t,b,c,d){
            return -c * ((t=t/d-1)*t*t*t - 1) + b;
          },
          easeInOut: function(t,b,c,d){
            if ((t/=d/2) < 1) return c/2*t*t*t*t + b;
            return -c/2 * ((t-=2)*t*t*t - 2) + b;
          }
        },
        Quint: {
          easeIn: function(t,b,c,d){
            return c*(t/=d)*t*t*t*t + b;
          },
          easeOut: function(t,b,c,d){
            return c*((t=t/d-1)*t*t*t*t + 1) + b;
          },
          easeInOut: function(t,b,c,d){
            if ((t/=d/2) < 1) return c/2*t*t*t*t*t + b;
            return c/2*((t-=2)*t*t*t*t + 2) + b;
          }
        },
        Sine: {
          easeIn: function(t,b,c,d){
            return -c * Math.cos(t/d * (Math.PI/2)) + c + b;
          },
          easeOut: function(t,b,c,d){
            return c * Math.sin(t/d * (Math.PI/2)) + b;
          },
          easeInOut: function(t,b,c,d){
            return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b;
          }
        },
        Expo: {
          easeIn: function(t,b,c,d){
            return (t==0) ? b : c * Math.pow(2, 10 * (t/d - 1)) + b;
          },
          easeOut: function(t,b,c,d){
            return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b;
          },
          easeInOut: function(t,b,c,d){
            if (t==0) return b;
            if (t==d) return b+c;
            if ((t/=d/2) < 1) return c/2 * Math.pow(2, 10 * (t - 1)) + b;
            return c/2 * (-Math.pow(2, -10 * --t) + 2) + b;
          }
        },
        Circ: {
          easeIn: function(t,b,c,d){
            return -c * (Math.sqrt(1 - (t/=d)*t) - 1) + b;
          },
          easeOut: function(t,b,c,d){
            return c * Math.sqrt(1 - (t=t/d-1)*t) + b;
          },
          easeInOut: function(t,b,c,d){
            if ((t/=d/2) < 1) return -c/2 * (Math.sqrt(1 - t*t) - 1) + b;
            return c/2 * (Math.sqrt(1 - (t-=2)*t) + 1) + b;
          }
        },
        Elastic: {
          easeIn: function(t,b,c,d,a,p){
            if (t==0) return b;  if ((t/=d)==1) return b+c;  if (!p) p=d*.3;
            if (!a || a < Math.abs(c)) { a=c; var s=p/4; }
            else var s = p/(2*Math.PI) * Math.asin (c/a);
            return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
          },
          easeOut: function(t,b,c,d,a,p){
            if (t==0) return b;  if ((t/=d)==1) return b+c;  if (!p) p=d*.3;
            if (!a || a < Math.abs(c)) { a=c; var s=p/4; }
            else var s = p/(2*Math.PI) * Math.asin (c/a);
            return (a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b);
          },
          easeInOut: function(t,b,c,d,a,p){
            if (t==0) return b;  if ((t/=d/2)==2) return b+c;  if (!p) p=d*(.3*1.5);
            if (!a || a < Math.abs(c)) { a=c; var s=p/4; }
            else var s = p/(2*Math.PI) * Math.asin (c/a);
            if (t < 1) return -.5*(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
            return a*Math.pow(2,-10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )*.5 + c + b;
          }
        },
        Back: {
          easeIn: function(t,b,c,d,s){
            if (s == undefined) s = 1.70158;
            return c*(t/=d)*t*((s+1)*t - s) + b;
          },
          easeOut: function(t,b,c,d,s){
            if (s == undefined) s = 1.70158;
            return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b;
          },
          easeInOut: function(t,b,c,d,s){
            if (s == undefined) s = 1.70158; 
            if ((t/=d/2) < 1) return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b;
            return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b;
          }
        },
        Bounce: {
          easeIn: function(t,b,c,d){
            return c - Tween.Bounce.easeOut(d-t, 0, c, d) + b;
          },
          easeOut: function(t,b,c,d){
            if ((t/=d) < (1/2.75)) {
              return c*(7.5625*t*t) + b;
            } else if (t < (2/2.75)) {
              return c*(7.5625*(t-=(1.5/2.75))*t + .75) + b;
            } else if (t < (2.5/2.75)) {
              return c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b;
            } else {
              return c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b;
            }
          },
          easeInOut: function(t,b,c,d){
            if (t < d/2) return Tween.Bounce.easeIn(t*2, 0, c, d) * .5 + b;
            else return Tween.Bounce.easeOut(t*2-d, 0, c, d) * .5 + c*.5 + b;
          }
        }
      }
    <div id="taxiway">
      <div id="move" onclick="transition(this,{ease:Tween.Bounce.easeOut})"></div>
    </div>

    但我不喜欢flash流派的缓动公式,为了使用prototype流派的缓动公式,我进一步改进与抽象化我的缓动函数

    //******************@author : 司徒正美************
      var transition = function(el){
        el.style.position = "absolute";
        var options = arguments[1] || {},
        begin =  options.begin,//开始位置
        change = options.change,//变化量
        duration = options.duration || 500,//缓动效果持续时间
        field = options.field,//必须指定,基本上对top,left,width,height这个属性进行设置
        ftp = options.ftp || 50,
        onStart = options.onStart || function(){},
        onEnd = options.onEnd || function(){},
        ease = options.ease,//要使用的缓动公式
        end = begin + change,//结束位置
        startTime = new Date().getTime();//开始执行的时间
        onStart();
        (function(){
          setTimeout(function(){
            var newTime = new Date().getTime(),//当前帧开始的时间
            timestamp = newTime - startTime,//逝去时间
            delta = ease(timestamp / duration);
            el.style[field] = Math.ceil(begin + delta * change) + "px"
            if(duration <= timestamp){
              el.style[field] = end + "px";
              onEnd();
            }else{
              setTimeout(arguments.callee,1000/ftp);
            }
          },1000/ftp)
        })()
      }
    参数类型说明
    el element 必需,为页面元素
    begin number 必需,开始的位置
    change number 必需,要移动的距离
    duration number 可选,缓动效果持续时间,默认是500ms。建议取300~1000ms。
    field string 必需,要发生变化的样式属性。请在top,left,bottom,right,width与height中选择。
    ftp number 可选,每秒进行多少帧动画,默认50帧,保证流畅播放。一些参考资料,日本动画1秒36帧,中国卡通24帧,赛车游戏60帧。
    ease function 必需,缓动公式,参数为0~1之间的数。可参考我下面给出的45条公式。
    onStart function 可选,在开始时执行。
    onEnd function 可选,在结束时执行。

    prototype流派的缓动公式,只需一个参数(增至45种)

    var tween = {
        easeInQuad: function(pos){
          return Math.pow(pos, 2);
        },
     
        easeOutQuad: function(pos){
          return -(Math.pow((pos-1), 2) -1);
        },
     
        easeInOutQuad: function(pos){
          if ((pos/=0.5) < 1) return 0.5*Math.pow(pos,2);
          return -0.5 * ((pos-=2)*pos - 2);
        },
     
        easeInCubic: function(pos){
          return Math.pow(pos, 3);
        },
     
        easeOutCubic: function(pos){
          return (Math.pow((pos-1), 3) +1);
        },
     
        easeInOutCubic: function(pos){
          if ((pos/=0.5) < 1) return 0.5*Math.pow(pos,3);
          return 0.5 * (Math.pow((pos-2),3) + 2);
        },
     
        easeInQuart: function(pos){
          return Math.pow(pos, 4);
        },
     
        easeOutQuart: function(pos){
          return -(Math.pow((pos-1), 4) -1)
        },
     
        easeInOutQuart: function(pos){
          if ((pos/=0.5) < 1) return 0.5*Math.pow(pos,4);
          return -0.5 * ((pos-=2)*Math.pow(pos,3) - 2);
        },
     
        easeInQuint: function(pos){
          return Math.pow(pos, 5);
        },
     
        easeOutQuint: function(pos){
          return (Math.pow((pos-1), 5) +1);
        },
     
        easeInOutQuint: function(pos){
          if ((pos/=0.5) < 1) return 0.5*Math.pow(pos,5);
          return 0.5 * (Math.pow((pos-2),5) + 2);
        },
     
        easeInSine: function(pos){
          return -Math.cos(pos * (Math.PI/2)) + 1;
        },
     
        easeOutSine: function(pos){
          return Math.sin(pos * (Math.PI/2));
        },
     
        easeInOutSine: function(pos){
          return (-.5 * (Math.cos(Math.PI*pos) -1));
        },
     
        easeInExpo: function(pos){
          return (pos==0) ? 0 : Math.pow(2, 10 * (pos - 1));
        },
     
        easeOutExpo: function(pos){
          return (pos==1) ? 1 : -Math.pow(2, -10 * pos) + 1;
        },
     
        easeInOutExpo: function(pos){
          if(pos==0) return 0;
          if(pos==1) return 1;
          if((pos/=0.5) < 1) return 0.5 * Math.pow(2,10 * (pos-1));
          return 0.5 * (-Math.pow(2, -10 * --pos) + 2);
        },
     
        easeInCirc: function(pos){
          return -(Math.sqrt(1 - (pos*pos)) - 1);
        },
     
        easeOutCirc: function(pos){
          return Math.sqrt(1 - Math.pow((pos-1), 2))
        },
     
        easeInOutCirc: function(pos){
          if((pos/=0.5) < 1) return -0.5 * (Math.sqrt(1 - pos*pos) - 1);
          return 0.5 * (Math.sqrt(1 - (pos-=2)*pos) + 1);
        },
     
        easeOutBounce: function(pos){
          if ((pos) < (1/2.75)) {
            return (7.5625*pos*pos);
          } else if (pos < (2/2.75)) {
            return (7.5625*(pos-=(1.5/2.75))*pos + .75);
          } else if (pos < (2.5/2.75)) {
            return (7.5625*(pos-=(2.25/2.75))*pos + .9375);
          } else {
            return (7.5625*(pos-=(2.625/2.75))*pos + .984375);
          }
        },
     
        easeInBack: function(pos){
          var s = 1.70158;
          return (pos)*pos*((s+1)*pos - s);
        },
     
        easeOutBack: function(pos){
          var s = 1.70158;
          return (pos=pos-1)*pos*((s+1)*pos + s) + 1;
        },
     
        easeInOutBack: function(pos){
          var s = 1.70158;
          if((pos/=0.5) < 1) return 0.5*(pos*pos*(((s*=(1.525))+1)*pos -s));
          return 0.5*((pos-=2)*pos*(((s*=(1.525))+1)*pos +s) +2);
        },
     
        elastic: function(pos) {
          return -1 * Math.pow(4,-8*pos) * Math.sin((pos*6-1)*(2*Math.PI)/2) + 1;
        },
     
        swingFromTo: function(pos) {
          var s = 1.70158;
          return ((pos/=0.5) < 1) ? 0.5*(pos*pos*(((s*=(1.525))+1)*pos - s)) :
            0.5*((pos-=2)*pos*(((s*=(1.525))+1)*pos + s) + 2);
        },
     
        swingFrom: function(pos) {
          var s = 1.70158;
          return pos*pos*((s+1)*pos - s);
        },
     
        swingTo: function(pos) {
          var s = 1.70158;
          return (pos-=1)*pos*((s+1)*pos + s) + 1;
        },
     
        bounce: function(pos) {
          if (pos < (1/2.75)) {
            return (7.5625*pos*pos);
          } else if (pos < (2/2.75)) {
            return (7.5625*(pos-=(1.5/2.75))*pos + .75);
          } else if (pos < (2.5/2.75)) {
            return (7.5625*(pos-=(2.25/2.75))*pos + .9375);
          } else {
            return (7.5625*(pos-=(2.625/2.75))*pos + .984375);
          }
        },
     
        bouncePast: function(pos) {
          if (pos < (1/2.75)) {
            return (7.5625*pos*pos);
          } else if (pos < (2/2.75)) {
            return 2 - (7.5625*(pos-=(1.5/2.75))*pos + .75);
          } else if (pos < (2.5/2.75)) {
            return 2 - (7.5625*(pos-=(2.25/2.75))*pos + .9375);
          } else {
            return 2 - (7.5625*(pos-=(2.625/2.75))*pos + .984375);
          }
        },
     
        easeFromTo: function(pos) {
          if ((pos/=0.5) < 1) return 0.5*Math.pow(pos,4);
          return -0.5 * ((pos-=2)*Math.pow(pos,3) - 2);
        },
     
        easeFrom: function(pos) {
          return Math.pow(pos,4);
        },
     
        easeTo: function(pos) {
          return Math.pow(pos,0.25);
        },
     
        linear:  function(pos) {
          return pos
        },
     
        sinusoidal: function(pos) {
          return (-Math.cos(pos*Math.PI)/2) + 0.5;
        },
     
        reverse: function(pos) {
          return 1 - pos;
        },
     
        mirror: function(pos, transition) {
          transition = transition || tween.sinusoidal;
          if(pos<0.5)
            return transition(pos*2);
          else
            return transition(1-(pos-0.5)*2);
        },
     
        flicker: function(pos) {
          var pos = pos + (Math.random()-0.5)/5;
          return tween.sinusoidal(pos < 0 ? 0 : pos > 1 ? 1 : pos);
        },
     
        wobble: function(pos) {
          return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5;
        },
     
        pulse: function(pos, pulses) {
          return (-Math.cos((pos*((pulses||5)-.5)*2)*Math.PI)/2) + .5;
        },
     
        blink: function(pos, blinks) {
          return Math.round(pos*(blinks||5)) % 2;
        },
     
        spring: function(pos) {
          return 1 - (Math.cos(pos * 4.5 * Math.PI) * Math.exp(-pos * 6));
        },
     
        none: function(pos){
          return 0
        },
     
        full: function(pos){
          return 1
        }
      }
     
    <!doctype html>
    <html dir="ltr" lang="zh-CN">
        <head>
            <meta charset="utf-8"/>
            <meta http-equiv="X-UA-Compatible" content="IE=8">
            <style type="text/css">
                .taxiway{
                    width:800px;
                    height:100px;
                    background:#E8E8FF;
                }
                .move{
                    width:100px;
                    height:100px;
                    background:#a9ea00;
                }
                #panel {
                    float:left;
                    width:810px
                }
                #panel div{
                    float:left;
                    width:88px;
                    border:1px solid #333;
                    height:20px;
                    font-size:11px;
                }
                div.transition {
                    margin-top: 30px;
                    width: 200px;
                    height: 200px;
                    position: relative;
                    margin-bottom:10px;
                }
    
                div.transition div {
                    position: absolute;
                    height: 1px;
                    width: 1px;
                    background: #000;
                }
    
                div.transition span {
                    display: block;
                    position: absolute;
                    border-bottom: 1px solid #dadada;
                    font-size: 10px;
                    color: #888;
                    width: 200px;
                    left: 0px;
                }
                div.transition div#indicator {
                    position:absolute;
                    background-color:#a9ea00;
                    height: 200px;
                    top: 0px;
                    left: 0px;
                }
    
                div.transition div#marker {
                    background-color: #f00;
                    height: 6px;
                    width: 6px;
                    border-radius: 3px;
                    -webkit-border-radius: 3px;
                    -moz-border-radius: 3px;
                    left: 0px;
                    margin-bottom: -3px;
                    margin-left: -3px;
                }
                div.transition div#label {
                    background: transparent;
                    color: #ABD474;
                    font-size: 20px;
                    height: 20px;
                    width: 200px;
                    text-align: center;
                    top: 80px;
                    left: 0px;
                    z-index: -1;
                }
    
            </style>
            <script type="text/javascript">
                var getCoords = function(el){
                    var box = el.getBoundingClientRect(),
                    doc = el.ownerDocument,
                    body = doc.body,
                    html = doc.documentElement,
                    clientTop = html.clientTop || body.clientTop || 0,
                    clientLeft = html.clientLeft || body.clientLeft || 0,
                    top  = box.top  + (self.pageYOffset || html.scrollTop  ||  body.scrollTop ) - clientTop,
                    left = box.left + (self.pageXOffset || html.scrollLeft ||  body.scrollLeft) - clientLeft
                    return { 'top': top, 'left': left };
                };
    
                var getStyle = function(el, style){
                    if(!+"v1"){
                        style = style.replace(/-(w)/g, function(all, letter){
                            return letter.toUpperCase();
                        });
                        var value = el.currentStyle[style];
                        (value == "auto")&&(value = "0px" );
                        return value;
                    }else{
                        return document.defaultView.getComputedStyle(el, null).getPropertyValue(style)
                    }
                }
                var tween = {
                    easeInQuad: function(pos){
                        return Math.pow(pos, 2);
                    },
    
                    easeOutQuad: function(pos){
                        return -(Math.pow((pos-1), 2) -1);
                    },
    
                    easeInOutQuad: function(pos){
                        if ((pos/=0.5) < 1) return 0.5*Math.pow(pos,2);
                        return -0.5 * ((pos-=2)*pos - 2);
                    },
    
                    easeInCubic: function(pos){
                        return Math.pow(pos, 3);
                    },
    
                    easeOutCubic: function(pos){
                        return (Math.pow((pos-1), 3) +1);
                    },
    
                    easeInOutCubic: function(pos){
                        if ((pos/=0.5) < 1) return 0.5*Math.pow(pos,3);
                        return 0.5 * (Math.pow((pos-2),3) + 2);
                    },
    
                    easeInQuart: function(pos){
                        return Math.pow(pos, 4);
                    },
    
                    easeOutQuart: function(pos){
                        return -(Math.pow((pos-1), 4) -1)
                    },
    
                    easeInOutQuart: function(pos){
                        if ((pos/=0.5) < 1) return 0.5*Math.pow(pos,4);
                        return -0.5 * ((pos-=2)*Math.pow(pos,3) - 2);
                    },
    
                    easeInQuint: function(pos){
                        return Math.pow(pos, 5);
                    },
    
                    easeOutQuint: function(pos){
                        return (Math.pow((pos-1), 5) +1);
                    },
    
                    easeInOutQuint: function(pos){
                        if ((pos/=0.5) < 1) return 0.5*Math.pow(pos,5);
                        return 0.5 * (Math.pow((pos-2),5) + 2);
                    },
    
                    easeInSine: function(pos){
                        return -Math.cos(pos * (Math.PI/2)) + 1;
                    },
    
                    easeOutSine: function(pos){
                        return Math.sin(pos * (Math.PI/2));
                    },
    
                    easeInOutSine: function(pos){
                        return (-.5 * (Math.cos(Math.PI*pos) -1));
                    },
    
                    easeInExpo: function(pos){
                        return (pos==0) ? 0 : Math.pow(2, 10 * (pos - 1));
                    },
    
                    easeOutExpo: function(pos){
                        return (pos==1) ? 1 : -Math.pow(2, -10 * pos) + 1;
                    },
    
                    easeInOutExpo: function(pos){
                        if(pos==0) return 0;
                        if(pos==1) return 1;
                        if((pos/=0.5) < 1) return 0.5 * Math.pow(2,10 * (pos-1));
                        return 0.5 * (-Math.pow(2, -10 * --pos) + 2);
                    },
    
                    easeInCirc: function(pos){
                        return -(Math.sqrt(1 - (pos*pos)) - 1);
                    },
    
                    easeOutCirc: function(pos){
                        return Math.sqrt(1 - Math.pow((pos-1), 2))
                    },
    
                    easeInOutCirc: function(pos){
                        if((pos/=0.5) < 1) return -0.5 * (Math.sqrt(1 - pos*pos) - 1);
                        return 0.5 * (Math.sqrt(1 - (pos-=2)*pos) + 1);
                    },
    
                    easeOutBounce: function(pos){
                        if ((pos) < (1/2.75)) {
                            return (7.5625*pos*pos);
                        } else if (pos < (2/2.75)) {
                            return (7.5625*(pos-=(1.5/2.75))*pos + .75);
                        } else if (pos < (2.5/2.75)) {
                            return (7.5625*(pos-=(2.25/2.75))*pos + .9375);
                        } else {
                            return (7.5625*(pos-=(2.625/2.75))*pos + .984375);
                        }
                    },
    
                    easeInBack: function(pos){
                        var s = 1.70158;
                        return (pos)*pos*((s+1)*pos - s);
                    },
    
                    easeOutBack: function(pos){
                        var s = 1.70158;
                        return (pos=pos-1)*pos*((s+1)*pos + s) + 1;
                    },
    
                    easeInOutBack: function(pos){
                        var s = 1.70158;
                        if((pos/=0.5) < 1) return 0.5*(pos*pos*(((s*=(1.525))+1)*pos -s));
                        return 0.5*((pos-=2)*pos*(((s*=(1.525))+1)*pos +s) +2);
                    },
    
                    elastic: function(pos) {
                        return -1 * Math.pow(4,-8*pos) * Math.sin((pos*6-1)*(2*Math.PI)/2) + 1;
                    },
    
                    swingFromTo: function(pos) {
                        var s = 1.70158;
                        return ((pos/=0.5) < 1) ? 0.5*(pos*pos*(((s*=(1.525))+1)*pos - s)) :
                            0.5*((pos-=2)*pos*(((s*=(1.525))+1)*pos + s) + 2);
                    },
    
                    swingFrom: function(pos) {
                        var s = 1.70158;
                        return pos*pos*((s+1)*pos - s);
                    },
    
                    swingTo: function(pos) {
                        var s = 1.70158;
                        return (pos-=1)*pos*((s+1)*pos + s) + 1;
                    },
    
                    bounce: function(pos) {
                        if (pos < (1/2.75)) {
                            return (7.5625*pos*pos);
                        } else if (pos < (2/2.75)) {
                            return (7.5625*(pos-=(1.5/2.75))*pos + .75);
                        } else if (pos < (2.5/2.75)) {
                            return (7.5625*(pos-=(2.25/2.75))*pos + .9375);
                        } else {
                            return (7.5625*(pos-=(2.625/2.75))*pos + .984375);
                        }
                    },
    
                    bouncePast: function(pos) {
                        if (pos < (1/2.75)) {
                            return (7.5625*pos*pos);
                        } else if (pos < (2/2.75)) {
                            return 2 - (7.5625*(pos-=(1.5/2.75))*pos + .75);
                        } else if (pos < (2.5/2.75)) {
                            return 2 - (7.5625*(pos-=(2.25/2.75))*pos + .9375);
                        } else {
                            return 2 - (7.5625*(pos-=(2.625/2.75))*pos + .984375);
                        }
                    },
    
                    easeFromTo: function(pos) {
                        if ((pos/=0.5) < 1) return 0.5*Math.pow(pos,4);
                        return -0.5 * ((pos-=2)*Math.pow(pos,3) - 2);
                    },
    
                    easeFrom: function(pos) {
                        return Math.pow(pos,4);
                    },
    
                    easeTo: function(pos) {
                        return Math.pow(pos,0.25);
                    },
    
                    linear:  function(pos) {
                        return pos
                    },
    
                    sinusoidal: function(pos) {
                        return (-Math.cos(pos*Math.PI)/2) + 0.5;
                    },
    
                    reverse: function(pos) {
                        return 1 - pos;
                    },
    
                    mirror: function(pos, transition) {
                        transition = transition || tween.sinusoidal;
                        if(pos<0.5)
                            return transition(pos*2);
                        else
                            return transition(1-(pos-0.5)*2);
                    },
    
                    flicker: function(pos) {
                        var pos = pos + (Math.random()-0.5)/5;
                        return tween.sinusoidal(pos < 0 ? 0 : pos > 1 ? 1 : pos);
                    },
    
                    wobble: function(pos) {
                        return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5;
                    },
    
                    pulse: function(pos, pulses) {
                        return (-Math.cos((pos*((pulses||5)-.5)*2)*Math.PI)/2) + .5;
                    },
    
                    blink: function(pos, blinks) {
                        return Math.round(pos*(blinks||5)) % 2;
                    },
    
                    spring: function(pos) {
                        return 1 - (Math.cos(pos * 4.5 * Math.PI) * Math.exp(-pos * 6));
                    },
    
                    none: function(pos){
                        return 0
                    },
    
                    full: function(pos){
                        return 1
                    }
                }
    
                var _ = function(id){
                    return document.getElementById(id);
                }
                var transition = function(el){
                    el.style.position = "absolute";
                    var options = arguments[1] || {},
                    begin =  options.begin,//开始位置
                    change = options.change,//变化量
                    duration = options.duration || 500,//缓动效果持续时间
                    field = options.field,//必须指定,基本上对top,left,width,height这个属性进行设置
                    ftp = options.ftp || 50,
                    onEnd = options.onEnd || function(){},
                    ease = options.ease,//要使用的缓动公式
                    end = begin + change,//结束位置
                    startTime = new Date().getTime();//开始执行的时间
                    (function(){
                        setTimeout(function(){
                            var newTime = new Date().getTime(),//当前帧开始的时间
                            timestamp = newTime - startTime,//逝去时间
                            delta = ease(timestamp / duration);
                            el.style[field] = Math.ceil(begin + delta * change) + "px"
                            if(duration <= timestamp){
                                el.style[field] = end + "px";
                                onEnd();
                            }else{
                                setTimeout(arguments.callee,1000/ftp);
                            }
                        },1000/ftp)
                    })()
                }
    
                if (typeof Array.prototype['max'] == 'undefined') {
                    Array.prototype.map = function(fn, thisObj) {
                        var scope = thisObj || window;
                        var a = [];
                        for ( var i=0, j=this.length; i < j; ++i ) {
                            a.push(fn.call(scope, this[i], i, this));
                        }
                        return a;
                    };
                    Array.prototype.max = function(){
                        return Math.max.apply({},this)
                    }
                    Array.prototype.min = function(){
                        return Math.min.apply({},this)
                    }
                }
    
                var range = function(start,end){
                    var _range = []
                    for(var i = start,l=end-start;i<l;i++){
                        _range.push(i)
                    }
                    return _range
                }
    
                var draw = function(ease){
                    var demo = _("transition");
                    demo.innerHTML = "";//还原!
                    //***********绘制控制台********************
                    var values = range(0,200).map(function(v){
                        return  tween[ease](v/200) * 200;
                    }),
                    max = Math.max(200, values.max()),
                    min = Math.min(0, values.min());
                    if (min==max) {
                        min = 0;
                        max = 200;
                    }
                    var factor = 200/(max-min),
                    grid = '<span style="bottom:'+Math.round((0-min)*factor)+'px">0</span>'+
                        '<span style="bottom:'+Math.round((200-min)*factor)+'px">1</span>',
                    graph = range(0,200).map(function(v){
                        return '<div style="left:'+v+'px;bottom:'+Math.round((values[v]-min)*factor)+'px;height:1px"></div>';
                    }).join('') + '<div id="indicator" style="display:none">'
                        +'</div><div id="marker" style="display:none"></div><div id="label"></div>';
                    demo.innerHTML = grid + graph;
                    var indicator = _("indicator"),
                    marker = _("marker"),
                    label = _("label"),
                    demoTransition = function(pos){
                        var value = tween[ease](pos);
                        indicator.style.display = "block";
                        marker.style.display = "block";
                        marker.style.left = Math.round(pos*200)+'px';
                        marker.style.bottom = Math.round((value*200-min)*factor)+'px';
                        label.innerHTML = Math.round(pos*200)+'px';
                        return value;
                    }
                    transition(indicator,{field:"left",begin:parseFloat(getCoords(demo).left),change:200,
                        ease:demoTransition})
                }
    
                window.onload = function(){
                    var panelHTML = function(){
                        var builder = [];
                        var _temp = 'Back Circ Cubic Expo Quad Quart Quint Sine'.split(' ');
                        var ease = _temp.map(function(v){
                            return 'easeIn'+v;
                        });
                        ease = ease.concat(_temp.map(function(v){
                            return 'easeOut'+v;
                        }));
                        ease = ease.concat(_temp.map(function(v){
                            return 'easeInOut'+v;
                        }));
                        ease = ease.concat('blink bounce bouncePast easeFrom easeFromTo easeOutBounce easeTo elastic'.split(' '));
                        ease = ease.concat('flicker full linear mirror none pulse reverse sinusoidal spring swingTo swingFrom swingFromTo wobble'.split(' '))
                        for(var i =0,l=ease.length;i<l;i++){
                            builder.push("<div onclick='draw(this.innerHTML)'>");
                            builder.push(ease[i]);
                            builder.push("</div>");
                        }
                        return builder.join('');
                    }
                    var panel = document.createElement("div");
                    panel.id = "panel"
                    panel.innerHTML = panelHTML();
                    _("transition").parentNode.insertBefore(panel,_("transition").nextSibling);
    
                }
            </script>
            <title>缓动BY司徒正美</title>
        </head>
        <body>
            <div class="taxiway">
                <div class="move" onclick="transition(this,{field:'left',begin:parseFloat(getCoords(this).left),change:700,ease:tween.bouncePast})"></div>
            </div>
            <div class="taxiway">
                <div class="move" onclick="transition(this,{field:'width',begin:parseFloat(getStyle(this,'width')),change:300,ease:tween.spring})"></div>
            </div>
    <span class="clear"></span>
    <h2>请点击下表的格子</h2>
            <div id="transition" class="transition">
    
            </div>
    
        </body>
    </html>
     

    除了这45条公式外,我们还可以制定自己的缓动公式。正如我在上面表格中提到, 它在运行过程是不执行回调函数时,但你们可以在运行框中看到,我可以实现一边移动一边记录点的坐标。这是怎样实现的呢? 我们只要把上面的缓动公式的任何一条塞进一个只有一个参数的函数就行了。当然此函数要有返回,供继续向下调用。以下就是一个模板:

    var myTween = function(pos){ //缓动公式
        var value = tween[ease](pos);
        //***********这上面是固定的**************
        indicator.style.display = "block";
        marker.style.display = "block";
        marker.style.left = Math.round((pos*200))+'px';
        marker.style.bottom = Math.round(((value*200)-min)*factor)+'px';
        label.innerHTML = Math.round((pos*200))+'px';
        //************这下面是固定的*************
        return value;
      }
    <div class="taxiway">
      <div class="move" onclick="transition(this,{field:'left',begin:parseFloat(getCoords(this).left),change:700,ease:tween.bouncePast})"></div>
    </div>
    <div class="taxiway">
      <div class="move" onclick="transition(this,{field:'width',begin:parseFloat(getStyle(this,'width')),change:300,ease:tween.spring})"></div>
    </div>

    http://www.cnblogs.com/rubylouvre/archive/2009/09/17/1567607.html

  • 相关阅读:
    假期第五天
    假期第四天
    假期第三天
    假期第二天
    假期第一天
    《如何高效学习》读书笔记六
    十天冲刺-第八天
    十天冲刺第七天
    十天冲刺-第六天
    十天冲刺-第五天
  • 原文地址:https://www.cnblogs.com/jiangxiaobo/p/6244381.html
Copyright © 2020-2023  润新知