123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552 |
- /*
- * transform: A jQuery cssHooks adding cross-browser 2d transform capabilities to $.fn.css() and $.fn.animate()
- *
- * limitations:
- * - requires jQuery 1.4.3+
- * - Should you use the *translate* property, then your elements need to be absolutely positionned in a relatively positionned wrapper **or it will fail in IE678**.
- * - transformOrigin is not accessible
- *
- * latest version and complete README available on Github:
- * https://github.com/louisremi/jquery.transform.js
- *
- * Copyright 2011 @louis_remi
- * Licensed under the MIT license.
- *
- * This saved you an hour of work?
- * Send me music http://www.amazon.co.uk/wishlist/HNTU0468LQON
- *
- */
- (function( $, window, document, Math, undefined ) {
-
- /*
- * Feature tests and global variables
- */
- var div = document.createElement("div"),
- divStyle = div.style,
- suffix = "Transform",
- testProperties = [
- "O" + suffix,
- "ms" + suffix,
- "Webkit" + suffix,
- "Moz" + suffix
- ],
- i = testProperties.length,
- supportProperty,
- supportMatrixFilter,
- supportFloat32Array = "Float32Array" in window,
- propertyHook,
- propertyGet,
- rMatrix = /Matrix([^)]*)/,
- rAffine = /^\s*matrix\(\s*1\s*,\s*0\s*,\s*0\s*,\s*1\s*(?:,\s*0(?:px)?\s*){2}\)\s*$/,
- _transform = "transform",
- _transformOrigin = "transformOrigin",
- _translate = "translate",
- _rotate = "rotate",
- _scale = "scale",
- _skew = "skew",
- _matrix = "matrix";
-
- // test different vendor prefixes of these properties
- while ( i-- ) {
- if ( testProperties[i] in divStyle ) {
- $.support[_transform] = supportProperty = testProperties[i];
- $.support[_transformOrigin] = supportProperty + "Origin";
- continue;
- }
- }
- // IE678 alternative
- if ( !supportProperty ) {
- $.support.matrixFilter = supportMatrixFilter = divStyle.filter === "";
- }
-
- // px isn't the default unit of these properties
- $.cssNumber[_transform] = $.cssNumber[_transformOrigin] = true;
-
- /*
- * fn.css() hooks
- */
- if ( supportProperty && supportProperty != _transform ) {
- // Modern browsers can use jQuery.cssProps as a basic hook
- $.cssProps[_transform] = supportProperty;
- $.cssProps[_transformOrigin] = supportProperty + "Origin";
-
- // Firefox needs a complete hook because it stuffs matrix with "px"
- if ( supportProperty == "Moz" + suffix ) {
- propertyHook = {
- get: function( elem, computed ) {
- return (computed ?
- // remove "px" from the computed matrix
- $.css( elem, supportProperty ).split("px").join(""):
- elem.style[supportProperty]
- );
- },
- set: function( elem, value ) {
- // add "px" to matrices
- elem.style[supportProperty] = /matrix\([^)p]*\)/.test(value) ?
- value.replace(/matrix((?:[^,]*,){4})([^,]*),([^)]*)/, _matrix+"$1$2px,$3px"):
- value;
- }
- };
- /* Fix two jQuery bugs still present in 1.5.1
- * - rupper is incompatible with IE9, see http://jqbug.com/8346
- * - jQuery.css is not really jQuery.cssProps aware, see http://jqbug.com/8402
- */
- } else if ( /^1\.[0-5](?:\.|$)/.test($.fn.jquery) ) {
- propertyHook = {
- get: function( elem, computed ) {
- return (computed ?
- $.css( elem, supportProperty.replace(/^ms/, "Ms") ):
- elem.style[supportProperty]
- );
- }
- };
- }
- /* TODO: leverage hardware acceleration of 3d transform in Webkit only
- else if ( supportProperty == "Webkit" + suffix && support3dTransform ) {
- propertyHook = {
- set: function( elem, value ) {
- elem.style[supportProperty] =
- value.replace();
- }
- }
- }*/
-
- } else if ( supportMatrixFilter ) {
- propertyHook = {
- get: function( elem, computed, asArray ) {
- var elemStyle = ( computed && elem.currentStyle ? elem.currentStyle : elem.style ),
- matrix, data;
-
- if ( elemStyle && rMatrix.test( elemStyle.filter ) ) {
- matrix = RegExp.$1.split(",");
- matrix = [
- matrix[0].split("=")[1],
- matrix[2].split("=")[1],
- matrix[1].split("=")[1],
- matrix[3].split("=")[1]
- ];
- } else {
- matrix = [1,0,0,1];
- }
-
- if ( ! $.cssHooks[_transformOrigin] ) {
- matrix[4] = elemStyle ? parseInt(elemStyle.left, 10) || 0 : 0;
- matrix[5] = elemStyle ? parseInt(elemStyle.top, 10) || 0 : 0;
-
- } else {
- data = $._data( elem, "transformTranslate", undefined );
- matrix[4] = data ? data[0] : 0;
- matrix[5] = data ? data[1] : 0;
- }
-
- return asArray ? matrix : _matrix+"(" + matrix + ")";
- },
- set: function( elem, value, animate ) {
- var elemStyle = elem.style,
- currentStyle,
- Matrix,
- filter,
- centerOrigin;
-
- if ( !animate ) {
- elemStyle.zoom = 1;
- }
-
- value = matrix(value);
-
- // rotate, scale and skew
- Matrix = [
- "Matrix("+
- "M11="+value[0],
- "M12="+value[2],
- "M21="+value[1],
- "M22="+value[3],
- "SizingMethod='auto expand'"
- ].join();
- filter = ( currentStyle = elem.currentStyle ) && currentStyle.filter || elemStyle.filter || "";
-
- elemStyle.filter = rMatrix.test(filter) ?
- filter.replace(rMatrix, Matrix) :
- filter + " progid:DXImageTransform.Microsoft." + Matrix + ")";
-
- if ( ! $.cssHooks[_transformOrigin] ) {
-
- // center the transform origin, from pbakaus's Transformie http://github.com/pbakaus/transformie
- if ( (centerOrigin = $.transform.centerOrigin) ) {
- elemStyle[centerOrigin == "margin" ? "marginLeft" : "left"] = -(elem.offsetWidth/2) + (elem.clientWidth/2) + "px";
- elemStyle[centerOrigin == "margin" ? "marginTop" : "top"] = -(elem.offsetHeight/2) + (elem.clientHeight/2) + "px";
- }
-
- // translate
- // We assume that the elements are absolute positionned inside a relative positionned wrapper
- elemStyle.left = value[4] + "px";
- elemStyle.top = value[5] + "px";
-
- } else {
- $.cssHooks[_transformOrigin].set( elem, value );
- }
- }
- };
- }
- // populate jQuery.cssHooks with the appropriate hook if necessary
- if ( propertyHook ) {
- $.cssHooks[_transform] = propertyHook;
- }
- // we need a unique setter for the animation logic
- propertyGet = propertyHook && propertyHook.get || $.css;
-
- /*
- * fn.animate() hooks
- */
- $.fx.step.transform = function( fx ) {
- var elem = fx.elem,
- start = fx.start,
- end = fx.end,
- pos = fx.pos,
- transform = "",
- precision = 1E5,
- i, startVal, endVal, unit;
-
- // fx.end and fx.start need to be converted to interpolation lists
- if ( !start || typeof start === "string" ) {
-
- // the following block can be commented out with jQuery 1.5.1+, see #7912
- if ( !start ) {
- start = propertyGet( elem, supportProperty );
- }
-
- // force layout only once per animation
- if ( supportMatrixFilter ) {
- elem.style.zoom = 1;
- }
-
- // replace "+=" in relative animations (-= is meaningless with transforms)
- end = end.split("+=").join(start);
-
- // parse both transform to generate interpolation list of same length
- $.extend( fx, interpolationList( start, end ) );
- start = fx.start;
- end = fx.end;
- }
-
- i = start.length;
-
- // interpolate functions of the list one by one
- while ( i-- ) {
- startVal = start[i];
- endVal = end[i];
- unit = +false;
-
- switch ( startVal[0] ) {
-
- case _translate:
- unit = "px";
- case _scale:
- unit || ( unit = "");
-
- transform = startVal[0] + "(" +
- Math.round( (startVal[1][0] + (endVal[1][0] - startVal[1][0]) * pos) * precision ) / precision + unit +","+
- Math.round( (startVal[1][1] + (endVal[1][1] - startVal[1][1]) * pos) * precision ) / precision + unit + ")"+
- transform;
- break;
-
- case _skew + "X":
- case _skew + "Y":
- case _rotate:
- transform = startVal[0] + "(" +
- Math.round( (startVal[1] + (endVal[1] - startVal[1]) * pos) * precision ) / precision +"rad)"+
- transform;
- break;
- }
- }
-
- fx.origin && ( transform = fx.origin + transform );
-
- propertyHook && propertyHook.set ?
- propertyHook.set( elem, transform, +true ):
- elem.style[supportProperty] = transform;
- };
-
- /*
- * Utility functions
- */
-
- // turns a transform string into its "matrix(A,B,C,D,X,Y)" form (as an array, though)
- function matrix( transform ) {
- transform = transform.split(")");
- var
- trim = $.trim
- , i = -1
- // last element of the array is an empty string, get rid of it
- , l = transform.length -1
- , split, prop, val
- , prev = supportFloat32Array ? new Float32Array(6) : []
- , curr = supportFloat32Array ? new Float32Array(6) : []
- , rslt = supportFloat32Array ? new Float32Array(6) : [1,0,0,1,0,0]
- ;
-
- prev[0] = prev[3] = rslt[0] = rslt[3] = 1;
- prev[1] = prev[2] = prev[4] = prev[5] = 0;
-
- // Loop through the transform properties, parse and multiply them
- while ( ++i < l ) {
- split = transform[i].split("(");
- prop = trim(split[0]);
- val = split[1];
- curr[0] = curr[3] = 1;
- curr[1] = curr[2] = curr[4] = curr[5] = 0;
-
- switch (prop) {
- case _translate+"X":
- curr[4] = parseInt(val, 10);
- break;
-
- case _translate+"Y":
- curr[5] = parseInt(val, 10);
- break;
-
- case _translate:
- val = val.split(",");
- curr[4] = parseInt(val[0], 10);
- curr[5] = parseInt(val[1] || 0, 10);
- break;
-
- case _rotate:
- val = toRadian(val);
- curr[0] = Math.cos(val);
- curr[1] = Math.sin(val);
- curr[2] = -Math.sin(val);
- curr[3] = Math.cos(val);
- break;
-
- case _scale+"X":
- curr[0] = +val;
- break;
-
- case _scale+"Y":
- curr[3] = val;
- break;
-
- case _scale:
- val = val.split(",");
- curr[0] = val[0];
- curr[3] = val.length>1 ? val[1] : val[0];
- break;
-
- case _skew+"X":
- curr[2] = Math.tan(toRadian(val));
- break;
-
- case _skew+"Y":
- curr[1] = Math.tan(toRadian(val));
- break;
-
- case _matrix:
- val = val.split(",");
- curr[0] = val[0];
- curr[1] = val[1];
- curr[2] = val[2];
- curr[3] = val[3];
- curr[4] = parseInt(val[4], 10);
- curr[5] = parseInt(val[5], 10);
- break;
- }
-
- // Matrix product (array in column-major order)
- rslt[0] = prev[0] * curr[0] + prev[2] * curr[1];
- rslt[1] = prev[1] * curr[0] + prev[3] * curr[1];
- rslt[2] = prev[0] * curr[2] + prev[2] * curr[3];
- rslt[3] = prev[1] * curr[2] + prev[3] * curr[3];
- rslt[4] = prev[0] * curr[4] + prev[2] * curr[5] + prev[4];
- rslt[5] = prev[1] * curr[4] + prev[3] * curr[5] + prev[5];
-
- prev = [rslt[0],rslt[1],rslt[2],rslt[3],rslt[4],rslt[5]];
- }
- return rslt;
- }
-
- // turns a matrix into its rotate, scale and skew components
- // algorithm from http://hg.mozilla.org/mozilla-central/file/7cb3e9795d04/layout/style/nsStyleAnimation.cpp
- function unmatrix(matrix) {
- var
- scaleX
- , scaleY
- , skew
- , A = matrix[0]
- , B = matrix[1]
- , C = matrix[2]
- , D = matrix[3]
- ;
-
- // Make sure matrix is not singular
- if ( A * D - B * C ) {
- // step (3)
- scaleX = Math.sqrt( A * A + B * B );
- A /= scaleX;
- B /= scaleX;
- // step (4)
- skew = A * C + B * D;
- C -= A * skew;
- D -= B * skew;
- // step (5)
- scaleY = Math.sqrt( C * C + D * D );
- C /= scaleY;
- D /= scaleY;
- skew /= scaleY;
- // step (6)
- if ( A * D < B * C ) {
- A = -A;
- B = -B;
- skew = -skew;
- scaleX = -scaleX;
- }
-
- // matrix is singular and cannot be interpolated
- } else {
- // In this case the elem shouldn't be rendered, hence scale == 0
- scaleX = scaleY = skew = 0;
- }
-
- // The recomposition order is very important
- // see http://hg.mozilla.org/mozilla-central/file/7cb3e9795d04/layout/style/nsStyleAnimation.cpp#l971
- return [
- [_translate, [+matrix[4], +matrix[5]]],
- [_rotate, Math.atan2(B, A)],
- [_skew + "X", Math.atan(skew)],
- [_scale, [scaleX, scaleY]]
- ];
- }
-
- // build the list of transform functions to interpolate
- // use the algorithm described at http://dev.w3.org/csswg/css3-2d-transforms/#animation
- function interpolationList( start, end ) {
- var list = {
- start: [],
- end: []
- },
- i = -1, l,
- currStart, currEnd, currType;
-
- // get rid of affine transform matrix
- ( start == "none" || isAffine( start ) ) && ( start = "" );
- ( end == "none" || isAffine( end ) ) && ( end = "" );
-
- // if end starts with the current computed style, this is a relative animation
- // store computed style as the origin, remove it from start and end
- if ( start && end && !end.indexOf("matrix") && toArray( start ).join() == toArray( end.split(")")[0] ).join() ) {
- list.origin = start;
- start = "";
- end = end.slice( end.indexOf(")") +1 );
- }
-
- if ( !start && !end ) { return; }
-
- // start or end are affine, or list of transform functions are identical
- // => functions will be interpolated individually
- if ( !start || !end || functionList(start) == functionList(end) ) {
-
- start && ( start = start.split(")") ) && ( l = start.length );
- end && ( end = end.split(")") ) && ( l = end.length );
-
- while ( ++i < l-1 ) {
- start[i] && ( currStart = start[i].split("(") );
- end[i] && ( currEnd = end[i].split("(") );
- currType = $.trim( ( currStart || currEnd )[0] );
-
- append( list.start, parseFunction( currType, currStart ? currStart[1] : 0 ) );
- append( list.end, parseFunction( currType, currEnd ? currEnd[1] : 0 ) );
- }
-
- // otherwise, functions will be composed to a single matrix
- } else {
- list.start = unmatrix(matrix(start));
- list.end = unmatrix(matrix(end))
- }
-
- return list;
- }
-
- function parseFunction( type, value ) {
- var
- // default value is 1 for scale, 0 otherwise
- defaultValue = +(!type.indexOf(_scale)),
- scaleX,
- // remove X/Y from scaleX/Y & translateX/Y, not from skew
- cat = type.replace( /e[XY]/, "e" );
-
- switch ( type ) {
- case _translate+"Y":
- case _scale+"Y":
-
- value = [
- defaultValue,
- value ?
- parseFloat( value ):
- defaultValue
- ];
- break;
-
- case _translate+"X":
- case _translate:
- case _scale+"X":
- scaleX = 1;
- case _scale:
-
- value = value ?
- ( value = value.split(",") ) && [
- parseFloat( value[0] ),
- parseFloat( value.length>1 ? value[1] : type == _scale ? scaleX || value[0] : defaultValue+"" )
- ]:
- [defaultValue, defaultValue];
- break;
-
- case _skew+"X":
- case _skew+"Y":
- case _rotate:
- value = value ? toRadian( value ) : 0;
- break;
-
- case _matrix:
- return unmatrix( value ? toArray(value) : [1,0,0,1,0,0] );
- break;
- }
-
- return [[ cat, value ]];
- }
-
- function isAffine( matrix ) {
- return rAffine.test(matrix);
- }
-
- function functionList( transform ) {
- return transform.replace(/(?:\([^)]*\))|\s/g, "");
- }
-
- function append( arr1, arr2, value ) {
- while ( value = arr2.shift() ) {
- arr1.push( value );
- }
- }
-
- // converts an angle string in any unit to a radian Float
- function toRadian(value) {
- return ~value.indexOf("deg") ?
- parseInt(value,10) * (Math.PI * 2 / 360):
- ~value.indexOf("grad") ?
- parseInt(value,10) * (Math.PI/200):
- parseFloat(value);
- }
-
- // Converts "matrix(A,B,C,D,X,Y)" to [A,B,C,D,X,Y]
- function toArray(matrix) {
- // remove the unit of X and Y for Firefox
- matrix = /([^,]*),([^,]*),([^,]*),([^,]*),([^,p]*)(?:px)?,([^)p]*)(?:px)?/.exec(matrix);
- return [matrix[1], matrix[2], matrix[3], matrix[4], matrix[5], matrix[6]];
- }
-
- $.transform = {
- centerOrigin: "margin"
- };
-
- })( jQuery, window, document, Math );
|