简要:zepto 提供了一个基础方法animate来方便我们运用css动画。主要针对transform,animate以及普通属性(例如left,right,height,width等等)的transition过渡。

在js中能方便的,灵活的调用animate方法来操作元素动画。

源码如下:

//     Zepto.js
// (c) 2010-2015 Thomas Fuchs
// Zepto.js may be freely distributed under the MIT license. ;(function($, undefined){
var prefix = '', eventPrefix, // prefix浏览器前缀 -webkit等,eventPrefix事件前缀
vendors = { Webkit: 'webkit', Moz: '', O: 'o' }, //前缀数据源 不包含IE
testEl = document.createElement('div'), //临时DIV容器
supportedTransforms = /^((translate|rotate|scale)(X|Y|Z|3d)?|matrix(3d)?|perspective|skew(X|Y)?)$/i, //变形检测
transform, //变形
transitionProperty, transitionDuration, transitionTiming, transitionDelay,//过渡
animationName, animationDuration, animationTiming, animationDelay, //动画
cssReset = {} //将驼峰字符串转成css属性,如aB-->a-b
function dasherize(str) { return str.replace(/([a-z])([A-Z])/, '$1-$2').toLowerCase() } //修正事件名
function normalizeEvent(name) { return eventPrefix ? eventPrefix + name : name.toLowerCase() } /**
* 根据浏览器内核,设置CSS前缀,事件前缀
* 如-webkit, css:-webkit- event:webkit
* 这里会在vendors存储webkit,moz,o三种前缀
*/
$.each(vendors, function(vendor, event){
if (testEl.style[vendor + 'TransitionProperty'] !== undefined) {
prefix = '-' + vendor.toLowerCase() + '-'
eventPrefix = event
return false
}
}) transform = prefix + 'transform' //变形 //过渡,对于css属性重新设置前缀
cssReset[transitionProperty = prefix + 'transition-property'] =
cssReset[transitionDuration = prefix + 'transition-duration'] =
cssReset[transitionDelay = prefix + 'transition-delay'] =
cssReset[transitionTiming = prefix + 'transition-timing-function'] =
cssReset[animationName = prefix + 'animation-name'] =
cssReset[animationDuration = prefix + 'animation-duration'] =
cssReset[animationDelay = prefix + 'animation-delay'] =
cssReset[animationTiming = prefix + 'animation-timing-function'] = '' /**
* 动画常量数据源,默认设置
* @type {{off: boolean, speeds: {_default: number, fast: number, slow: number}, cssPrefix: string, transitionEnd: *, animationEnd: *}}
*/
$.fx = {
off: (eventPrefix === undefined && testEl.style.transitionProperty === undefined), //能力检测是否支持动画,具体检测是否支持过渡,支持过渡事件
speeds: { _default: 400, fast: 200, slow: 600 },
cssPrefix: prefix, //css 前缀 如-webkit-
transitionEnd: normalizeEvent('TransitionEnd'), //过渡结束事件
animationEnd: normalizeEvent('AnimationEnd') //动画播放结束事件
} /**
* 创建自定义动画
* @param properties 样式集
* @param duration 持续事件
* @param ease 速率
* @param callback 完成时的回调
* @param delay 动画延迟
* @returns {*}
*/
// 这里是对参数的修正和处理,真正操作的是anim方法
$.fn.animate = function(properties, duration, ease, callback, delay){
//参数修正,传参为function(properties,callback)
if ($.isFunction(duration))
callback = duration, ease = undefined, duration = undefined
if ($.isFunction(ease)) //传参为function(properties,duration,callback)
callback = ease, ease = undefined
if ($.isPlainObject(duration)) //传参为function(properties,{})
ease = duration.easing, callback = duration.complete, delay = duration.delay, duration = duration.duration
// duration 数字:持续时间 字符串:取speeds: { _default: 400, fast: 200, slow: 600 }对应数字
if (duration) duration = (typeof duration == 'number' ? duration :
($.fx.speeds[duration] || $.fx.speeds._default)) / 1000 //动画持续时间默认值
if (delay) delay = parseFloat(delay) / 1000 //延迟时间,除以1000转换成s
return this.anim(properties, duration, ease, callback, delay)
} /**
* 动画核心方法
* @param properties 样式集
* @param duration 持续事件
* @param ease 速率
* @param callback 完成时的回调
* @param delay 动画延迟
* @returns {*}
*/
$.fn.anim = function(properties, duration, ease, callback, delay){
var key, cssValues = {}, cssProperties, transforms = '', // transforms 变形 cssValues设置给DOM的样式
that = this, wrappedCallback, endEvent = $.fx.transitionEnd,
fired = false //修正持续时间
if (duration === undefined) duration = $.fx.speeds._default / 1000
if (delay === undefined) delay = 0 //如果浏览器不支持动画,持续时间设为0,直接跳动画结束
if ($.fx.off) duration = 0 // properties是动画名
if (typeof properties == 'string') {
// keyframe [animationName] = properties
cssValues[animationName] = properties
cssValues[animationDuration] = duration + 's'
cssValues[animationDelay] = delay + 's'
cssValues[animationTiming] = (ease || 'linear')
endEvent = $.fx.animationEnd //动画结束事件
} else { //properties 是样式集
cssProperties = []
// CSS transitionsanimation
cssValues
for (key in properties)
// supportedTransforms.test(key) 正则检测是否为变形
// key + '(' + properties[key] + ') '拼凑成变形方法
if (supportedTransforms.test(key)) transforms += key + '(' + properties[key] + ') '
else cssValues[key] = properties[key], cssProperties.push(dasherize(key))
console.log(transforms) // 变形统一存入 cssValues cssProperties
if (transforms) cssValues[transform] = transforms, cssProperties.push(transform) // duration > 0可以播放动画,且properties是对象,表明为过渡,上面有字符串,则为animate
if (duration > 0 && typeof properties === 'object') {
cssValues[transitionProperty] = cssProperties.join(', ')
cssValues[transitionDuration] = duration + 's'
cssValues[transitionDelay] = delay + 's'
cssValues[transitionTiming] = (ease || 'linear') //默认线性速率
}
} //动画完成后的响应函数
wrappedCallback = function(event){
if (typeof event !== 'undefined') {
if (event.target !== event.currentTarget) return // makes sure the event didn't bubble from "below"
$(event.target).unbind(endEvent, wrappedCallback)
} else
$(this).unbind(endEvent, wrappedCallback) // triggered by setTimeout fired = true
// TODO 既然已经执行完了,为什么这里要重复css一下,不太理解
$(this).css(cssReset)
callback && callback.call(this)
} //处理动画结束事件
if (duration > 0){
//绑定动画结束事件
this.bind(endEvent, wrappedCallback)
// transitionEnd is not always firing on older Android phones
// so make sure it gets fired //延时ms后执行动画,注意这里加了25ms,保持endEvent,动画先执行完。
//绑定过事件还做延时处理,是transitionEnd在older Android phones不一定触发
setTimeout(function(){
//如果触发过,就不处理
if (fired) return
wrappedCallback.call(that)
}, ((duration + delay) * 1000) + 25)
} // trigger page reflow so new elements can animate
//主动触发页面回流,刷新DOM,让接下来设置的动画可以正确播放
//更改 offsetTop、offsetLeft、 offsetWidth、offsetHeight;scrollTop、scrollLeft、scrollWidth、scrollHeight;clientTop、clientLeft、clientWidth、clientHeight;getComputedStyle() 、currentStyle()。这些都会触发回流。回流导致DOM重新渲染,平时要尽可能避免,但这里,为了动画即时生效播放,则主动触发回流,刷新DOM。
// 与.length属性一致
this.size() && this.get(0).clientLeft //设置样式,启动动画
this.css(cssValues) // duration为0,即浏览器不支持动画的情况,直接执行动画结束,执行回调。
if (duration <= 0) setTimeout(function() {
that.each(function(){ wrappedCallback.call(this) })
}, 0) return this;
} testEl = null //去掉不必要的数据存储,便于垃圾回收
})(Zepto)

整个fx.js的大致结构如下:

1:根据浏览器属性获取前缀,并设置cssReset的属性名称前加入前缀,

2:设置$.fx全局默认值,(animate方法里面没有传的参数会取此处的默认值)

3:$.fn.animate 的主要功能其实是判断并修正参数,最后调用的$.fn.anim才是操作动画的核心方法。

4:$.fn.anim :

4.1 首先若properties == 'string'  ,则为操作animate动画的情况,cssValues存入动画名称,动画过渡时间,动画延迟时间以及动画函数

4.2 若properties为对象,则将里面的属性加入到cssValues里面,其中对于transform的值先做加工处理。

5:触发动画结束的事件,这里有个技巧:先绑定endEvent,然后setTimeout,这是为了处理绑定endEvent不被兼容的情况。

demo如下:

<style>
@keyframes mymove
{
from {top:0px;}
to {top:200px;}
} @-moz-keyframes mymove /* Firefox */
{
from {top:0px;}
to {top:200px;}
} @-webkit-keyframes mymove /* Safari 和 Chrome */
{
from {top:0px;}
to {top:200px;}
} @-o-keyframes mymove /* Opera */
{
from {top:0px;}
to {top:200px;}
}
</style> <script> $("#high").anim({
transform:'translate(20px)',
/!*scale:3,*!/
/!*translate:'20px,20px',*!/
/!* rotateX:'90deg',
rotateY:'40deg',*!/
height:'100px'
},1,'linear',function () {
alert("调用完成");
},1); $("#high").anim('mymove',1,'linear',function () {
alert('调用完成');
},1);
</script>

这里有几个非常高明的技巧需要提一下:

1:因为要运用js来给元素设置css样式,但css样式需要写多个前缀来兼容,所以这里利用   testEl.style[vendor + 'TransitionProperty'] !== undefined 来获取当前浏览器所支持的前缀然后运用到css属性设置上去。

 /**
* 根据浏览器内核,设置CSS前缀,事件前缀
* 如-webkit, css:-webkit- event:webkit
* 这里会在vendors存储webkit,moz,o三种前缀
*/
$.each(vendors, function(vendor, event){
if (testEl.style[vendor + 'TransitionProperty'] !== undefined) {
prefix = '-' + vendor.toLowerCase() + '-'
eventPrefix = event
return false
}
}) transform = prefix + 'transform' //变形 //过渡,对于css属性重新设置前缀
cssReset[transitionProperty = prefix + 'transition-property'] =
cssReset[transitionDuration = prefix + 'transition-duration'] =
cssReset[transitionDelay = prefix + 'transition-delay'] =
cssReset[transitionTiming = prefix + 'transition-timing-function'] =
cssReset[animationName = prefix + 'animation-name'] =
cssReset[animationDuration = prefix + 'animation-duration'] =
cssReset[animationDelay = prefix + 'animation-delay'] =
cssReset[animationTiming = prefix + 'animation-timing-function'] = ''

2:还是zepto对于参数处理的老套路,在$.fn.animate中针对与形参的不同的输入形式,对参数做对应的处理,使得这个方法能够灵活的执行。

// 这里是对参数的修正和处理,真正操作的是anim方法
$.fn.animate = function(properties, duration, ease, callback, delay){
//参数修正,传参为function(properties,callback)
if ($.isFunction(duration))
callback = duration, ease = undefined, duration = undefined
if ($.isFunction(ease)) //传参为function(properties,duration,callback)
callback = ease, ease = undefined
if ($.isPlainObject(duration)) //传参为function(properties,{})
ease = duration.easing, callback = duration.complete, delay = duration.delay, duration = duration.duration
// duration 数字:持续时间 字符串:取speeds: { _default: 400, fast: 200, slow: 600 }对应数字
if (duration) duration = (typeof duration == 'number' ? duration :
($.fx.speeds[duration] || $.fx.speeds._default)) / 1000 //动画持续时间默认值
if (delay) delay = parseFloat(delay) / 1000 //延迟时间,除以1000转换成s
return this.anim(properties, duration, ease, callback, delay)
}

3:动画前主动触发页面回流: this.size() && this.get(0).clientLeft

4:动画结束后触发事件:使用setTimeout不失为一种好办法

//处理动画结束事件
if (duration > 0){
//绑定动画结束事件
this.bind(endEvent, wrappedCallback)
// transitionEnd is not always firing on older Android phones
// so make sure it gets fired //延时ms后执行动画,注意这里加了25ms,保持endEvent,动画先执行完。
//绑定过事件还做延时处理,是transitionEnd在older Android phones不一定触发
setTimeout(function(){
//如果触发过,就不处理
if (fired) return
wrappedCallback.call(that)
}, ((duration + delay) * 1000) + 25)
}

zepto源码研究 - fx.js的更多相关文章

  1. zepto源码研究 - fx_methods.js

    简要:依赖fx.js,主要是针对show,hide,fadeIn,fadeOut的封装. 源码如下: // Zepto.js // (c) 2010-2015 Thomas Fuchs // Zept ...

  2. zepto源码研究 - deferred.js(jquery-deferred.js)

    简要:zepto的deferred.js 并不遵守promise/A+ 规范,而在jquery v3.0.0中的defer在一定程度上实现了promise/A+ ,因此本文主要研究jquery v3. ...

  3. zepto源码研究 - callback.js

    简要:$.Callbacks是一个生成回调管家Callback的工厂,Callback提供一系列方法来管理一个回调列表($.Callbacks的一个私有变量list),包括添加回调函数, 删除回调函数 ...

  4. zepto源码研究 - ajax.js($.ajax具体流程分析)

    简要:$.ajax是zepto发送请求的核心方法,$.get,$.post,$.jsonp都是封装了$.ajax方法.$.ajax将jsonp与异步请求的代码格式统一起来,内部主要是先处理url,数据 ...

  5. zepto源码研究 - ajax.js($.ajaxJSONP 的分析)

    简要:jsonp是一种服务器和客户端信息传递方式,一般是利用script元素赋值src来发起请求.一般凡是带有src属性的元素发起的请求都是可以跨域的. 那么jsonp是如何获取服务器的数据的呢? j ...

  6. zepto源码研究 - ajax.js(请求过程中的各个事件分析)

    简要:ajax请求具有能够触发各类事件的功能,包括:触发全局事件,请求发送前事件,请求开始事件,请求结束事件等等,贯穿整个ajax请求过程,这是非常有用的,我们可以利用这些事件来做一些非常有意思的事情 ...

  7. zepto源码研究 - zepto.js - 1

    简要:网上已经有很多人已经将zepto的源码研究得很细致了,但我还是想写下zepto源码系列,将别人的东西和自己的想法写下来以加深印象也是自娱自乐,文章中可能有许多错误,望有人不吝指出,烦请赐教. 首 ...

  8. 读Zepto源码之Fx模块

    fx 模块为利用 CSS3 的过渡和动画的属性为 Zepto 提供了动画的功能,在 fx 模块中,只做了事件和样式浏览器前缀的补全,没有做太多的兼容.对于不支持 CSS3 过渡和动画的, Zepto ...

  9. 从壹开始前后端分离 [ vue + .netcore 补充教程 ] 二八║ Nuxt 基础:面向源码研究Nuxt.js

    前言 哈喽大家周五好,又是一个开开心心的周五了,接下来就是三天小团圆啦,这里先祝大家节日快乐咯,希望都没有加班哈哈,今天公司发了月饼,嗯~时间来不及了,上周应该搞个活动抽中几个粉丝发月饼的,下次吧,这 ...

随机推荐

  1. linux vi模式下基本命令和快捷键

    移动光标上:k nk:向上移动n行 9999k或gg可以移到第一行 G移到最后一行下:j nj:向下移动n行左:h nh:向左移动n列右:l nl:向右移动n列 w:光标以单词向前移动 nw:光标向前 ...

  2. MySQL6:视图

    什么是视图 数据库中的视图是一个虚拟表.视图是从一个或者多个表中导出的表,视图的行为与表非常相似,在视图中用户可以使用SELECT语句查询数据,以及使用INSERT.UPDATE和DELETE修改记录 ...

  3. 1.1 JAVA装箱和拆箱以及Java Number & Math&Character 类

    JAVA装箱和拆箱 从Java SE5开始就提供了自动装箱的特性,如果要生成一个数值为10的Integer对象,只需要这样就可以了.原文链接: http://www.cnblogs.com/dolph ...

  4. 序列化模块1 json

    ......得到一个 字符串 的结果 过程就叫序列化 字典 / 列表 / 数字 /对象 -序列化->字符串 为什么要序列化 # 1.要把内容写入文件 序列化 # 2.网络传输数据 序列化 字符串 ...

  5. LeetCode CombinationSum II

    class Solution { public: vector<vector<int> > combinationSum2(vector<int> &num ...

  6. 谷歌浏览器network请求时间(stalled,DNS Lookup,Waiting)分析以及解决方案

    network工具功能强大,能够让我看到网页加载的信息,比如加载时间,和先后顺序,是否是并行加载,还是堵塞加载. 默认情况下有八列: (1).Name:表示加载的文件名. (2).Method:表示请 ...

  7. Yii 之components

    当我们创建一个module的时候,对应的path alias就已经创建.比如我们定义了一个module: www 1 2 3 4 5 'modules'=>array(     'www'=&g ...

  8. excel导入 服务器忘了装组件了。。。

    excel导入 本地没问题 一直在找权限问题  最后发现服务器忘了装组件了... 郁闷 记录下 http://www.microsoft.com/zh-cn/download/confirmation ...

  9. elixir mac环境

    1.升级brew brew update 2.安装 erlang brew install erlang 3.安装Elixir: brew install elixir 终端 iex iex> ...

  10. Spring Boot—07应用application.properties中的配置

    方法1 @Value("${test.msg}") private String msg; 方法2 @Autowired private Environment env; Stri ...