您的位置:首页 > 移动开发

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

2016-01-14 13:14 615 查看
移动端 单页有时候 制作只用到简单的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 动画

$("#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 源码如下

//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;
if(typeof callback == 'number') delay=callback,callback=undefined;   //没有回调函数 有delay
delay = (typeof delay == 'number') ? delay :0;   // delay 设置 delay:'none'也可以

//参数修正
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 语法几乎一样

先看效果



相关布局

<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 在移动端发展的前景是相当不错的,目前安卓上感觉还是有点丢帧。抖动明显;

  

如下 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;
}
}

var now=function (){
return +new Date();
}

function animate(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


                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: