移动端 transition动画函数的封装(仿Zepto)以及 requestAnimationFrame动画函数封装(仿jQuery)

 

移动端 css3 transition 动画 ,requestAnimationFrame 动画  对于性能的要求,h5优先考虑;

移动端 单页有时候 制作只用到简单的css3动画即可,我们封装一下,没必要引入zepto框架,把zepto的动画模块从Zepto 扒下来,加以改造独立;用于生产环境;下面是 Demo栗子;

上面图片对应的 js

         var leftsbox=document.getElementById("leftsbox");
	  var boxdiv=leftsbox.getElementsByTagName("div");
	  leftsbox.onclick=function(){
		   for(var i=0;i<boxdiv.length;i++){
			         var that=boxdiv[i];
			 			 transform(that,{ translate3d:'220px,10px,0',left:'1em',opacity:0.2,perspective:'400px', rotateY:'30deg'},800,'cubic-bezier(0.15, 0.5, 0.25, 1.0)',function(){
							   this.innerHTML='结束回调'+this.innerHTML;
						},100*i);
			} //for

  

再看看另外一种 常见的 如下图

上面对用的 js 代码

  
                       var nav=document.querySelector(".nav");
			var nava=nav.getElementsByTagName("li");
			var content=document.querySelector(".content");
			var ulcontent=document.getElementById("ulcontent");
			ulcontent.style.width=nav.offsetWidth*nava.length+'px';
			for(var i=0;i<nava.length;i++) {
				nava[i].index=i;
				nava[i].onclick=function(){
					var that=this;
					var now=-(that.index)*content.offsetWidth+'px';

					 transform(ulcontent,{translate3d:''+now+',0,0',},'linear',function(){
					//console.log('success   回调函数');
					})
				}//click end
			}

  htm结构

 <ul class="nav">
	        <li ><a >首页</a></li>
	        <li ><a >插件</a></li>
	        <li ><a >新闻</a></li>
	        <li ><a >其他</a></li>
	    </ul>
   
     <div class="content">
        <ul id="ulcontent" >
        <li ><img src="../../images/1a.jpg"></li>
        <li ><img src="../../images/2a.jpg"></li>
        <li ><img src="../../images/3a.jpg"></li>
        <li style="background:#ddd;" >44444444444</li>
        </ul>
     </div>

  

基于zepto动画独立出来,原先zepto 动画书写方式

$("#banners").animate(
    {
        translate3d:'220px,10px,0',
		left:'1em',
		opacity:0.2,
		perspective:'400px', 
		rotateY:'30deg'
    },
    800,
   'cubic-bezier(0.15, 0.5, 0.25, 1.0)',
    function(){  alert('回调'); },
   1000    
)

  

改写后 独立与zepto的 动画函数 语法如下 

transform(dom元素,{ transitionProperty:css值},transitionDuration(单位:毫秒),transitionTiming,transitionend回调,transitionDelay(单位:毫秒)); 

transform(dom元素,animationName,animationDuration(单位:毫秒),animationTiming,animationend回调,animationDelay(单位:毫秒)); 

 

/*
* js transfrom
* @param obj {obj}    原生dom对象
* @param properties  {json} ||string     { translate3d:'220px,10px,0',left:'1em',opacity:0.2, rotateY:'30deg'} || animationName 多个可以以逗号分割 如 'fadeIn,sliderDown';
* @param duration {number}      css3持续时间 秒 默认400毫秒
* @param ease {str}           默认linear 支持  cubic-bezier(0.42,0,1,1)写法;
* @param callback {function}    回调函数   
* @param delay {number}    延迟时间    

*/

 

/* http://www.cnblogs.com/surfaces/
    * @param properties 为  {}  或者 string     ;如果 properties= string 为animation-name
    * transform(elem, properties)
    * transform(elem, properties, ease)
    * transform(elem, properties, ease, delay)
    * transform(elem, properties, ease, callback, delay)
    * transform(elem, properties, callback)
    * transform(elem, properties, callback, delay)
    * transform(elem, properties, duration )
    * transform(elem, properties, duration, ease)
    * transform(elem, properties, duration, delay)
    * transform(elem, properties, duration, callback)	
    * transform(elem, properties, duration, callback,delay)   
    * transform(elem, properties, duration, ease, delay)
    * transform(elem, properties, duration, ease, callback)   
    * transform(elem, properties, duration, ease, callback,delay)
    
    transform(elem,{translateX:'150px',left:'1em',opacity:0.2, rotateY:'40deg'},600,'linear',function(){  console.log('结束回调') },200) ; 

    transform(elem, keyframesName,600,'linear',function(){  console.log('结束回调') },200) ; 
*/

  

    

关于兼容性:几乎与zepto一致 ,也支持 动画帧 keyframe,个人觉得  keyframe移动端 兼容性不是很好,尤其手机自带的浏览器(原生app 调用 内嵌H5页面,小问题还是蛮多的),适合无关dom数据操作,只显示美化用;

 

 另外,我们需要了解下  css3 transition-duration的属性写法有多种;如下

1). 过渡单个属性:

 transition-property:opacity;
transition-duration:2s;
transition-timing-function:ease-in;
transition-delay:0;

  

2). 过渡多个属性:
[1]. 上下一一对应型:
 

transition-property:opacity left;
transition-duration:2s, 4s;
transition-timing-function:ease-in;
transition-delay:0;

  

此时:opacity过渡时间为2s,left过渡时间为4s。

[2]. 循环对应型:
 

transition-property:opacity left width height;
transition-duration:2s, 4s;
transition-timing-function:ease-in;
transition-delay:0;

  

此时:opacity和width过渡时间为2s,left和height过渡时间为4s。

3). transition简写模式:
顺序为:transition-property transition-duration transition-timing-function transition-delay


  /*单个属性:*/
-moz-transition:background 0.5s ease-out 0s;
/*多个属性:*/
-moz-transition:background, 0.5s ease-out 0s, color 0.4 ease-out 0s;

 

 过渡持续时间
transition-duration 属性规定了一个过渡从初始状态到目标状态的持续时间。它接受以秒或毫秒的值(例如,2.3S和2300ms都是指2.3秒)。
尽管规范明确规定了过渡值必须为正数,但 Opera 仍接受-5S的值,至少对于getComputedStyle()来说是这样的。虽然规范中并没有限制属性值的大小,但 Opera 和 IE 不接受低于10ms的值。而 WebKit 在 getComputedStyle()执行中有个小bug,例如:返回值0.009999999776482582s会取代0.01s。

 

过渡延迟时间
transition-delay 属性规定了在执行一个过渡之前的等待时间,同样使用值。Delay 可以是负值,但这会导致动画无法平滑过渡。
IE 和 Opera 不接受 transition-duration 在-10ms和10ms之间的值。WebKit 的 floating point 也会在这儿出现。

http://www.cnblogs.com/surfaces/ 

 

另外属性相似点 animation-name:keyfrmaes1,keyfrmaes2;以逗号分割;

 其他的

zepto核心动画但是实现了大部分常用的动画;在阅读器zepto核心动画源码的 时候,自己手动封装 实践;就机会发现 看似读懂其实不懂;自己正真封装调试后;才发现哪些巧;

 

封装后的   transform.js  源码如下

 参数说明

/*
* js transfrom
* @param obj {obj}    原生dom对象
* @param properties  {json} || string     { translate3d:'220px,10px,0',left:'1em',opacity:0.2,perspective:'400px', rotateY:'30deg'} ||animation-name
* @param duration {number}      css3持续时间 秒 默认400毫秒
* @param ease {str}         transition-timing-function  默认linear 支持  cubic-bezier(0.42,0,1,1)写法;
* @param callback {function}    回调函数   
* @param delay {number}    延迟时间    

*/

 使用方法

   /* http://www.cnblogs.com/surfaces/
    * @param properties 为  {}  或者 string     ;如果 properties= string 为animation-name
    * transform(elem, properties)
    * transform(elem, properties, ease)
    * transform(elem, properties, ease, delay)
    * transform(elem, properties, ease, callback, delay)
    * transform(elem, properties, callback)
    * transform(elem, properties, callback, delay)
    * transform(elem, properties, duration )
    * transform(elem, properties, duration, ease)
    * transform(elem, properties, duration, delay)
    * transform(elem, properties, duration, callback)	
    * transform(elem, properties, duration, callback,delay)   
    * transform(elem, properties, duration, ease, delay)
    * transform(elem, properties, duration, ease, callback)   
    * transform(elem, properties, duration, ease, callback,delay)
    
    transform(elem,{translateX:'150px',left:'1em',opacity:0.2,perspective:'400px', rotateY:'40deg'},600,'linear',function(){  console.log('结束回调') },200) ; 
    transform(elem, keyframesName,600,'linear',function(){  console.log('结束回调') },200) ; 
*/

 移动端 transform.js 源码如下;双击直接复制即可; 

;(function(window,document,undefined){var prefix=function(){var div=document.createElement("div");var cssText="-webkit-transition:all .1s;-moz-transition:all .1s; -Khtml-transition:all .1s; -o-transition:all .1s; -ms-transition:all .1s; transition:all .1s;";div.style.cssText=cssText;var style=div.style;var dom="";if(style.webkitTransition){dom="webkit"}else{if(style.MozTransition){dom="moz"}else{if(style.khtmlTransition){dom="Khtml"}else{if(style.oTransition){dom="o"}else{if(style.msTransition){dom="ms"}}}}}div=null;if(dom){return{dom:dom,lowercase:dom,css:"-"+dom+"-",js:dom[0].toUpperCase()+dom.substr(1)}}else{return false}}();var transitionEnd=function(){var el=document.createElement("div");var transEndEventNames={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd",msTransition:"MSTransitionEnd",transition:"transitionend"};for(var name in transEndEventNames){if(el.style[name]!==undefined){return transEndEventNames[name]}}el=null;return false}();var animationEnd=(function(){var eleStyle=document.createElement("div").style;var verdors=["a","webkitA","MozA","OA","msA"];var endEvents=["animationend","webkitAnimationEnd","animationend","oAnimationEnd","MSAnimationEnd"];var animation;for(var i=0,len=verdors.length;i<len;i++){animation=verdors[i]+"nimation";if(animation in eleStyle){return endEvents[i]}}return"animationend"}());var supportedTransforms=/^((translate|rotate|scale)(X|Y|Z|3d)?|matrix(3d)?|perspective|skew(X|Y)?)$/i;var dasherize=function(str){return str.replace(/::/g,"/").replace(/([A-Z]+)([A-Z][a-z])/g,"$1_$2").replace(/([a-z\d])([A-Z])/g,"$1_$2").replace(/_/g,"-").toLowerCase()};function transform(obj,properties,duration,ease,callback,delay){if(!obj){return}if(typeof duration=="undefined"){duration=400;ease="linear";callback=undefined;delay=undefined}if(typeof duration=="string"){if(typeof ease=="number"){delay=ease;callback=undefined}if(typeof ease=="function"){delay=callback;callback=ease}ease=duration;duration=400}else{if(typeof duration=="function"){if(typeof ease=="number"){delay=ease}callback=duration;duration=400;ease="linear"}else{if(typeof duration=="number"){if(typeof ease=="undefined"){ease="linear"}else{if(typeof ease=="string"){ease=ease}else{if(typeof ease=="function"){if(typeof callback=="number"){delay=callback}callback=ease;ease="linear"}else{if(typeof ease=="number"){delay=ease;ease="linear"}}}}if(typeof callback=="number"){delay=callback;callback=undefined}}}}delay=(typeof delay=="number")?delay:0;var endEvent=transitionEnd;var nowTransition=prefix.js+"Transition";var nowTransform=prefix.js+"Transform";var prefixcss=prefix.css;if(!prefix.js){nowTransition="transition";nowTransform="transform";prefixcss=""}var transitionProperty,transitionDuration,transitionTiming,transitionDelay;var animationName,animationDuration,animationTiming,animationDelay;var key,cssValues={},cssProperties,transforms="";var transform;var cssReset={};var css="";var cssProperties=[];transform=prefixcss+"transform";cssReset[transitionProperty=prefixcss+"transition-property"]=cssReset[transitionDuration=prefixcss+"transition-duration"]=cssReset[transitionTiming=prefixcss+"transition-timing-function"]=cssReset[transitionDelay=prefixcss+"transition-delay"]=cssReset[animationName=prefixcss+"animation-name"]=cssReset[animationDuration=prefixcss+"animation-duration"]=cssReset[animationTiming=prefixcss+"animation-timing-function"]=cssReset[animationDelay=prefixcss+"animation-delay"]="";if(typeof properties=="string"){cssValues[animationName]=properties;cssValues[animationDuration]=duration+"ms";cssValues[animationTiming]=(ease||"linear");cssValues[animationDelay]=(delay)+"ms";endEvent=animationEnd}else{endEvent=transitionEnd;for(key in properties){if(supportedTransforms.test(key)){transforms+=key+"("+properties[key]+") "}else{cssValues[key]=properties[key],cssProperties.push(dasherize(key))}}if(transforms){cssValues[transform]=transforms,cssProperties.push(transform)}if(duration>0&&typeof properties==="object"){cssValues[transitionProperty]=cssProperties.join(", ");cssValues[transitionDuration]=duration+"ms";cssValues[transitionTiming]=(ease||"linear");cssValues[transitionDelay]=(delay)+"ms"}}for(var attr in cssValues){css+=dasherize(attr)+":"+cssValues[attr]+";"}obj.style.cssText=obj.style.cssText+";"+css;obj.clientLeft;if(!callback){return}var fired=false;var handler=function(event){if(typeof event!=="undefined"){if(event.target!==event.currentTarget){fired=true;return}}callback&&callback.apply(obj,arguments);fired=true;obj.removeEventListener(endEvent,arguments.callee,false)};if(obj.addEventListener){obj.addEventListener(endEvent,handler,false)}if(!endEvent||duration<=0){setTimeout(function(){handler()});return}setTimeout(function(){if(fired){return}handler()},parseInt((duration+delay)+25))}window.transform=transform})(window,document);

 部分示例 使用代码

 

<style>
*{ margin:0; padding:0;}
ul,li{ list-style:none; margin:0; padding:0;}

html{-webkit-touch-callout:none;-webkit-user-select:none;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent; font-size:62.5%; }
body{ font-size:14px;}

	  
#leftsbox{ width:88%; height:auto; overflow:hidden; border:2px solid red; margin:0 auto; padding:30px 0;}
#leftsbox div{ width:80px; height:80px; background:#6C6; margin-bottom:10px; position:relative; left:0; backface-visibility:hidden;  
-webkit-animation-fill-mode:forwards; 
	animation-fill-mode:forwards;
}

@keyframes keyframesName{
	0%{ transform:translateX(0)}
	100%{transform:translateX(600px) }
}
@-webkit-keyframes keyframesName{
	0%{ -webkit-transform:translateX(0)}
	100%{-webkit-transform:translateX(600px) }
}

    </style>
    <div id="leftsbox">
      <div class="leftssa" style="background:red">1 lefts 22   RAF</div>
      <div class="leftssa " style="width:120px; background:#FF0">2 lefts 22   RAF</div>
      <div class="leftssa"> 333lefts 22   RAF</div>
    </div>
    
<script>

	  var leftsbox=document.getElementById("leftsbox");
	  var boxdiv=leftsbox.getElementsByTagName("div");
	  leftsbox.onclick=function(e){
		 
		   for(var i=0;i<boxdiv.length;i++){
			         var that=boxdiv[i];
			 // transform(that,{ translate3d:'220px,10px,0',left:'1em',opacity:0.2,perspective:'400px', width:'200px', rotateY:'30deg'},800,'cubic-bezier(0.15, 0.5, 0.25, 1.0)',function(){
				//  this.innerHTML='结束回调'+this.innerHTML;
			// },1000*i);
						
						
                    /*   properties json(css3 属性)  或者 string(css3动画名称)
				      * transform(elem, properties)
					  * transform(elem, properties, ease)
					  * transform(elem, properties, ease, delay)
					  * transform(elem, properties, ease, callback, delay)
					  * transform(elem, properties, callback)
					  * transform(elem, properties, callback, delay)
					  * transform(elem, properties, duration )
					  * transform(elem, properties, duration, ease)
					  * transform(elem, properties, duration, delay)
					  * transform(elem, properties, duration, callback)	
					  * transform(elem, properties, duration, callback,delay)   
					  * transform(elem, properties, duration, ease, delay)
					  * transform(elem, properties, duration, ease, callback)   
					  * transform(elem, properties, duration, ease, callback,delay)
                     */
					 
					 //测试 properties为 keyframes
					 var keyframes='keyframesName';
					 //transform(that, keyframes) 
					 //transform(that, keyframes,'ease') 
					 //transform(that, keyframes,'ease',1000*i) 
					 //transform(that, keyframes,'ease',function(){ this.innerHTML='结束回调'+this.innerHTML;},1000*i) 
					
					//transform(that, keyframes,function(){ this.innerHTML='结束回调'+this.innerHTML;})
					//transform(that, keyframes,function(){ this.innerHTML='结束回调'+this.innerHTML;},1000*i) 
					
					//transform(that, keyframes,600)  
					//transform(that, keyframes,600,ease') 
					//transform(that, keyframes,600,1000*i) ;
					//transform(that, keyframes,600,function(){ this.innerHTML='结束回调'+this.innerHTML;}) ;  
					//transform(that, keyframes,600,function(){ this.innerHTML='结束回调'+this.innerHTML;},1000*i) ;
					
					//transform(that, keyframes,600,'ease',1000*i) 
					// transform(that, keyframes,600,'ease',function(){ this.innerHTML='结束回调'+this.innerHTML;}) ;
					// transform(that, keyframes,600,function(){ this.innerHTML='结束回调'+this.innerHTML;},1020*i) ; 
					// transform(that, keyframes,600,function(){ this.innerHTML='结束回调'+this.innerHTML;},-20*i) ; //css3 animation-delay 支持负数 直接进入到时间点
					
					 //测试 properties为 Json
					 var json={translate3d:'600px,10px,0',left:'1em',opacity:0.2,perspective:'400px', width:'200px', rotateY:'30deg'}
					 //transform(that,json);
					  // transform(that, json,'ease') 
					 //transform(that, json,'ease',1000*i) 
					 //transform(that, json,'ease',function(){ this.innerHTML='结束回调'+this.innerHTML;},1000*i) 
					
					//transform(that, json,function(){ this.innerHTML='结束回调'+this.innerHTML;})
					//transform(that, json,function(){ this.innerHTML='结束回调'+this.innerHTML;},1000*i) 
					
					//transform(that, json,600)  
					//transform(that, json,600,'ease') 
					//transform(that, json,600,1000*i) ;
					//transform(that, json,600,function(){ this.innerHTML='结束回调'+this.innerHTML;}) ;  
					// transform(that, json,600,function(){ this.innerHTML='结束回调'+this.innerHTML;},1000*i) ;
					
					//transform(that, json,600,'ease',1000*i) 
					//transform(that, json,600,'ease',function(){ this.innerHTML='结束回调'+this.innerHTML;}) ;
					transform(that, json,600,'ease',function(){ this.innerHTML='结束回调'+this.innerHTML;},1000*i) ;
					//transform(that, json,600,'ease',function(){ this.innerHTML='结束回调'+this.innerHTML;},-100*i) ; ////css3 transition-delay 支持负数 直接进入到时间点
					
					   
            } //for end
		 
	  }	  			
</script>

  

唯一的缺点  scrollTop 缓动不支持; 这里有个简易的  函数 类似jquery  语法几乎一样 基于时间 算法 使用 requestAnimationFrame  

先看效果

 

相关布局

<ul id="inner" >
        <li><a class="on" >护肤</a></li>
        <li><a >彩妆</a></li>
        <li><a >洗护</a></li>
        <li><a >套装</a></li>
    </ul>
    
     <ul class="uls" id="uls">
        <li id='li1'></li>
         <li id='li12'></li>
         <li id='li3'></li>
  
 </ul>

    
    
 <div class="box22">
 <div class="boxs">1111111111111111111111111111111111</div>
  <div class="boxs">22222222222222222222222222222</div>
  
   <div class="boxs">33333333333333333333333</div>
  <div class="boxs" style="height:100px;">4444444444444444444最后一个 </div>
 </div>
 
 
 <div id="gpTop">tops</div>

 

 

上图相关js

  document.getElementById("gpTop").onclick=function(e){
              var  that=this;
                 startMove(that,{"scrollTop":0},function(){
                    that.innerHTML='到顶了';
                     });  //width: 206px; height: 101px; opacity: 0.3;
                }
                
                
                var inner=document.getElementById("inner");
                var inlione=inner.getElementsByTagName("li");
                var box=document.querySelectorAll(".boxs");
                
                for(var i=0;i<inlione.length;i++) {  
                 inlione[i].index=i;
                 inlione[i].onclick=function(e){
                 var that=this;
                // console.log(that.index);
                 var box2=box[that.index];
                 var nowTop=getOffest(box2).top;
                // console.log(nowTop);
                 
                  animate(window,{"scrollTop":nowTop},function(){
                    //console.log('success');
                    });
           }
             
        }

如果通过原生js  获取  jquey的offset().top  的值呢 如下  这里

  
var getOffest=function (obj){
var top=0,left=0;
if(obj){
 while(obj.offsetParent){
      top += obj.offsetTop;
      left += obj.offsetLeft;
      obj = obj.offsetParent;
   }
 }

  return{
  top : top,
  left : left
  }
}

 

animate.js基于 requestAnimationFrame  兼容ie8+    (单位px  未做rem 兼容处理 ,没有jquery的 stop()处理)

语法如下

 

animate(dom元素,{"left":1300,"opacity":90},持续时间(毫秒),缓动形式(tween函数),回调函数);

			  animate(element,{"left":1300,"opacity":90},2000,'easeOut',function sa(){
			  console.log('回调函数');
			 });  

 

为什么要用   requestAnimationFrame  。

浏览器可以优化并行的动画动作,更合理的重新排列动作序列,并把能够合并的动作放在一个渲染周期内完成,从而呈现出更流畅的动画效果。比如,通过requestAnimationFrame(),JS动画能够和CSS动画/变换或SVG SMIL动画同步发生。另外,如果在一个浏览器标签页里运行一个动画,当这个标签页不可见时,浏览器会暂停它,这会减少CPU,内存的压力,节省电池电量。

相信日后  requestAnimationFrame 在移动端发展的前景是相当不错的,目前安卓上感觉还是有点丢帧。抖动明显;

 

requestAnimationFrame  一个简单的例子

var start = null;
var ele = document.getElementById("j_precent");
var progress = 0;

function step() {
    progress += 1;
    ele.style.width = progress + "%";
    ele.innerHTML=progress + "%";
    if (progress < 100) {
        requestAnimationFrame(step);
    }
}

document.getElementById("btn_run").addEventListener("click", function() {
    ele.style.width = "1px";
    progress = 0;
     step()
}, false);

  

如下   jQuery1.6.2还用 requestAnimationFrame;

// Start an animation from one number to another
custom: function( from, to, unit ) {
    var self = this,
        fx = jQuery.fx,
        raf;

    this.startTime = fxNow || createFxNow();
    this.start = from;
    this.end = to;
    this.unit = unit || this.unit || ( jQuery.cssNumber[ this.prop ] ? "" : "px" );
    this.now = this.start;
    this.pos = this.state = 0;

    function t( gotoEnd ) {
        return self.step(gotoEnd);
    }

    t.elem = this.elem;

    if ( t() && jQuery.timers.push(t) && !timerId ) {
        // Use requestAnimationFrame instead of setInterval if available
        if ( requestAnimationFrame ) {
            timerId = true;
            raf = function() {
                // When timerId gets set to null at any point, this stops
                if ( timerId ) {
                    requestAnimationFrame( raf );
                    fx.tick();
                }
            };
            requestAnimationFrame( raf );
        } else {
            timerId = setInterval( fx.tick, fx.interval );
        }
    }
},

  http://stackoverflow.com/questions/7999680/why-doesnt-jquery-use-requestanimationframe

为啥jquery 放弃,上面说的比较完善;

 

animate源码如下

;(function() {

  var lastTime = 0;
  var vendors = ['ms', 'moz', 'webkit', 'o'];

  for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
    window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];
    window.cancelAnimationFrame = window[vendors[x]+'CancelAnimationFrame'] || window[vendors[x]+'CancelRequestAnimationFrame'];
  }

  if (!window.requestAnimationFrame) {
    window.requestAnimationFrame = function(callback, element) {
      var currTime = new Date().getTime();
      var timeToCall = Math.max(0, 16 - (currTime - lastTime));
      var id = window.setTimeout(function() { callback(currTime + timeToCall); },
        timeToCall);
      lastTime = currTime + timeToCall;
      return id;
    };
  }

  if (!window.cancelAnimationFrame) {
    window.cancelAnimationFrame = function(id) {
      clearTimeout(id);
    };
  }

}());

var getStyle=function (obj,attr){
    return obj.currentStyle ? obj.currentStyle[attr]:getComputedStyle(obj)[attr];
}    
      
var Tween = {
    linear: function (t, b, c, d){  //匀速
        return c*t/d + b;
    },
    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;
    },
    easeBoth: function(t, b, c, d){  //加速减速曲线
        if ((t/=d/2) < 1) {
            return c/2*t*t + b;
        }
        return -c/2 * ((--t)*(t-2) - 1) + b;
    },
    easeInStrong: function(t, b, c, d){  //加加速曲线
        return c*(t/=d)*t*t*t + b;
    },
    easeOutStrong: function(t, b, c, d){  //减减速曲线
        return -c * ((t=t/d-1)*t*t*t - 1) + b;
    },
    easeBothStrong: 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;
    },
    elasticIn: function(t, b, c, d, a, p){  //正弦衰减曲线(弹动渐入)
        if (t === 0) { 
            return b; 
        }
        if ( (t /= d) == 1 ) {
            return b+c; 
        }
        if (!p) {
            p=d*0.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;
    },
    elasticOut: function(t, b, c, d, a, p){    //正弦增强曲线(弹动渐出)
        if (t === 0) {
            return b;
        }
        if ( (t /= d) == 1 ) {
            return b+c;
        }
        if (!p) {
            p=d*0.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;
    },    
    elasticBoth: function(t, b, c, d, a, p){
        if (t === 0) {
            return b;
        }
        if ( (t /= d/2) == 2 ) {
            return b+c;
        }
        if (!p) {
            p = d*(0.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 - 0.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 )*0.5 + c + b;
    }
}




function animate(obj,json,times,fx,fn){
    
    if( typeof times == 'undefined' ){
        times = 400;
        fx = 'linear';
    }
    
    if( typeof times == 'string' ){
        if(typeof fx == 'function'){
            fn = fx;
        }
        fx = times;
        times = 400;
    }
    else if(typeof times == 'function'){
        fn = times;
        times = 400;
        fx = 'linear';
    }
    else if(typeof times == 'number'){
        if(typeof fx == 'function'){
            fn = fx;
            fx = 'linear';
        }
        else if(typeof fx == 'undefined'){
            fx = 'linear';
        }
    }
    
    

    var iCur = {};
    var startTime = +new Date();
    
    for(var attr in json){
        iCur[attr] = 0;
        
        if( attr == 'opacity' ){
            if(Math.round(getStyle(obj,attr)*100) == 0){
                iCur[attr] = 0;
            }
            else{
                iCur[attr] = Math.round(getStyle(obj,attr)*100) || 100;
            }
        }
        else if(attr == 'scrollTop' ){
                 iCur[attr]=window.scrollY||document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop;;         
            }
        else{
            iCur[attr] = parseInt(getStyle(obj,attr)) || 0;
        }
        
    }
    
    
        if(obj.timer){
        cancelAnimationFrame(obj.timer)
        }
        //obj.timer=null;
    
    function update(){    
        
        var changeTime = +new Date();
        var scale = 1 - Math.max(0,startTime - changeTime + times)/times;
        
        for(var attr in json){
            
            var value = Tween[fx](scale*times, iCur[attr] , json[attr] - iCur[attr] , times );
            
            if(attr == 'opacity'){
                obj.style.filter = 'alpha(opacity='+ value +')';
                obj.style.opacity = value/100;
            }else if(attr == 'scrollTop'){
                 window.scrollTo(0, value); 
           }else{
                obj.style[attr] = value + 'px';
            }
            
        }
        
        if(scale == 1){
                cancelAnimationFrame(obj.timer);
                 fn&&fn.apply(this, arguments);
        }else{
             //setup_fps_meters();
            obj.timer=requestAnimationFrame(arguments.callee);
            }
        
   }//update  end
   //    requestAnimationFrame(update);  //某些时候 画的过快  有bug
    update();
    
}    
    
    
Tween.js  
/*

* t : time 已过时间
* b : begin 起始值
* c : count 总的运动值
* d : duration 持续时间
* */
 
//Tween.linear();

 

 

css cubic-bezier 缓动备份查看  注意点:x1,x2,y1,y2的值范围在[0, 1]。

var easingMap = {
  "linear": [0.250, 0.250, 0.750, 0.750],
  "ease": [0.250, 0.100, 0.250, 1.000],
  "easeIn": [0.420, 0.000, 1.000, 1.000],
  "easeOut": [0.000, 0.000, 0.580, 1.000],
  "easeInOut": [0.420, 0.000, 0.580, 1.000],
  "easeInQuad": [0.550, 0.085, 0.680, 0.530],
  "easeInCubic": [0.550, 0.055, 0.675, 0.190],
  "easeInQuart": [0.895, 0.030, 0.685, 0.220],
  "easeInQuint": [0.755, 0.050, 0.855, 0.060],
  "easeInSine": [0.470, 0.000, 0.745, 0.715],
  "easeInExpo": [0.950, 0.050, 0.795, 0.035],
  "easeInCirc": [0.600, 0.040, 0.980, 0.335],
  "easeInBack": [0.600, -0.280, 0.735, 0.045],
  "easeOutQuad": [0.250, 0.460, 0.450, 0.940],
  "easeOutCubic": [0.215, 0.610, 0.355, 1.000],
  "easeOutQuart": [0.165, 0.840, 0.440, 1.000],
  "easeOutQuint": [0.230, 1.000, 0.320, 1.000],
  "easeOutSine": [0.390, 0.575, 0.565, 1.000],
  "easeOutExpo": [0.190, 1.000, 0.220, 1.000],
  "easeOutCirc": [0.075, 0.820, 0.165, 1.000],
  "easeOutBack": [0.175, 0.885, 0.320, 1.275],
  "easeInOutQuad": [0.455, 0.030, 0.515, 0.955],
  "easeInOutCubic": [0.645, 0.045, 0.355, 1.000],
  "easeInOutQuart": [0.770, 0.000, 0.175, 1.000],
  "easeInOutQuint": [0.860, 0.000, 0.070, 1.000],
  "easeInOutSine": [0.445, 0.050, 0.550, 0.950],
  "easeInOutExpo": [1.000, 0.000, 0.000, 1.000],
  "easeInOutCirc": [0.785, 0.135, 0.150, 0.860],
  "easeInOutBack": [0.680, -0.550, 0.265, 1.550],
  "custom": [0.000, 0.350, 0.500, 1.300],
  "random": [Math.random().toFixed(3),
      Math.random().toFixed(3),
      Math.random().toFixed(3),
      Math.random().toFixed(3)]
    }

This curve contains values out of range. But fear not young padawan! Just use cubic-bezier(.25,.1,.39,1) as well for Webkit until the bug #45761 fix propagates to Safari.

 接近ios 原生 的切换 cubic-bezier(0.42, 0, 0.58, 1.0)

http://www.cnblogs.com/surfaces/

 

 

 

 // transform兼容
    function preTransform() {
        var cssPrefix,
        vendors = {
          '': '',
          Webkit: 'webkit',
          Moz: '',
          O: 'o',
          ms: 'ms'
        },
        testEle = document.createElement('p'),
        cssSupport = {};
 
         // 嗅探特性
        Object.keys(vendors).some(function(vendor) {
            if (testEle.style[vendor + (vendor ? 'T' : 't') + 'ransform'] !== undefined) {
              cssPrefix = vendor ? '-' + vendor.toLowerCase() + '-' : '';
              return true;
            }
        });
 
      function normalizeCss(name) {
        name = name.toLowerCase();
        return cssPrefix ? cssPrefix + name : name;
      }
 
      cssSupport = {
        transform: normalizeCss('Transform'),
      }
 
      return cssSupport.transform;
    }
}());

 

 

 使用tween 单独的 , easeIn1 自定义的,

var easeIn1= Tween.easeIn=function(t, b, c, d){  //加速曲线
        return c*(t/=d)*t + b;
    }

 

https://stackoverflow.com/questions/8917921/cross-browser-javascript-not-jquery-scroll-to-top-animation/16136789#16136789

document.getElementById("asdad").onclick = function() {
					function easeInOutQuad(t, b, c, d) {
						t /= d / 2;
						if (t < 1) return c / 2 * t * t + b;
						t--;
						return -c / 2 * (t * (t - 2) - 1) + b;
					};

					function easeIn1(currentTime, start, change, duration) {
						currentTime /= duration / 2;
						if (currentTime < 1) {
							return change / 2 * currentTime * currentTime + start;
						}
						currentTime -= 1;
						return -change / 2 * (currentTime * (currentTime - 2) - 1) + start;
					}
					console.log("body goTop", document.scrollingElement.scrollTop)
					//  scrollTo(document.scrollingElement, 0, 1250,easeIn1);  
					scrollTo(document.scrollingElement, 100, 1250, easeIn1);
				}



				function scrollTo(element, to, duration, easefn) {
					var start = element.scrollTop,
						change = to - start,
						increment = 20;
					var easefn = easefn
					//console.log(start,document.body.scrollTop,easefn===easeInOut,"easefn",typeof easefn)
					var animateScroll = function(elapsedTime) {
						elapsedTime += increment;
						var position = easefn(elapsedTime, start, change, duration);
						element.scrollTop = position;
						if (elapsedTime < duration) {
							setTimeout(function() {
								animateScroll(elapsedTime);
							}, increment);
						}
					};

					animateScroll(0);
				}

  

 

 

示例

 

<!DOCTYPE HTML>
<html>
	<head>
		<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
		<meta name="viewport" content="initial-scale=1.0,user-scalable=no,maximum-scale=1,width=device-width" />

		<title>scrollAnimate</title>
	</head>
	<style>
		* {
			margin: 0;
			padding: 0;
		}

		ul,
		li {
			margin: 0;
			padding: 0;
			list-style: none;
		}



		.scroll-box {
			background-color: #EEEEEE;
			border: 1px solid red;
			height: 500px;
			overflow-y: hidden;
			touch-action: pan-y;
			;
		}

		.scroll-box p {
			min-height: 50px;
			;
		}

		.scroll-box p:nth-child(odd) {
			background-color: #999;
		}
	</style>
	<body>


		<p style="margin: 20px auto;  text-align: center;">------ -------start- <button
				id="j_dom">点击观察</button>-----------------</p>

		<div class="scroll-box " id="j_target" style="width: 80%; height: 400px;; margin: 0 auto;">

			<p style="height: 100px;background-color: red;;">111111111111111</p>
			<p>22222</p>
			<p style="height: 600px;background-color: #ddd;;">33</p>
			<p>44</p>
			<p style="height: 100px; background-color: red;">55</p>


		</div>

		<p style="margin: 20px auto;  text-align: center;">------ -------end------------------</p>
		<script>
			window.onload = function() {
				function getArrlist(len, height) {
					var strHtml = ""
					var len = 500;
					var height = 100;
					for (var i = 0; i < len; i++) {
						var str = '';
						if (i == 0) {
							str = "开始"
						} else
						if (i == len - 1) {
							str = "最后"
						} else {
							str = i
						}
						strHtml += '<p index="' + i + '">' + str + '</p>'

					}
					return strHtml;
				}

				function moveTo(element, start, to, prop, duration, easing, endFn, stepFn) {
					var prop = prop;
					var start = start || 0;
					var change = to - start;
					var duration = duration;
					var easeInOut = easing;
					var increment = 13;

					console.log("moveTo", start, "easefn", easeInOut)
					var animateScroll = function(elapsedTime) {
						elapsedTime += increment;
						var value = easeInOut(elapsedTime, start, change, duration);
						element.style[prop] = value + 'px';
						stepFn && stepFn.call(this, value);
						if (elapsedTime < duration) {
							setTimeout(function() {
								animateScroll(elapsedTime);
							}, increment);
						} else {
							element.style[prop] = to + 'px';
							endFn && endFn.call(this, value);
						}
					};

					animateScroll(0);
				}

				function scrollAnimate(element, start, to, prop, duration, easing, endFn, stepFn) {

					var prop = prop;
					var start = start || 0;
					var change = parseInt(to - start)
					var duration = duration;
					var easeInOut = easing;
					var startTime = (+new Date);
					//var currentY= parseInt(element.scrollTop);


					console.log("scrollAnimate", start, "easefn", easeInOut)
					step();

					//结束时间的计算
					function step() {
						var changeTime = +new Date();
						var scale = 1 - Math.max(0, startTime - changeTime + duration) / duration;


						var value = easeInOut(scale * duration, start, change, duration);
						// var easingFunc = BezierEasing(.42, 0, 1, 1);
						// var percent = easingFunc(scale);
						// var value = start + to * percent;
						//console.log("scrollAnimate", scale, "percent", percent)

						// var value = Tween[easing](scale*duration,targetY,change,duration);
						//var value = easeInOut(scale * duration, start, change, duration);

						//console.log("scale",scale,"已过时间",(changeTime-startTime),"总时间",duration,"已过时间百分比",(changeTime-startTime)/duration,'change距离',change)
						// console.log(value,"scale",scale,"已过时间百分比",(changeTime-startTime)/duration,'change距离',change)
						//得到新的坐标值后我们继续通过left/top或者transform来完成计算就可以了
						element.style[prop] = value + 'px';
						stepFn && stepFn.call(this, value);
						if (scale == 1) {
							// console.log("运动结束");
							cancelAnimationFrame(element.timer);
							endFn && endFn.call(this, value);
							return;
						} else {
							element.timer = requestAnimationFrame(step);
						}

					}
					//https://github.com/alvarotrigo/fullPage.js/blob/master/src/fullpage.js
				}


				function easeInOutQuad(t, b, c, d) {
					t /= d / 2;
					if (t < 1) return c / 2 * t * t + b;
					t--;
					return -c / 2 * (t * (t - 2) - 1) + b;
				};

				var j_target = document.getElementById("j_target");
				var j_dom = document.getElementById("j_dom")
				j_target.innerHTML = getArrlist();
				j_target.scrollTop = 100;
				j_dom.addEventListener("click", function() {
					console.log("click ", j_target);
					//scrollAnimate(element, start, to, prop, duration, easing, endFn, stepFn)

					scrollAnimate(j_target, 0, 1200, "scrollTop", 1400, easeInOutQuad, function() {
						//console.log("外面运动中");

					}, function(val) {
						j_target.scrollTop = val;
						console.log("外面运动中");
					})

					return false;
				})
			}
		</script>
		<script>

		</script>

	</body>

  

 

 

 

 

  

 腾讯的一个例子 http://alloyteam.github.io/AlloyFinger/example/picture/

var To=function (el, property, value, time, ease, onEnd,onChange ) {
    var current = el[property];
    var dv = value - current;
    var beginTime = new Date();
    var self = this;
    var currentEase=ease||function(a){return a };
    this.tickID=null;
    var toTick = function () {
        var dt = new Date() - beginTime;
        if (dt >= time) {
            el[property] = value;
            onChange && onChange(value);
            onEnd && onEnd(value);
            cancelAnimationFrame(self.tickID);
            self.toTick=null;
            return;
        }
        el[property] = dv * currentEase(dt / time) + current;
        self.tickID=requestAnimationFrame(toTick);
        //self.tickID = requestAnimationFrame(toTick);
        //cancelAnimationFrame������ tickID = requestAnimationFrame(toTick);���
        onChange && onChange(el[property]);
    };
    toTick();
    To.List.push(this);
};

To.List=[];

To.stopAll=function(){
    for(var i= 0,len=To.List.length;i<len;i++){
        cancelAnimationFrame(To.List[i].tickID);
    }
    To.List.length=0;
};

To.stop=function(to) {
    cancelAnimationFrame(to.tickID);
};

  

调用方式     new To(el, "translateX", x, 500, ease);

<!DOCTYPE html>
<html>
<head>
    <title>AlloyFinger</title>
    <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no"/>
    <style>
        .ribbon {
            top: 3.2em;
            right: -4.7em;
            -webkit-transform: rotate(45deg);
            -moz-transform: rotate(45deg);
            -ms-transform: rotate(45deg);
            -o-transform: rotate(45deg);
            transform: rotate(45deg);
            color:#fff;
            display: block;
            padding: .6em 3.5em;
            position: fixed;
            text-align: center;
            text-decoration: none;
            -webkit-user-select: none;
            -moz-user-select: none;
            -ms-user-select: none;
            user-select: none;
            background-color: green;
            z-index: 10000;
        }
    </style>
</head>
<body>
    <a href="https://github.com/AlloyTeam/AlloyFinger" class="ribbon">Fork me on Github</a>
    <script src="../../asset/transform.js"></script>
    <script src="../../alloy_finger.js"></script>
    <script src="../../asset/image_loaded.js"></script>
    <script src="../../asset/to.js"></script>
    <div id="imgBox" style="position:fixed;width: 100%;height: 100%;left:0;top:0;
     background:black;display: none;">
        <img src="../../asset/cover.jpg" id="testImg" alt="" style="width: 100%;position: absolute; " />
    </div>

    <script>
        var topPx;
        imageLoaded("#testImg",function(w,h){
            document.querySelector("#imgBox").style.display="block";
            topPx=window.innerHeight/2-(h*window.innerWidth/w)/2;
            this.style.top=topPx+"px";
        });


        function ease(x) {
            return Math.sqrt(1 - Math.pow(x - 1, 2));
        }

        var el = document.getElementById("testImg");
        Transform(el);
        var initScale = 1;
        new AlloyFinger(el, {
            multipointStart: function () {
                To.stopAll();
                initScale = el.scaleX;
            },
            rotate: function (evt) {
                el.rotateZ += evt.angle;
            },
            pinch: function (evt) {
                el.scaleX = el.scaleY = initScale * evt.zoom;
            },
            multipointEnd: function () {
                To.stopAll();
                if (el.scaleX < 1) {

                    new To(el, "scaleX", 1, 500, ease);
                    new To(el, "scaleY", 1, 500, ease);
                }
                if (el.scaleX > 2) {

                    new To(el, "scaleX", 2, 500, ease);
                    new To(el, "scaleY", 2, 500, ease);
                }
                var rotation = el.rotateZ % 360;

                if (rotation < 0)rotation = 360 + rotation;
                el.rotateZ=rotation;

                if (rotation > 0 && rotation < 45) {
                    new To(el, "rotateZ", 0, 500, ease);
                } else if (rotation >= 315) {
                    new To(el, "rotateZ", 360, 500, ease);
                } else if (rotation >= 45 && rotation < 135) {
                    new To(el, "rotateZ", 90, 500, ease);
                } else if (rotation >= 135 && rotation < 225) {
                    new To(el, "rotateZ", 180, 500, ease);
                } else if (rotation >= 225 && rotation < 315) {
                    new To(el, "rotateZ", 270, 500, ease);
                }
            },
            pressMove: function (evt) {
                el.translateX += evt.deltaX;
                el.translateY += evt.deltaY;
                evt.preventDefault();
            },
            tap: function (evt) {
                //console.log(el.scaleX + "_" + el.scaleY + "_" + el.rotateZ + "_" + el.translateX + "_" + el.translateY);
                //console.log("tap");
            },
            doubleTap: function (evt) {
                To.stopAll();
                if (el.scaleX > 1.5) {

                    new To(el, "scaleX", 1, 500, ease);
                    new To(el, "scaleY", 1, 500, ease);
                    new To(el, "translateX", 0, 500, ease);
                    new To(el, "translateY", 0, 500, ease);
                } else {
                    var box = el.getBoundingClientRect();
                    var y = box.height - (( evt.changedTouches[0].pageY - topPx) * 2) - (box.height / 2 - ( evt.changedTouches[0].pageY - topPx));

                    var x = box.width - (( evt.changedTouches[0].pageX) * 2) - (box.width / 2 - ( evt.changedTouches[0].pageX));
                    new To(el, "scaleX", 2, 500, ease);
                    new To(el, "scaleY", 2, 500, ease);
                    new To(el, "translateX", x, 500, ease);
                    new To(el, "translateY", y, 500, ease);
                }
                //console.log("doubleTap");
            },
            longTap: function (evt) {
                //console.log("longTap");
            },
            swipe: function (evt) {
                //console.log("swipe" + evt.direction);
            }

        });

    </script>
</body>
</html>

  

 

 参考资料:

snabbt.js

 http://daniel-lundin.github.io/snabbt.js/index.html

jquery.transit.js

Zepto.js

Bootstrap.transition.js jquery.js

 


posted @ 2016-01-14 13:14  surfaces  阅读(4817)  评论(0编辑  收藏  举报