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

 

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

移动端 transition动画函数的封装(仿Zepto)以及 requestAnimationFrame动画函数封装(仿jQuery)_第1张图片

上面图片对应的 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

  

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

移动端 transition动画函数的封装(仿Zepto)以及 requestAnimationFrame动画函数封装(仿jQuery)_第2张图片

上面对用的 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 动画

$("#some_element").animate(
    {
        opacity:0.25,
        left:'50px',
        color:'#abcdef',
        rotateZ:'45deg',
        translate3d:'0,10px,0'
    },
    500,
    'ease-out',function(){  alert('回调'); }
)

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

transform(dom元素,{ css属性:css值},css持续时间(单位:毫秒),css过度形式,transitionend回调函数,css延迟时间(单位:毫秒));

transform(element,{ translate3d:'220px,10px,0',left:'1em',opacity:0.2,perspective:'400px', rotateY:'30deg'},duration,'linear',fn,delay);

  关于兼容性:几乎与zepto一致 ,但是不支持 动画帧 keyframe,(如果需要,自己手动扩冲一下就好)个人觉得  keyframe移动端 兼容性不是很好,尤其手机自带的浏览器(原生app 调用 内嵌H5页面,小问题还是蛮多的);

 

  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;

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

     

源码如下

 
//transform(obj,{translateX:'150px',left:'1em',opacity:0.2,perspective:'400px', rotateY:'40deg'},duration,'linear',fn,delay);

               
				
;(function(window,document,undefined){

var prefix = function() {
  var div = document.createElement('div');//建立临时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';
  }
  if (style.MozTransition) {
    dom='moz';
  }
   if (style.khtmlTransition) {
    dom='Khtml';
  }
  
  if (style.oTransition) {
    dom='o';
  }
  if (style.msTransition) {
    dom='ms';
  }
 
  div=null; ////去掉不必要的数据存储,便于垃圾回收 

  if(dom){////style.transition 情况
	  return {
		dom: dom,
		lowercase: dom,
		css: '-' + dom + '-',
		js: dom[0].toUpperCase() + dom.substr(1)
	  }; 
  }else{
	  return false;  
 }
}();

//alert(prefix.js);

var transitionEnd=function () { //参考  bootstrap.transition.js  
  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;    //不支持的 transition的返回false     这个函数 利用 return  ,写的非常巧;
  
}();

//alert('支持'+transitionEnd);
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') //在大小写字符之间插入_,大写在前,比如AAAbb,得到AA_Abb
    .replace(/([a-z\d])([A-Z])/g, '$1_$2') //在大小写字符之间插入_,小写或数字在前,比如bbbAaa,得到bbb_Aaa
    .replace(/_/g, '-') //将_替换成-
    .toLowerCase() //转成小写
  }





var transform=function (obj,properties, duration, ease, callback, delay){
	
	
	 if (!obj) return;
	//参数修正
	
	  if (typeof duration == 'function')
      callback = duration, ease = undefined, duration = 400,delay=delay
      if (typeof ease == 'function')  //传参为function(properties,duration,callback)
        callback = ease, ease = undefined,delay=delay
     
      if (duration) duration = typeof duration == 'number' ? duration :400;
	
	  delay = (typeof delay == 'number') ? delay :0;
	  
     
	//参数修正
	var nowTransition=prefix.js+'Transition';
	var nowTransform=prefix.js+'Transform';
	var prefixcss=prefix.css;
	if(!prefix.js){
		nowTransition='transition';
	    nowTransform='transform';
		prefixcss='';  //-webkit-transition >> transition  
	}
	
	var transitionProperty, transitionDuration, transitionTiming, transitionDelay;//过渡
	var key, cssValues = {}, cssProperties, transforms = "";    // transforms 变形   cssValues设置给DOM的样式
	var transform;     //变形
    var cssReset = {};
	var css='';
	var cssProperties = [];
	
	
	  transform = prefixcss + 'transform';       //变形    cssValues[transform]
	  cssReset[transitionProperty = prefixcss + 'transition-property'] =
	  cssReset[transitionDuration = prefixcss + 'transition-duration'] =
	  cssReset[transitionDelay    = prefixcss + 'transition-delay'] =
	  cssReset[transitionTiming   = prefixcss + 'transition-timing-function']='';
	
	
      // CSS transitions
	
     for (key in properties){
       //如果设置 的CSS属性是变形之类的
       if (supportedTransforms.test(key)) transforms += key + '(' + properties[key] + ') '
       else cssValues[key] = properties[key], cssProperties.push(dasherize(key))
	 } //for end 
       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;   //加分号 兼容ie	
			
		    	  
		if(!callback){return } //没有回调函数 return

	    var fired = false;
		var handler = function () {

				 callback && callback.apply(obj,arguments);
				 fired=true;
				  
				if(obj.removeEventListener)
				 obj.removeEventListener(transitionEnd,arguments.callee,false)				
        };
          
		  if(obj.addEventListener){
	       obj.addEventListener(transitionEnd, handler,false);
		  }
		

		  if(!transitionEnd||duration<=0){ //没有  @1 transitionEnd 事件    或者@2 duration为0,即浏览器不支持动画的情况  直接执行动画结束,执行回调。     
			 setTimeout(function(){
			 handler();
			 });
			return;
		  }
		   
		
		
		 setTimeout(function(){//绑定过事件还做延时处理,是transitionEnd在older Android phones不一定触发
			    if(fired) return
				  handler()	
		},(duration + delay) + 25);
		
							  
}//end

   window.transform=transform;

})(window,document);

  

 唯一的缺点  scrollTop 缓动不支持; 这里有个简易的  函数 类似jquery  语法几乎一样

先看效果

移动端 transition动画函数的封装(仿Zepto)以及 requestAnimationFrame动画函数封装(仿jQuery)_第3张图片

 

相关布局

<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);
                 
                  startMove(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
  }
}

 

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

语法如下

 

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

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

 为什么要用   requestAnimationFrame  。

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

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

  

如下   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 放弃,上面说的比较完善;

 

 

 

startMove源码如下

;(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; } } var now=function (){ return +new Date(); } function startMove(obj,json,times,fx,fn){
//参数修正 start 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'; } } //参数修正 end var iCur = {}; var startTime = now(); 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|| window.pageYOffset|| document.documentElement.scrollTop; } else{ iCur[attr] = parseInt(getStyle(obj,attr)) || 0; } } if(obj.timer){ cancelAnimationFrame(obj.timer) } //obj.timer=null; function update(){ var changeTime = now(); 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 ); //Tween 函数 Tween(当前时间的变化,当前设定的目标值,当前目标值的变化,当前设定的时间值) 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(obj,arguments); //回调函数 }else{ //setup_fps_meters(); //fps 流畅性能监测 obj.timer=requestAnimationFrame(arguments.callee); //递归调用 } }//update end // requestAnimationFrame(update); //某些时候 画的过快 有bug update(); }

  

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

 另外比较强大的 动画库 推荐

 snabbt.js

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

 参考:

jquery.transit.js

Zepto.js

Bootstrap.transition.js 
jquery.js

 

 

你可能感兴趣的:(移动端 transition动画函数的封装(仿Zepto)以及 requestAnimationFrame动画函数封装(仿jQuery))