You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1945 lines
54 KiB
JavaScript

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/*!
* 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) {
// 修复一个 <img> 动画的 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属性中未定义该属性则直接返回该属性值
// 由于 <img> 自身拥有 x,y 这两个属性,因此最终导致了 <img> 元素的 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 $;
}));