一步一步DIY zepto库,研究zepto源码7 -- 动画模块(fx,fx_method)
2016-12-05 11:34
411 查看
代码挂在我的github上,对应文件夹v0.7.1。
https://github.com/zrysmt/DIY-zepto
注:要在github源代码中自己编译的话,要在基础包
核心方法是
其本质就是设置好css3属性对象,然后用
整体的源代码和注释放在下面
整体源码和注释放在这里
全部代码挂在我的github上,本博文对应文件夹v0.7.x。
https://github.com/zrysmt/DIY-zepto
参考阅读:
- Zepto源码分析-动画(fx fx_method)模块
https://github.com/zrysmt/DIY-zepto
注:要在github源代码中自己编译的话,要在基础包
命令:npm run dist上要进行扩展了,输入命令:
MODULES="zepto event fx fx_methods" npm run dist # on Windows > SET MODULES=zepto event fx fx_methods > npm run dist
1.示例Demo
var div1 = $('#foo1'); div1.animate({ "width": "300px", "height": "300px" }, 'slow', 'ease-in-out', function() { console.log('div1 animate callback'); // div2.hide('slow',function(){ div2.fadeOut('slow', function() { console.log('div2 animate callback'); }); }, '2000');
2.fx
zepto的动画采用的是CSS3的动画/过渡,未做兼容。核心方法是
$.fn.animate = function(properties, duration, ease, callback, delay),实际上的处理逻辑是
$.fn.anim = function(properties, duration, ease, callback, delay)
其本质就是设置好css3属性对象,然后用
this.css(cssValues)方法是css3动画起作用。浏览器不支持的动画使用setTimeout
// duration为0,即浏览器不支持动画的情况,直接执行动画结束,执行回调。 if (duration <= 0) setTimeout(function() { that.each(function() { wrappedCallback.call(this); }); }, 0);
整体的源代码和注释放在下面
var Fx = function($) {
var prefix = '',
eventPrefix,
vendors = { Webkit: 'webkit', Moz: '', O: 'o' },
testEl = document.createElement('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前缀,事件前缀
* 如css:-webkit- event:webkit
* 为prefix和eventPrefix赋值
*/
if (testEl.style.transform === undefined) {
$.each(vendors, function(vendor, event) {
if (testEl.style[vendor + 'TransitionProperty'] !== undefined) {
prefix = '-' + vendor.toLowerCase() + '-';
eventPrefix = event;
return false;
}
});
}
transform = prefix + 'transform';
//均为空''
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') //动画播放结束事件
};
/**
* [animate 自定义动画]
* @param {[Object]} properties [属性变化成,如{"width":"300px"}]
* @param {[type]} duration [速度 如slow或者一个数字]
* @param {[type]} ease [变化的速率ease、linear、ease-in / ease-out、ease-in-out
cubic-bezier]
* @param {Function} callback [回调函数]
* @param {[type]} delay [延迟时间]
*/
$.fn.animate = function(properties, duration, ease, callback, delay) {
//参数处理
if ($.isFunction(duration)) //传参为function(properties,callback)
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参数处理
if (duration) duration = (typeof duration == 'number' ? duration :
($.fx.speeds[duration] || $.fx.speeds._default)) / 1000;
if (delay) delay = parseFloat(delay) / 1000;
return this.anim(properties, duration, ease, callback, delay);
};
$.fn.anim = function(properties, duration, ease, callback, delay) {
var key, cssValues = {},
cssProperties, transforms = '',
that = this,
wrappedCallback, endEvent = $.fx.transitionEnd,
fired = false;
//修正好时间
if (duration === undefined) duration = $.fx.speeds._default / 1000;
if (delay === undefined) delay = 0;
if ($.fx.off) duration = 0; //如果浏览器不支持动画,持续时间设为0,直接跳动画结束
if (typeof properties == 'string') {
// keyframe animation
cssValues[animationName] = properties; //properties是动画名
cssValues[animationDuration] = duration + 's';
cssValues[animationDelay] = delay + 's';
cssValues[animationTiming] = (ease || 'linear');
endEvent = $.fx.animationEnd;
} else { //properties 是样式集对象
cssProperties = [];
// CSS transitions
for (key in properties) {
//是这些属性^((translate|rotate|scale)(X|Y|Z|3d)?|matrix(3d)?|perspective|skew(X|Y)?)
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 + '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;
$(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
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;
};
export default Fx;
3.fx_methods
源码中的fx_methods(fx_methods.js中)方法,说白了就是利用上面的
fx.js文件下的
$.fn.animate函数提供便捷的方法
整体源码和注释放在这里
var Fx_methods = function($) { var document = window.document, docElem = document.documentElement, origShow = $.fn.show, origHide = $.fn.hide, origToggle = $.fn.toggle; function anim(el, speed, opacity, scale, callback) { if (typeof speed == 'function' && !callback) callback = speed, speed = undefined; var props = { opacity: opacity }; if (scale) { props.scale = scale; el.css($.fx.cssPrefix + 'transform-origin', '0 0'); //设置变形原点 } return el.animate(props, speed, null, callback); //不支持速率变化 } function hide(el, speed, scale, callback) { //$(dom).animate({opacity: 0, '-webkit-transform-origin': '0px 0px 0px', '-webkit-transform': 'scale(0, 0)' },800) //设置了变形原点,缩放为0,它就会缩到左上角再透明 return anim(el, speed, 0, scale, function() { origHide.call($(this)); callback && callback.call(this); }); } $.fn.show = function(speed, callback) { origShow.call(this); if (speed === undefined) { speed = 0; } else { this.css('opacity', 0); } return anim(this, speed, 1, '1,1', callback); }; $.fn.hide = function(speed, callback) { if (speed === undefined) { return origHide.call(this); } else { return hide(this, speed, '0,0', callback); } }; $.fn.toggle = function(speed, callback) { if (speed === undefined || typeof speed == 'boolean') { return origToggle.call(this, speed); } else { return this.each(function() { var el = $(this); el[el.css('display') == 'none' ? 'show' : 'hide'](speed, callback); }); } }; $.fn.fadeTo = function(speed, opacity, callback) { return anim(this, speed, opacity, null, callback); }; $.fn.fadeIn = function(speed, callback) { var target = this.css('opacity'); if (target > 0){ this.css('opacity', 0); } else { target = 1; } return origShow.call(this).fadeTo(speed, target, callback); }; $.fn.fadeOut = function(speed, callback) { return hide(this, speed, null, callback); }; $.fn.fadeToggle = function(speed, callback) { return this.each(function() { var el = $(this); el[ (el.css('opacity') == 0 || el.css('display') == 'none') ? 'fadeIn' : 'fadeOut' ](speed, callback); }); }; }; export default Fx_methods;
全部代码挂在我的github上,本博文对应文件夹v0.7.x。
https://github.com/zrysmt/DIY-zepto
参考阅读:
- Zepto源码分析-动画(fx fx_method)模块
相关文章推荐
- 一步一步DIY zepto库,研究zepto源码6 -- deferred模块
- 一步一步DIY zepto库,研究zepto源码8 -- touch模块
- 一步一步DIY zepto库,研究zepto源码1--基础模块
- 一步一步DIY zepto库,研究zepto源码4 -- ajax模块
- 一步一步DIY zepto库,研究zepto源码5-- callbacks
- Zepto源码分析-动画(fx fx_method)模块
- 一步一步DIY zepto库,研究zepto源码2 -- selector选择符
- 一步一步DIY zepto库,研究zepto源码3 -- event模块
- zepto源码研究 - fx_methods.js
- 读Zepto源码之Fx模块
- 读Zepto源码之Fx模块
- Zepto源码之fx模块
- 读Zepto源码之fx_methods模块
- 读Zepto源码之assets模块
- Zepto源码分析-ajax模块
- 读Zepto源码之Gesture模块
- 读Zepto源码之Event模块
- zepto源码之fx_methods.js
- 读Zepto源码之fx_methods模块
- 读Zepto源码之Touch模块