/*!
* jQuery Animate v1.8.9 - By CSS3 transition
* (c) 2014-2017 BaiJunjie
* MIT Licensed.
*
* https://github.com/baijunjie/jquery.animate
*/
(function(root, factory) {
'use strict';
if (typeof module === 'object' && typeof exports === 'object') {
module.exports = factory(require('jquery'));
} else if (typeof define === 'function' && define.amd) {
define(['jquery'], factory);
} else {
factory(root.jQuery);
}
}(this, function($) {
'use strict';
// jQuery 3.0.0 以及之后的版本已经兼容 requestAnimationFrame API
if (compareVersion('3.0.0', $.fn.jquery) > 0) {
// Integration jQuery requestAnimationFrame - v0.1.3pre
// - https://github.com/gnarf37/jquery-requestAnimationFrame
(function(jQuery) {
var animating,
lastTime = 0,
vendors = ['webkit', 'moz'],
requestAnimationFrame = window.requestAnimationFrame,
cancelAnimationFrame = window.cancelAnimationFrame;
for(; lastTime < vendors.length && !requestAnimationFrame; lastTime++) {
requestAnimationFrame = window[ vendors[lastTime] + "RequestAnimationFrame" ];
cancelAnimationFrame = cancelAnimationFrame ||
window[ vendors[lastTime] + "CancelAnimationFrame" ] ||
window[ vendors[lastTime] + "CancelRequestAnimationFrame" ];
}
function raf() {
if ( animating ) {
requestAnimationFrame( raf );
jQuery.fx.tick();
}
}
if ( requestAnimationFrame ) {
// use rAF
window.requestAnimationFrame = requestAnimationFrame;
window.cancelAnimationFrame = cancelAnimationFrame;
jQuery.fx.timer = function( timer ) {
if ( timer() && jQuery.timers.push( timer ) && !animating ) {
animating = true;
raf();
}
};
jQuery.fx.stop = function() {
animating = false;
};
}
}($));
}
// Integration jQuery Easing v1.3
// - http://gsgd.co.uk/sandbox/jquery/easing/
$.easing['jswing'] = $.easing['swing'];
$.extend( $.easing, {
def: 'easeOutQuad',
swing: function (x, t, b, c, d) {
return $.easing[$.easing.def](x, t, b, c, d);
},
easeInQuad: function (x, t, b, c, d) {
return c*(t/=d)*t + b;
},
easeOutQuad: function (x, t, b, c, d) {
return -c *(t/=d)*(t-2) + b;
},
easeInOutQuad: function (x, t, b, c, d) {
if ((t/=d/2) < 1) return c/2*t*t + b;
return -c/2 * ((--t)*(t-2) - 1) + b;
},
easeInCubic: function (x, t, b, c, d) {
return c*(t/=d)*t*t + b;
},
easeOutCubic: function (x, t, b, c, d) {
return c*((t=t/d-1)*t*t + 1) + b;
},
easeInOutCubic: function (x, t, b, c, d) {
if ((t/=d/2) < 1) return c/2*t*t*t + b;
return c/2*((t-=2)*t*t + 2) + b;
},
easeInQuart: function (x, t, b, c, d) {
return c*(t/=d)*t*t*t + b;
},
easeOutQuart: function (x, t, b, c, d) {
return -c * ((t=t/d-1)*t*t*t - 1) + b;
},
easeInOutQuart: function (x, 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;
},
easeInQuint: function (x, t, b, c, d) {
return c*(t/=d)*t*t*t*t + b;
},
easeOutQuint: function (x, t, b, c, d) {
return c*((t=t/d-1)*t*t*t*t + 1) + b;
},
easeInOutQuint: function (x, t, b, c, d) {
if ((t/=d/2) < 1) return c/2*t*t*t*t*t + b;
return c/2*((t-=2)*t*t*t*t + 2) + b;
},
easeInSine: function (x, t, b, c, d) {
return -c * Math.cos(t/d * (Math.PI/2)) + c + b;
},
easeOutSine: function (x, t, b, c, d) {
return c * Math.sin(t/d * (Math.PI/2)) + b;
},
easeInOutSine: function (x, t, b, c, d) {
return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b;
},
easeInExpo: function (x, t, b, c, d) {
return (t==0) ? b : c * Math.pow(2, 10 * (t/d - 1)) + b;
},
easeOutExpo: function (x, t, b, c, d) {
return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b;
},
easeInOutExpo: function (x, t, b, c, d) {
if (t==0) return b;
if (t==d) return b+c;
if ((t/=d/2) < 1) return c/2 * Math.pow(2, 10 * (t - 1)) + b;
return c/2 * (-Math.pow(2, -10 * --t) + 2) + b;
},
easeInCirc: function (x, t, b, c, d) {
return -c * (Math.sqrt(1 - (t/=d)*t) - 1) + b;
},
easeOutCirc: function (x, t, b, c, d) {
return c * Math.sqrt(1 - (t=t/d-1)*t) + b;
},
easeInOutCirc: function (x, t, b, c, d) {
if ((t/=d/2) < 1) return -c/2 * (Math.sqrt(1 - t*t) - 1) + b;
return c/2 * (Math.sqrt(1 - (t-=2)*t) + 1) + b;
},
easeInElastic: function (x, t, b, c, d) {
var s=1.70158;var p=0;var a=c;
if (t==0) return b; if ((t/=d)==1) return b+c; if (!p) p=d*.3;
if (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;
},
easeOutElastic: function (x, t, b, c, d) {
var s=1.70158;var p=0;var a=c;
if (t==0) return b; if ((t/=d)==1) return b+c; if (!p) p=d*.3;
if (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;
},
easeInOutElastic: function (x, t, b, c, d) {
var s=1.70158;var p=0;var a=c;
if (t==0) return b; if ((t/=d/2)==2) return b+c; if (!p) p=d*(.3*1.5);
if (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 -.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 )*.5 + c + b;
},
easeInBack: function (x, t, b, c, d, s) {
if (s == undefined) s = 1.70158;
return c*(t/=d)*t*((s+1)*t - s) + b;
},
easeOutBack: function (x, t, b, c, d, s) {
if (s == undefined) s = 1.70158;
return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b;
},
easeInOutBack: function (x, t, b, c, d, s) {
if (s == undefined) s = 1.70158;
if ((t/=d/2) < 1) return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b;
return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b;
},
easeInBounce: function (x, t, b, c, d) {
return c - $.easing.easeOutBounce (x, d-t, 0, c, d) + b;
},
easeOutBounce: function (x, t, b, c, d) {
if ((t/=d) < (1/2.75)) {
return c*(7.5625*t*t) + b;
} else if (t < (2/2.75)) {
return c*(7.5625*(t-=(1.5/2.75))*t + .75) + b;
} else if (t < (2.5/2.75)) {
return c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b;
} else {
return c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b;
}
},
easeInOutBounce: function (x, t, b, c, d) {
if (t < d/2) return $.easing.easeInBounce (x, t*2, 0, c, d) * .5 + b;
return $.easing.easeOutBounce (x, t*2-d, 0, c, d) * .5 + c*.5 + b;
}
});
// 当浏览器不支持 transition 时,如果设置了以下的 Easing,可以使 animate 不至于报错,达到向下兼容的目的
$.extend( $.easing, {
ease: function (x, t, b, c, d) {
return $.easing.easeInOutCubic(x, t, b, c, d);
},
easeIn: function (x, t, b, c, d) {
return $.easing.easeInSine(x, t, b, c, d);
},
easeOut: function (x, t, b, c, d) {
return $.easing.easeOutSine(x, t, b, c, d);
},
easeInOut: function (x, t, b, c, d) {
return $.easing.easeInOutSine(x, t, b, c, d);
}
});
var testElem = document.createElement('div'),
$testElem = $(testElem);
// 返回支持的属性名
function getSupportPropertyName(prop) {
if (prop in testElem.style) return prop;
var testProp = prop.charAt(0).toUpperCase() + prop.substr(1),
prefixs = [ 'Webkit', 'Moz', 'ms', 'O' ];
for (var i = 0, l = prefixs.length; i < l; i++) {
var prefixProp = prefixs[i] + testProp;
if (prefixProp in testElem.style) {
return prefixProp;
}
}
}
// 检查是否支持3D
function checkTransform3dSupport() {
testElem.style[support.transform] = '';
testElem.style[support.transform] = 'rotateY(90deg)';
return testElem.style[support.transform] !== '';
}
// 检查浏览器的 transition 支持
var support = {};
support.transform = getSupportPropertyName('transform');
if (!support.transform) return $;
support.transformOrigin = getSupportPropertyName('transformOrigin');
support.transformStyle = getSupportPropertyName('transformStyle');
support.perspective = getSupportPropertyName('perspective');
support.perspectiveOrigin = getSupportPropertyName('perspectiveOrigin');
support.backfaceVisibility = getSupportPropertyName('backfaceVisibility');
support.filter = getSupportPropertyName('filter');
support.transition = getSupportPropertyName('transition');
support.transform3d = checkTransform3dSupport();
// 将检测到的支持结果写入 $.support
for (var key in support) {
if (typeof $.support[key] === 'undefined') {
$.support[key] = support[key];
}
}
// 缓动列表
$.cssEase = {
'_default' : 'swing',
'swing' : 'easeOutQuad', // 和 jQuery Easing 相同,查看详情 https://github.com/gdsmith/jquery.easing
'linear' : 'cubic-bezier(0,0,1,1)',
'ease' : 'cubic-bezier(.25,.1,.25,1)',
'easeIn' : 'cubic-bezier(.42,0,1,1)',
'easeOut' : 'cubic-bezier(0,0,.58,1)',
'easeInOut' : 'cubic-bezier(.42,0,.58,1)',
'easeInCubic' : 'cubic-bezier(.550,.055,.675,.190)',
'easeOutCubic' : 'cubic-bezier(.215,.61,.355,1)',
'easeInOutCubic' : 'cubic-bezier(.645,.045,.355,1)',
'easeInCirc' : 'cubic-bezier(.6,.04,.98,.335)',
'easeOutCirc' : 'cubic-bezier(.075,.82,.165,1)',
'easeInOutCirc' : 'cubic-bezier(.785,.135,.15,.86)',
'easeInExpo' : 'cubic-bezier(.95,.05,.795,.035)',
'easeOutExpo' : 'cubic-bezier(.19,1,.22,1)',
'easeInOutExpo' : 'cubic-bezier(1,0,0,1)',
'easeInQuad' : 'cubic-bezier(.55,.085,.68,.53)',
'easeOutQuad' : 'cubic-bezier(.25,.46,.45,.94)',
'easeInOutQuad' : 'cubic-bezier(.455,.03,.515,.955)',
'easeInQuart' : 'cubic-bezier(.895,.03,.685,.22)',
'easeOutQuart' : 'cubic-bezier(.165,.84,.44,1)',
'easeInOutQuart' : 'cubic-bezier(.77,0,.175,1)',
'easeInQuint' : 'cubic-bezier(.755,.05,.855,.06)',
'easeOutQuint' : 'cubic-bezier(.23,1,.32,1)',
'easeInOutQuint' : 'cubic-bezier(.86,0,.07,1)',
'easeInSine' : 'cubic-bezier(.47,0,.745,.715)',
'easeOutSine' : 'cubic-bezier(.39,.575,.565,1)',
'easeInOutSine' : 'cubic-bezier(.445,.05,.55,.95)',
'easeInBack' : 'cubic-bezier(.6,-.28,.735,.045)',
'easeOutBack' : 'cubic-bezier(.175, .885,.32,1.275)',
'easeInOutBack' : 'cubic-bezier(.68,-.55,.265,1.55)'
};
// 转换easing为贝塞尔函数
//
// 'swing' => 'cubic-bezier(.25,.46,.45,.94)'
//
function convertEase(easing) {
if (typeof easing !== 'string') return;
if (easing.indexOf('cubic-bezier') !== 0) {
easing = $.cssEase[easing];
return convertEase(easing);
}
return easing;
}
// ## 'transform' CSS hook
//
// $('div').css({ transform: 'rotate(90deg)' });
// $('div').css('transform'); => { rotate: '90deg' }
//
$.cssHooks.transform = {
get: function(elem) {
return $.data(elem, 'bjj-transform') || new Transform();
},
set: function(elem, v) {
var value = v;
if (!(value instanceof Transform)) {
value = new Transform(value);
}
elem.style[support.transform] = value.toString();
$.data(elem, 'bjj-transform', value);
}
};
// jQuery 1.8- 不支持这些属性的前缀转换
if (compareVersion('1.8', $.fn.jquery) > 0) {
// ## 'transformOrigin' CSS hook
//
// $('div').css({ transformOrigin: '0 0' });
//
$.cssHooks.transformOrigin = {
get: function(elem) {
return elem.style[support.transformOrigin];
},
set: function(elem, value) {
elem.style[support.transformOrigin] = value;
}
};
// ## 'transformStyle' CSS hook
//
// $('div').css({ transformStyle: 'preserve-3d' });
//
$.cssHooks.transformStyle = {
get: function(elem) {
return elem.style[support.transformStyle];
},
set: function(elem, value) {
elem.style[support.transformStyle] = value;
}
};
// ## 'perspective' CSS hook
//
// $('div').css({ perspective: '1000px' });
//
$.cssHooks.perspective = {
get: function(elem) {
return elem.style[support.perspective];
},
set: function(elem, value) {
elem.style[support.perspective] = value;
}
};
// ## 'perspectiveOrigin' CSS hook
//
// $('div').css({ perspectiveOrigin: '100px 100px' });
//
$.cssHooks.perspectiveOrigin = {
get: function(elem) {
return elem.style[support.perspectiveOrigin];
},
set: function(elem, value) {
elem.style[support.perspectiveOrigin] = value;
}
};
// ## 'backfaceVisibility' CSS hook
//
// $('div').css({ backfaceVisibility: 'hidden' });
//
$.cssHooks.backfaceVisibility = {
get: function(elem) {
return elem.style[support.backfaceVisibility];
},
set: function(elem, value) {
elem.style[support.backfaceVisibility] = value;
}
};
// ## 'transition' CSS hook
//
// $('div').css({ transition: 'all 0 ease 0' });
//
$.cssHooks.transition = {
get: function(elem) {
return elem.style[support.transition];
},
set: function(elem, value) {
elem.style[support.transition] = value;
}
};
// ## 'filter' CSS hook
//
// $('div').css({ filter: 'blur(10px)' });
//
$.cssHooks.filter = {
get: function(elem) {
return elem.style[support.filter];
},
set: function(elem, value) {
elem.style[support.filter] = value;
}
};
}
// ## compare version
//
// a = '1.11.1', b = '1.8.2'
// a > b return 1
// a < b return -1
// a = b return 0
//
function compareVersion(a, b) {
var aa = a.split('.'),
bb = b.split('.'),
al = aa.length,
bl = bb.length,
len = Math.max(al, bl),
aInt, bInt;
for (; len > 0; len--) {
aInt = parseInt(aa.shift()) || 0;
bInt = parseInt(bb.shift()) || 0;
if (aInt > bInt) return 1;
else if (aInt < bInt) return -1;
}
return 0;
}
// 定义所有变换属性的 transition-property
var propertyMap = {};
// Register other CSS hooks
registerCssHook('x');
registerCssHook('y');
registerCssHook('z');
registerCssHook('translateX');
registerCssHook('translateY');
registerCssHook('translateZ');
registerCssHook('translate');
registerCssHook('translate3d');
registerCssHook('scale');
registerCssHook('scaleX');
registerCssHook('scaleY');
registerCssHook('scaleZ');
registerCssHook('scale3d');
registerCssHook('rotate');
registerCssHook('rotateX');
registerCssHook('rotateY');
registerCssHook('rotateZ');
registerCssHook('rotate3d');
registerCssHook('skew');
registerCssHook('skewX');
registerCssHook('skewY');
registerCssHook('pers');
function registerCssHook(prop, isPixels) {
// 所有属性都不应该被强制添加px单位,即使是 translate,因为它也可能是百分比
if (!isPixels) {
$.cssNumber[prop] = true;
}
propertyMap[prop] = support.transform;
$.cssHooks[prop] = {
get: function(elem) {
var t = $.css(elem, 'transform');
return t.get(prop);
},
set: function(elem, value) {
var t = $.css(elem, 'transform');
t.setFromString(prop, value);
$.style(elem, 'transform', t);
}
};
}
// ## Transform class
//
// var t = new Transform('rotate(90) scale(4)');
//
// Set properties
//
// t.set('rotate', 40)
//
// Get properties
//
// t.rotate => '40deg'
// t.scale => '4'
//
// The output string
//
// t.toString() => 'rotate(40deg) scale(4)'
//
function Transform(str) {
if (typeof str === 'string') {
this.parse(str);
}
}
Transform.prototype = {
// ### setFromString()
//
// t.setFromString('scale', '2,4'); => ['scale', '2', '4']
// t.setFromString('scale', [,4]); => ['scale', null, '4']
//
setFromString: function(prop, val) {
var args;
if ($.isArray(val)) {
for (var i = 0; i < 3; i++) {
if (val[i] === undefined) val[i] = null;
}
args = val;
} else {
args = (typeof val === 'string') ? val.split(',') : [val];
}
args.unshift(prop);
Transform.prototype.set.apply(this, args);
},
set: function(prop) {
var args = Array.prototype.slice.call(arguments, 1);
if (this.setter[prop]) {
this.setter[prop].apply(this, args);
} else {
this[prop] = args.join(',');
}
},
get: function(prop) {
if (this.getter[prop]) {
return this.getter[prop].call(this);
} else {
return this[prop];
}
},
setter: {
// ### x / y / z
//
// .css({ x: 4 }) => 'translate(4px, 0)'
// .css({ y: 10 }) => 'translate(4px, 10px)'
// .css({ z: 5 }) => 'translate(4px, 10px) translateZ(5px)'
//
x: function(x) {
this.set('translate', x, null);
},
y: function(y) {
this.set('translate', null, y);
},
z: function(z) {
this.setProp('translateZ', z, 'px');
},
translateX: function(x) {
this.set('x', x);
},
translateY: function(y) {
this.set('y', y);
},
translateZ: function(z) {
this.set('z', z);
},
// ### translate
//
// .css({ translate: '2, 5' }) => 'translate(2px, 5px)'
// .css({ translate: '' }) => remove 'translate(2px, 5px)'
//
translate: function(x, y) {
if (y === undefined) {
y = x;
}
this.setDoubleProp('translate', x, y, 'px');
},
// ### translate3d
//
// .css('translate3d', [100,200,300]); => 'translate(100px, 200px) translateZ(300px)'
//
translate3d: function(x, y, z) {
if (y === undefined && z === undefined) {
z = y = x;
}
this.set('translate', x, y);
this.set('z', z);
},
// ### scale
//
// .css({ scale: 3 }) => 'scale(3)'
// .css({ scale: '3,2' }) => 'scale(3,2)'
//
scale: function(x, y) {
if (y === undefined) {
y = x;
}
this.setDoubleProp('scale', x, y, '');
},
// ### scale3d
//
// .css('scale3d', [1,2,3]); => 'scale(1, 2) scaleZ(3)'
//
scale3d: function(x, y, z) {
if (y === undefined && z === undefined) {
z = y = x;
}
this.set('scale', x, y);
this.set('scaleZ', z);
},
scaleX: function(x) {
this.set('scale', x, null);
},
scaleY: function(y) {
this.set('scale', null, y);
},
scaleZ: function(z) {
this.setProp('scaleZ', z, '');
},
// ### rotate
//
// .css({ rotate: 30 })
// .css({ rotate: '30' })
// .css({ rotate: '30deg' })
//
rotate: function(angle) {
this.setProp('rotate', angle, 'deg');
},
rotateX: function(angle) {
this.setProp('rotateX', angle, 'deg');
},
rotateY: function(angle) {
this.setProp('rotateY', angle, 'deg');
},
rotateZ: function(angle) {
this.set('rotate', angle);
},
rotate3d: function(x, y, z) {
if (y === undefined && z === undefined) {
z = y = x;
}
this.set('rotateX', x);
this.set('rotateY', y);
this.set('rotate', z);
},
skew: function(x, y) {
if (y === undefined) {
y = x;
}
this.set('skewX', x);
this.set('skewY', y);
},
skewX: function(x) {
this.setProp('skewX', x, 'deg');
},
skewY: function(y) {
this.setProp('skewY', y, 'deg');
},
// {pers: 100} => transform: perspective(100px);
pers: function(pers) {
this.setProp('perspective', pers, 'px');
}
},
getter: {
x: function() {
return this._translateX || '0';
},
y: function() {
return this._translateY || '0';
},
z: function() {
return this.translateZ || '0';
},
translateX: function() {
return this.get('x');
},
translateY: function() {
return this.get('y');
},
translateZ: function() {
return this.get('z');
},
translate: function() {
return [this.get('x'), this.get('y')];
},
translate3d: function() {
return [this.get('x'), this.get('y'), this.get('z')];
},
scale: function() {
var x = this.get('scaleX'),
y = this.get('scaleY'),
s = [x, y];
// '2,2' => '2'
// '2,1' => ['2','1']
return (s[0] === s[1]) ? s[0] : s;
},
scale3d: function() {
var x = this.get('scaleX'),
y = this.get('scaleY'),
z = this.get('scaleZ'),
s = [x, y, z];
// '2,1,2' => ['2','1','2']
return s;
},
scaleX: function() {
return this._scaleX || '1';
},
scaleY: function() {
return this._scaleY || '1';
},
scaleZ: function() {
return this.scaleZ || '1';
},
rotate: function(theta) {
return this.rotate || '0';
},
rotateX: function(theta) {
return this.rotateX || '0';
},
rotateY: function(theta) {
return this.rotateY || '0';
},
rotateZ: function(theta) {
return this.get('rotate');
},
rotate3d: function() {
return [this.get('rotateX'), this.get('rotateY'), this.get('rotate')];
},
skew: function() {
return [this.get('skewX'), this.get('skewY')];
},
skewX: function() {
return this.skewX || 0;
},
skewY: function() {
return this.skewY || 0;
},
// .css('pers', 100).css('pers') => 100px
pers: function() {
return this.perspective || 0;
}
},
// ### setProp()
// If the property value is an empty string, the attributes are removed
// If the attribute values are not legal, ignore Settings
//
// .css({'rotate': 30}).css({'rotate': ''}) => remove 'rotate(30deg)'
// .css({'rotate': 30}).css({'rotate': null}) => 'rotate(30deg)'
//
setProp: function(prop, value, u) {
if (value !== undefined && value !== '') {
if (isNaN(parseFloat(value))) {
value = undefined;
}
}
if (value === '') {
delete this[prop];
} else if (value !== undefined) {
this[prop] = unit(value, u);
}
},
// ### setDoubleProp()
// If both the attribute value is empty string, the attributes are removed
// If one of the attribute value is empty string, is set to the default value
// If the attribute values are not legal, ignore Settings
//
// .css({'scaleX': 3}).css({'scale': ''}) => remove 'scale(3, 1)'
// .css({'scaleX': 3}).css({'scale': ['',4]}) => 'scale(1, 4)'
// .css({'scaleX': 3}).css({'scale': [null,4]}) => 'scale(3, 4)'
//
// Note
// .css({'translate3d': '2,,'}) === .css({'translate3d': [2, '', '']})
// .css({'translate3d': [2,,,]}) === .css({'translate3d': '2, null, null'})
//
setDoubleProp: function(prop, value1, value2, u) {
if (this['_' + prop + 'X'] === undefined) {
this['_' + prop + 'X'] = this.get(prop + 'X');
}
if (this['_' + prop + 'Y'] === undefined) {
this['_' + prop + 'Y'] = this.get(prop + 'Y');
}
if (value1 !== undefined && value1 !== '') {
if (isNaN(parseFloat(value1))) {
value1 = undefined;
}
}
if (value2 !== undefined && value2 !== '') {
if (isNaN(parseFloat(value2))) {
value2 = undefined;
}
}
if (value1 === '' && value2 === '') {
delete this['_' + prop + 'X'];
delete this['_' + prop + 'Y'];
delete this[prop];
} else {
if (value1 === '') {
delete this['_' + prop + 'X'];
value1 = this.get(prop + 'X');
} else if (value2 === '') {
delete this['_' + prop + 'Y'];
value2 = this.get(prop + 'Y');
}
if (value1 !== undefined) {
this['_' + prop + 'X'] = unit(value1, u);
}
if (value2 !== undefined) {
this['_' + prop + 'Y'] = unit(value2, u);
}
if (prop === 'scale') {
this[prop] = this['_' + prop + 'X'] === this['_' + prop + 'Y'] ?
this['_' + prop + 'X'] :
this['_' + prop + 'X'] + ',' + this['_' + prop + 'Y'];
} else {
this[prop] = this['_' + prop + 'X'] + ',' + this['_' + prop + 'Y'];
}
}
},
// ### parse()
//
// 'rotate(90) scale(4)' => self.setFromString('rotate', 90); self.setFromString('scale', 4);
//
parse: function(str) {
var self = this;
str.replace(/([a-zA-Z0-9]+)\((.*?)\)/g, function(x, prop, val) {
self.setFromString(prop, val);
});
},
toString: function() {
var re = [];
for (var i in this) {
if (this.hasOwnProperty(i)) {
if ((!support.transform3d) && (
(i === 'rotateX') ||
(i === 'rotateY') ||
(i === 'translateZ') ||
(i === 'scaleZ') ||
(i === 'perspective'))) {
continue;
}
if (i[0] !== '_') {
re.push(i + '(' + this[i] + ')');
}
}
}
return re.join(' ');
}
};
// ### getTransition()
// Returns the transition string to be used for the `transition` CSS property.
//
// getTransition({ opacity: 1, rotate: 30 }, 500, 'ease');
// => 'opacity 500ms ease, -webkit-transform 500ms ease'
//
function getTransition(properties, duration, easing, specialEasing) {
// 获取属性对应的 transition-property 和 transition-timing-function
// {marginTop: 100, paddingLeft: 200} => {'margin-top': 'swing', 'padding-left': 'swing'}
var props = {};
for (var p in properties) {
var key = $.camelCase(p); // Convert 'text-align' => 'textAlign'
key = propertyMap[key] || $.cssProps[key] || key;
// Get vendor specify propertie
// For example 'transform-origin' 'perspective'
if (support[key]) {
key = support[key];
}
if (!(key in props)) {
props[key] = specialEasing[p];
}
}
var MS = toMS(duration);
// For more properties, add them this way:
// 'margin 200ms ease, padding 200ms ease, ...'
var transitions = [];
for (var p in props) {
transitions.push(uncamel(p) + ' ' + MS + ' ' + (props[p]));
}
return transitions.join(',');
}
// ### disposeSpecialValue()
//
// .css({left: auto}).animate({left: '100'}) => .css({left: 0}).animate({left: 100});
// .animate({opacity: 'show'}) => .css({opacity: 0}).show().animate({opacity: 1});
// .animate({opacity: 'hide'}) => .css({opacity: 1}).animate({opacity: 0}, function() { $(this).hide() });
//
function disposeSpecialValue($self, endProps, startProps, callback) {
var clearStyles = {},
hidden = $self.css('display') === 'none',
toggle = $self.data('bjj-toggle'),
show;
// Height/width overflow pass
if ('height' in endProps || 'width' in endProps) {
if ($self.css('display') === 'inline' && $self.css('float') === 'none') {
$self.css('display', 'inline-block');
}
}
for (var p in endProps) {
var startValue = $self.css(p),
endValue = endProps[p],
isToggle = endValue === 'toggle';
if (endValue === 'show' || isToggle && (show === true || show === undefined && (hidden || toggle === false))) {
clearStyles[p] = '';
if (isToggle) $self.data('bjj-toggle', true);
// jQuery 在获取 display: none 元素的宽高等属性时,会先让元素显示出来,但是又会为元素添加 position: absolute。
// 因此,当元素的宽高为百分比时,其基于的父元素会改变为最近的定位父元素,这就导致获取的宽高不正确
// 这里只好手动让元素显示出来后,再获取它的宽高。
if (hidden) $self.show();
var inlineStyleValue = $self[0].style[p],
originalValue = $self.css(p, '').css(p);
$self.css(p, inlineStyleValue);
if (hidden) $self.hide();
if (hidden || toggle === false || startValue != originalValue) {
if (show === undefined) show = true;
endValue = originalValue;
if (hidden) startValue = 0;
} else {
endValue = undefined;
}
} else if (endValue === 'hide' || isToggle && (show === false || show === undefined && (!hidden || toggle === true))) {
clearStyles[p] = '';
if (isToggle) $self.data('bjj-toggle', false);
if (!hidden || toggle === true) {
if (show === undefined) show = false;
endValue = 0;
} else {
endValue = undefined;
}
}
var startUnit = getUnit(startValue),
endUnit = getUnit(endValue),
startColor,
endColor;
if (isComplex(startValue)) { // 检查复合值是否为阴影
startValue = checkShadow(p, startValue);
} else if (startColor = isColor(startValue)) { // 是否是颜色
} else if (isKeyword(startValue)) { // 处理关键字
// 如果是阴影属性,则返回一个复合属性
startValue = checkShadow(p, startValue, (endValue + '').indexOf('inset') >= 0);
if (!isComplex(startValue)) {
startValue = 0; // 主要针对定位属性,如:left默认为auto
}
} else if (!startUnit && endUnit) {
startValue += endUnit;
}
if (isComplex(endValue)) {
endValue = checkShadow(p, endValue);
} else if (endColor = isColor(endValue)) {
} else if (isKeyword(endValue)) {
endValue = checkShadow(p, endValue, startValue.indexOf('inset') >= 0); // 如果是阴影属性,则返回一个复合属性
if (!isComplex(endValue) && endValue.match(/^[+-]=/)) { // 主要针对递增或递减值,例如:left: '+=30%'
endValue = calculateValue($self, p, startValue, endValue, 1);
}
} else if (endValue !== undefined && !endUnit && startUnit) {
endValue += startUnit;
}
if ((endValue === undefined)
//|| (startValue == endValue)
|| (!!endColor ^ !!startColor)) {
delete endProps[p];
continue;
}
endProps[p] = endValue;
startProps[p] = startValue;
}
var fn = callback;
if (show === true) {
if ('width' in clearStyles || 'height' in clearStyles) {
if($self.data('bjj-overflow') === undefined) {
$self.data('bjj-overflow', $self[0].style.overflow);
}
clearStyles['overflow'] = $self.data('bjj-overflow');
$self.css('overflow', 'hidden');
}
$self.show();
} else if (show === false) {
if ('width' in clearStyles || 'height' in clearStyles) {
if($self.data('bjj-overflow') === undefined) {
$self.data('bjj-overflow', $self[0].style.overflow);
}
clearStyles['overflow'] = '';
$self.css('overflow', 'hidden');
}
clearStyles['display'] = 'none';
}
if (show !== undefined) {
fn = function() {
if(show) $self.removeData('bjj-overflow');
$self.css(clearStyles);
if (typeof callback === 'function') callback.call(this);
};
}
return fn;
}
// 调用队列
function callOrQueue(self, queue, callback) {
if (queue === true) {
self.queue(callback);
} else if (queue) {
self.queue(queue, callback);
} else {
self.each(function() {
callback.call(this);
});
}
}
function finishCall(self, callback, next) {
if (typeof callback === 'function') {
callback.call(self);
}
if (typeof next === 'function') {
next();
}
}
function delayRun(next) {
var self = this,
$self = $(self),
transitionDelayRunParams = $self.data('bjj-transitionDelayRunParams');
if (!transitionDelayRunParams) return;
var transitionValueList = transitionDelayRunParams.transitionValueList,
params = transitionDelayRunParams.queueParams.shift(),
startProps = {},
endProps = params.endProps,
duration = params.duration,
easing = params.easing,
queue = params.queue,
specialEasing = params.specialEasing,
callback = params.callback = disposeSpecialValue($self, endProps, startProps, params.callback),
empty = $.isEmptyObject(endProps);
// If there's nothing to do...
if (duration === 0 || empty) {
if (!empty) $self.css(endProps);
finishCall(self, callback, next);
return;
}
$self.css(startProps);
self.offsetWidth; // 强制刷新
// Prepare the callback.
var cb = function(e) {
var i = $.inArray(timer, $.timers);
if (i >= 0) $.timers.splice(i, 1);
i = $.inArray(transitionValue, transitionValueList);
if (i >= 0) transitionValueList.splice(i, 1);
self.style[support.transition] = transitionValueList.join(',');
finishCall(self, callback, next);
};
var stop = function(gotoEnd) {
window.clearTimeout(timerID);
var i = $.inArray(transitionValue, transitionValueList);
if (i >= 0) transitionValueList.splice(i, 1);
self.style[support.transition] = transitionValueList.join(',');
if (gotoEnd) {
finishCall(self, callback, next);
} else {
var curProp = {};
for (var p in endProps) {
var startValue = startProps[p],
endValue = endProps[p],
endColor = isColor(endValue),
dv;
setCubicBezier(specialEasing[p]);
var bezierY = cubicBezier.getY(($.now() - startTime) / duration);
if (endColor) { // 如果是颜色
dv = calculateColor(startValue, endColor, bezierY);
} else {
dv = calculateValue($self, p, startValue, endValue, bezierY);
}
curProp[p] = dv;
}
$self.css(curProp);
}
};
// 模拟 .stop() 所需要的对象
var timer = function() {
// 在 animate 动画完成后,且下一个队列函数已经执行,timer 被加入到 $.timers 队列中后,此时 jQuery 又会执行一次 jQuery.fx.tick
// 在 jQuery.fx.tick 中 $.timers 会将返回值为 false 的 timer 全部清空,这样会导致当前的动画无法执行 stop()
// 注释:animate 的动画中 timer() 的返回值为当前动画的剩余时间
return true;
};
timer.elem = self;
timer.queue = queue;
timer.anim = { stop: stop };
$.timers.push(timer);
// Build the `transition` property.
var transitionValue = getTransition(endProps, duration, easing, specialEasing);
transitionValueList.push(transitionValue);
var startTime = $.now();
self.style[support.transition] = transitionValueList.join(',');
$self.css(endProps);
// transitionend 事件还是存在多个 bug
// 例如,多个 transition-property 并行时,先结束的动画会将触发其它属性动画的 transitionend 事件
// 再例如,元素动画途中 display: none 后,将不会再触发 transitionend 事件
// 因此只好弃用它,使用 setTimeout 代替它
var timerID = window.setTimeout(cb, duration);
}
// 模拟 .finish() 所需要的方法
// 当执行 finish() 时
// 如果动画队列中只有一个动画,那么这里的 finish 不会执行,只会执行 stop()
// 如果动画队列中有多个动画,那么除第一个动画以外,剩余多少动画,这里的 finish 就会被执行多少次。
delayRun.finish = function() {
var $self = $(this),
transitionDelayRunParams = $self.data('bjj-transitionDelayRunParams');
if (!transitionDelayRunParams) return;
var params = transitionDelayRunParams.queueParams.shift();
// 能够执行 finish 的动画,将不会执行 delayRun
params.callback = disposeSpecialValue($self, params.endProps, {}, params.callback);
$self.css(params.endProps);
finishCall(this, params.callback);
};
// ## transition()
function transition(properties, duration, easing, callback, queue, specialEasing) {
duration = parseInt(toMS(duration), 10);
this.each(function() {
var $self = $(this),
transitionDelayRunParams = $self.data('bjj-transitionDelayRunParams');
if (!transitionDelayRunParams) {
transitionDelayRunParams = {
transitionValueList: [], // 用于分别保存每一个对象的 transition 属性值列表
queueParams: [] // 用于保存动画队列中各个动画的参数
}
}
transitionDelayRunParams.queueParams.push({
endProps: $.extend(true, {}, properties),
duration: duration,
easing: easing,
callback: callback,
queue: queue,
specialEasing: specialEasing
});
$self.data('bjj-transitionDelayRunParams', transitionDelayRunParams);
});
// Use jQuery's fx queue.
callOrQueue(this, queue, delayRun);
return this;
}
// ### propFilter()
// jQuery 源码使用该方法来过滤出属性和缓动
//
// props = {left: [200, 'easeInBack'], width: [100, 'linear'], height: 200}
// specialEasing = {height: 'swing'};
//
// propFilter(props, specialEasing);
//
// => props == {left: 200, width: 100, height: 200}
// specialEasing == {left: 'easeInBack', width: 'linear', height: 'swing'}
//
function propFilter(props, specialEasing) {
var index, name, easing, value, hooks;
// camelCase, specialEasing and expand cssHook pass
for (index in props) {
name = $.camelCase(index);
easing = specialEasing[name];
value = props[index];
if ($.isArray(value)) {
easing = value[1];
value = props[index] = value[0];
}
if (index !== name) {
props[name] = value;
delete props[index];
}
hooks = $.cssHooks[name];
if (hooks && 'expand' in hooks) {
value = hooks.expand(value);
delete props[name];
// not quite $.extend, this wont overwrite keys already present.
// also - reusing 'index' from above because we have the correct 'name'
for (index in value) {
if (!(index in props)) {
props[index] = value[index];
specialEasing[index] = easing;
}
}
} else {
specialEasing[name] = easing;
}
}
}
// ### Compatible with the following written
// Example:
// .animate.({'translate3d': [100, 200, 300]});
// .animate.({'scale': [1, 2]});
// .animate.({'scale': '1,2'});
var _animate = $.fn._animate = $.fn.animate;
$.fn.animate = function(properties, duration, easing, callback) {
var queue = true,
specialEasing = {},
originalProperties = $.extend({}, properties),
originalDuration = duration,
originalEasing = easing,
originalCallback = callback;
if (typeof duration === 'function') { // Account for `.transition(properties, callback)`.
callback = duration;
duration = undefined;
} else if ($.isPlainObject(duration)) { // Account for `.transition(properties, options)`.
originalDuration = $.extend({}, duration);
easing = duration.easing;
queue = typeof duration.queue === 'undefined' ? true : duration.queue;
specialEasing = duration.specialEasing || specialEasing;
callback = duration.complete;
duration = duration.duration;
} else if (typeof easing === 'function') { // Account for `.transition(properties, duration, callback)`.
callback = easing;
easing = undefined;
}
// Set defaults. (`400` duration, `ease` easing)
if (!duration && duration !== 0) {
duration = $.fx.speeds._default;
}
if (!easing) {
easing = $.cssEase._default;
}
// 拆分属性
resolveProp(properties);
var useTransition = support.transition;
if (useTransition && ('scrollLeft' in properties || 'scrollTop' in properties)) {
useTransition = false;
}
if (useTransition) {
// 将属性名与对应的缓动过滤出来
propFilter(properties, specialEasing);
var transformEasing;
for (var p in specialEasing) {
// 将缓动转换为贝塞尔函数
var e = specialEasing[p] = convertEase(specialEasing[p] || easing);
if (typeof e === 'undefined') {
useTransition = false;
break;
} else if (propertyMap[p] === support.transform) {
// 判断transform是否设置了不同的easing
if (transformEasing && transformEasing !== e) {
useTransition = false;
break;
} else {
transformEasing = e;
}
}
}
}
// 如果不支持 transition 动画,或者不支持该缓动类型,再或者动画属性中包含不支持的属性,则使用 animate 实现
if (!useTransition) {
// 修复一个 动画的 bug,该 bug 仅在 animate() 动画中才会出现
//
// jQuery 源码 6987 行
// if ( tween.elem[ tween.prop ] != null &&
// (!tween.elem.style || tween.elem.style[ tween.prop ] == null) ) {
// return tween.elem[ tween.prop ];
// }
//
// 这段代码检查元素是否定义了该属性,如果定义了,并且元素的style属性中未定义该属性,则直接返回该属性值
// 由于 自身拥有 x,y 这两个属性,因此最终导致了 元素的 x,y 动画起始位置不正确
this.each(function() {
try {
// 在window平台下的safari浏览器下,删除属性会导致程序报错
// 不过在该环境下即使不删除这两个属性,动画也不会出现问题
if (this.x != null) delete this.x;
if (this.y != null) delete this.y;
} catch(e) {}
});
return _animate.call(this, originalProperties, originalDuration, originalEasing, originalCallback);
}
// normalize opt.queue - true/undefined/null -> 'fx'
if (queue == null || queue === true) {
queue = 'fx';
}
return transition.call(this, properties, duration, easing, callback, queue, specialEasing);
}
// ### resolveProp(props)
//
// {scale: '3,2'} => {scaleX: 3, scaleY: 2}
//
var xyz = ['X', 'Y', 'Z'],
checkProp = [
'translate',
'translate3d',
'scale',
'scale3d',
'rotate3d',
'skew'
];
function resolveProp(props) {
for (var i = 0, l = checkProp.length; i < l; i++) {
var p = checkProp[i];
if (p in props) {
var val = props[p];
delete props[p];
// 区分 [[1,2], 'easeOutBack'] 和 [1,2]
var easeing;
if ($.isArray(val)) {
var val0 = val[0],
val1 = val[1];
if ($.isArray(val0)
|| (typeof val0 === 'string' && val0.split(',').length > 1)
|| (val1 && isNaN(parseFloat(val1)))) {
val = val0;
easeing = val1;
}
}
var is3d = p.indexOf('3d') !== -1,
arr;
if ($.isArray(val)) {
arr = val;
} else {
arr = (typeof val === 'string') ? val.split(',') : [val];
// {scale: [3]} => {scale: [3, 3]}
if (arr.length < 2) {
if (is3d) {
arr[2] = arr[1] = arr[0];
} else {
arr[1] = arr[0];
}
}
}
if (is3d) p = p.slice(0, -2);
var def = p === 'scale' ? 1 : 0;
// {scale: [2, 3]} => {scaleX: 2, scaleY: 3}
for (var j = 0; j < 3; j++) {
val = arr[j];
if (val || val === 0) {
props[p + xyz[j]] = easeing ? [val, easeing] : val;
} else if (val === '') {
props[p + xyz[j]] = easeing ? [def, easeing] : def;
}
}
}
}
}
// ### uncamel(str)
// Converts a camelcase string to a dasherized string.
//
// 'marginLeft' => 'margin-left'
// 'webkitTransformOrigin' => '-webkit-transform-origin'
//
function uncamel(str) {
if (!str.indexOf('webkit')) str = 'W' + str.substr(1);
if (!str.indexOf('moz') || !str.indexOf('ms')) str = 'M' + str.substr(1);
return str.replace(/([A-Z])/g, '-$1').toLowerCase();
}
// ### unit(number, unit)
//
// unit(30, 'px') => '30px'
// unit('30%', 'px') => '30%'
//
function unit(i, units) {
if ((typeof i === 'string') && (!i.match(/^[\-0-9\.]+$/))) {
return i;
} else {
return '' + i + units;
}
}
// ### getUnit(str)
//
// getUnit('30px') => 'px'
// getUnit('30%') => '%'
// getUnit('30') => ''
//
function getUnit(value) {
if (typeof value !== 'string') return '';
var s = value.match(/^(?:\-=|\+=)?[\-0-9\.]+/);
if (!s) return '';
return value.substr(s[0].length) || '';
}
// ### getBaseValue($self, prop, index)
// 根据属性获取百分比时基于的值
// @param index 参数表示需要获取的子属性在该符合属性值组中的索引位置,例如 ('margin', 0) == 'margin-top'
//
// getBaseValue($self, 'width') => $self.parent().width()
// getBaseValue($self, 'x') => $self.outerWidth()
//
// getBaseValue($self, 'margin', 2) => $self.parent().height()
// getBaseValue($self, 'background-position', 0) => $self.outerWidth()
//
function getBaseValue($self, prop, index) {
var baseSelfKeyword = [
'x',
'y',
'position',
'origin',
'radius'
],
horDirKeyword = [
'x',
'width',
'padding',
'margin',
'left',
'right'
],
baseSelf = false,
dir = 0, // 表示方向,1表示水平,-1表示垂直
baseValue = 0,
p = prop.toLowerCase(),
i = 0,
l = baseSelfKeyword.length;
for (; i < l; i++) {
var k = baseSelfKeyword[i];
if (p.indexOf(k) >= 0) {
baseSelf = true;
}
}
i = 0;
l = horDirKeyword.length;
for (; i < l; i++) {
if (p.indexOf(horDirKeyword[i]) >= 0) {
dir = 1;
}
}
if (!dir) {
if (p.indexOf('position') >= 0
|| p.indexOf('size') >= 0
|| p.indexOf('origin') >= 0) {
if (index === 1) {
dir = -1;
} else {
dir = 1;
}
} else {
if (index === 1 || index === 3) {
dir = 1;
} else {
dir = -1;
}
}
}
if (dir === 1) {
baseValue = baseSelf ? $self.outerWidth() : $self.parent().width();
} else if (dir === -1) {
baseValue = baseSelf ? $self.outerHeight() : $self.parent().height();
}
return baseValue;
}
// ### convertUnit()
// 将属性值转换为指定单位的新值
// @param index 参数表示需要获取的子属性在该符合属性值组中的索引位置,例如 ('margin', 0) == 'margin-top'
//
// convertUnit($self, 'margin', 1, 100px, %) => (100 / $self.parent().width()) * 100 + '%'
//
function convertUnit($self, prop, index, value, newUnit) {
var px = 'px',
em = 'em',
pe = '%',
oldUnit = getUnit(value),
newValue = value;
if (oldUnit != newUnit) {
var oldValue = parseFloat(value);
if (newUnit === px) {
if (oldUnit === pe) {
newValue = oldValue / 100 * getBaseValue($self, prop, index) + px;
} else if (oldUnit === em) {
newValue = oldValue * parseFloat($self.css('font-size')) + px;
}
} else if (newUnit === pe) {
var baseValue = getBaseValue($self, prop, index);
if (!baseValue) {
newValue = 0;
} else if (oldUnit === px) {
newValue = (oldValue / baseValue) * 100 + pe;
} else if (oldUnit === em) {
newValue = (oldValue * parseFloat($self.css('font-size')) / baseValue) * 100 + pe;
}
} else if (newUnit === em) {
if (oldUnit === px) {
newValue = oldValue / parseFloat($self.css('font-size')) + em;
} else if (oldUnit === pe) {
newValue = oldValue / 100 * getBaseValue($self, prop, index) / parseFloat($self.css('font-size')) + em;
}
}
}
return newValue;
}
// ### calculateValue($self, begin, end, pos)
// 根据 0-1 之间的位置比,计算两个属性值在该位置上的中间值
//
// calculateValue($self, 'left', 0, 10, .5); => 5
// calculateValue($self, 'left', '50px', '100%', .5); => (100 - 50 / $self.parent().width() * 100) * .5 + 50 / $self.parent().width() * 100 + '%'
// calculateValue($self, 'left', '50px', '+=100%', .5); => $self.parent().width() * 100% * .5 + 50 + 'px'
//
var rfxnum = new RegExp('^(?:([+-])=|)([+-]?(?:\\d*\\.|)\\d+(?:[eE][+-]?\\d+|))([a-z%]*)$', 'i');
function calculateValue($self, prop, begin, end, pos) {
begin = (begin + '').split(' ');
end = (end + '').split(' ');
var value = [],
i = 0,
l = end.length;
for (; i < l; i++) {
var startValue = begin[i] || 0,
endValue = end[i],
startUnit = getUnit(startValue),
endUnit = getUnit(endValue) || startUnit,
endColor = isColor(endValue);
if (endColor) {
value[i] = calculateColor(startValue, endColor, pos);
} else if (isNaN(parseFloat(endValue))) { // 可能是一些关键字或者特殊值
var ret = rfxnum.exec(end[i]);
if (ret) { // 如果结束值为 '+=' 或者 '-='
var v = (ret[1] + 1) * ret[2] + ret[3];
endUnit = startUnit || endUnit; // 单位改用起始值的单位
v = convertUnit($self, prop, i, v, endUnit);
startValue = parseFloat(startValue);
endValue = startValue + parseFloat(v);
value[i] = (endValue - startValue) * pos + startValue + endUnit;
} else { // 如果是关键字,就保留原值
value[i] = endValue;
}
} else {
endValue = parseFloat(endValue);
startValue = convertUnit($self, prop, i, startValue, endUnit);
startValue = parseFloat(startValue);
value[i] = (endValue - startValue) * pos + startValue + endUnit;
}
}
l = begin.length;
for (; i < l; i++) {
value.push(begin[i]);
}
value = value.join(' ');
var numberValue = parseFloat(value);
return value == numberValue ? numberValue : value;
}
// ### toMS(duration)
// Converts given `duration` to a millisecond string.
//
// toMS('fast') => $.fx.speeds[i] => '200ms'
// toMS('normal') => $.fx.speeds._default => '400ms'
// toMS(10) => '10ms'
// toMS('100ms') => '100ms'
//
function toMS(duration) {
var i = duration;
// Allow string durations like 'fast' and 'slow', without overriding numeric values.
if (typeof i === 'string' && (!i.match(/^[\-0-9\.]+/))) {
i = $.fx.speeds[i] || $.fx.speeds._default;
}
return unit(i, 'ms');
}
// ### isKeyword(value)
// 判断一个值是否为关键字
//
// isKeyword('auto'); => true
// isKeyword('50px 50px 1px #000'); => false
//
function isKeyword(value) {
return typeof value === 'string' && isNaN(parseFloat(value)) && value.split(' ').length === 1;
}
// ### isComplex(value)
// 判断一个值是否为复合值
//
// isComplex('50px'); => false
// isComplex('rgb(0, 0, 0)'); => false
// isComplex('50px 50px 1px #000'); => true
//
function isComplex(value) {
var str = value + '';
return str.split(',').length === 1 && str.split(' ').length > 1;
}
// ### isColor(value)
// 判断一个值是否为颜色,如果是颜色则返回该颜色的 rgb 形式
//
// isColor('white'); => 'rgb(255,255,255)'
// isColor('auto'); => false
//
function isColor(value) {
if (typeof value === 'string') {
if (!value.indexOf('rgb') && value.split(' ').length === 1) return value;
return getCorrectValue('color', value);
}
return false;
}
// ### checkShadow(prop, value, inset)
// 检查一个属性是否为阴影
// 如果不是阴影,则返回原值
// 如果阴影值不合法,则返回默认阴影。
// 如果是阴影则返回该阴影的正确形式,同时去掉参数中的多余空格。
// 第三个参数表示 boxShadow 的默认阴影是否为内阴影
//
// checkShadow('boxShadow', '50px 50px 1px #000'); => rgb(0,0,0) 50px 50px 1px 0px
// checkShadow('boxShadow', 'none'); => rgba(0,0,0,0) 0px 0px 0px 0px
// checkShadow('left', '50px'); => '50px'
//
function checkShadow(prop, value, inset) {
if (prop.indexOf('hadow') < 0) return value;
value = getCorrectValue(prop, value);
if (!value) {
var def = inset && !prop.indexOf('box') ? 'inset 0 0 0 transparent' : '0 0 0 transparent';
value = getCorrectValue(prop, def);
}
value = value.replace(/,\s/g, ',');
return value;
}
// ### getCorrectValue(prop, value)
// 返回该值的正确形式
//
// getCorrectValue('color', 'white'); => 'rgb(255,255,255)'
// getCorrectValue('boxShadow', '50px 50px 1px #000'); => rgb(0, 0, 0) 50px 50px 1px 0px
//
function getCorrectValue(prop, value) {
testElem.style[prop] = '';
testElem.style[prop] = value;
$testElem.appendTo('body');
value = testElem.style[prop] !== '' && testElem.style[prop] !== 'none' && $testElem.css(prop);
$testElem.detach();
return value;
}
// ### calculateColor(begin, end, pos)
// 根据 0-1 之间的位置比,计算两个颜色在该位置上的过渡色
//
// calculateColor([255,255,255,1], [0,0,0,1], .5); => 'rgba(127,127,127,1)'
// calculateColor([255,255,255], [0,0,0], .5); => 'rgb(127,127,127)'
//
function calculateColor(begin, end, pos) {
// 如果传入的是颜色值,将其先解析为数组
if (!$.isArray(begin)) begin = parseColor(begin);
if (!$.isArray(end)) end = parseColor(end);
var len = Math.min(begin.length, end.length),
color = 'rgb',
dr = pos * (end[0] - begin[0]),
dg = pos * (end[1] - begin[1]),
db = pos * (end[2] - begin[2]),
r = begin[0] + dr,
g = begin[1] + dg,
b = begin[2] + db,
a = 1;
if (len > 3) {
color += 'a';
a = parseFloat(begin[3] + pos * (end[3] - begin[3]), 10);
r = begin[0] + dr / a * end[3];
g = begin[1] + dg / a * end[3];
b = begin[2] + db / a * end[3];
}
r = parseInt(r, 10);
g = parseInt(g, 10);
b = parseInt(b, 10);
color += '(' + r + ',' + g + ',' + b ;
if (len > 3) { color += ',' + a; }
color += ')';
return color;
}
// ### parseColor(color)
// 将一个颜色值转化为数组形式
//
// 'rgba(127,127,127,1)' => [127, 127, 127, 1]
// 'rgb(127,127,127)' => [127, 127, 127, 1]
// '#000000' => [ 0, 0, 0, 1]
// '#fff' => [255, 255, 255, 1]
//
function parseColor(color) {
var match, triplet;
// Match #aabbcc
if (match = /#([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})/.exec(color)) {
triplet = [parseInt(match[1], 16), parseInt(match[2], 16), parseInt(match[3], 16), 1];
// Match #abc
} else if (match = /#([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])/.exec(color)) {
triplet = [parseInt(match[1], 16) * 17, parseInt(match[2], 16) * 17, parseInt(match[3], 16) * 17, 1];
// Match rgb(n, n, n)
} else if (match = /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(color)) {
triplet = [parseInt(match[1]), parseInt(match[2]), parseInt(match[3]), 1];
} else if (match = /rgba\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9\.]*)\s*\)/.exec(color)) {
triplet = [parseInt(match[1], 10), parseInt(match[2], 10), parseInt(match[3], 10),parseFloat(match[4])];
// No browser returns rgb(n%, n%, n%), so little reason to support this format.
}
return triplet;
}
// ### getArg(str)
// 获取字符串表达式中括号内的参数数组
//
// 'cubic-bezier(0,0,1,1)' => [0,0,1,1]
//
function getArg(str) {
var s = str.match(/\(.*\)$/);
if (s) {
var args = s[0].slice(1, -1).split(',');
for (var i = 0, l = args.length; i < l; i++) {
var arg = parseFloat(args[i]);
args[i] = isNaN(arg) ? undefined : arg;
}
return args;
}
return null;
}
/**
* 创建一个三次贝塞尔对象
* @exception 每一个参数为一个表示点的数组[x,y]
* @param {array} c1 表示起始点的控制点
* @param {array} c2 表示结束点的控制点
* @param {array} begin 表示起始点,默认为[0,0]
* @param {array} end 表示结束点,默认为[1,1]
*/
function CubicBezier() {
this.set.apply(this, arguments);
}
CubicBezier.prototype = {
_bezierFunc: function(p, t, targ) {
return this.begin[p] * Math.pow(1 - t, 3) +
this.c1[p] * 3 * t * Math.pow(1 - t, 2) +
this.c2[p] * 3 * (1 - t) * Math.pow(t, 2) +
this.end[p] * Math.pow(t, 3) -
targ;
},
_deltaBezierFunc: function(p, t, targ) {
var dt = 1e-8;
return (this._bezierFunc(p, t, targ) - this._bezierFunc(p, t - dt, targ)) / dt;
},
set: function(c1, c2, begin, end) {
this.c1 = c1 ? new Point(c1[0], c1[1]) : new Point(0, 0);
this.c2 = c2 ? new Point(c2[0], c2[1]) : new Point(1, 1);
this.begin = begin ? new Point(begin[0], begin[1]) : new Point(0, 0);
this.end = end ? new Point(end[0], end[1]) : new Point(1, 1);
},
/**
* 已知x,求y
* @param {number} x 参数表示一个在贝塞尔曲线上X轴方向的向量,取值在 0.0 - 1.0 之间
* @return 返回x在贝塞尔曲线上对应的y
*/
getY: function(x) {
var t = .5; //设置t的初值
for (var i = 0; i < 1000; i++) {
t = t - this._bezierFunc('x', t, x) / this._deltaBezierFunc('x', t, x);
if (this._bezierFunc('x', t, x) === 0) break;
}
return this._bezierFunc('y', t, 0);
}
}
// ## Point class
function Point(x, y) {
this.x = x || 0;
this.y = y || 0;
}
// 根据缓动函数,创建三次贝塞尔对象
var cubicBezier = new CubicBezier();
function setCubicBezier(easing) {
var args = getArg(easing);
cubicBezier.set([args[0], args[1]], [args[2], args[3]]);
}
return $;
}));