Repository: louisremi/jquery.transform.js Branch: master Commit: e195b9a71185 Files: 9 Total size: 38.7 KB Directory structure: gitextract_1q9xzhft/ ├── README.md ├── bower.json ├── jquery.transform2d.js ├── jquery.transform3d.js ├── package.json └── test/ ├── example3d.html ├── transform2d.html ├── transform3d.html └── transformOrigin.html ================================================ FILE CONTENTS ================================================ ================================================ FILE: README.md ================================================ jquery.transform2d.js adds 2d transform capabilities to jQuery `css()` and `animate()` functions. [Demo](http://louisremi.github.com/jquery.transform.js/index.html) Usage: ====== Set transform with a string --------------------------- $(elem).css('transform', 'translate(50px, 30px) rotate(25deg) scale(2,.5) skewX(-35deg)'); $(elem).animate({transform: 'translateY(-100px) rotate(1rad) scaleX(2) skewY(42deg)'}); You can use the following list of transform functions: - `translateX(px)` - `translateY(px)` - combined: `translate(px, px)` *the second number is optional and defaults to 0* - `scaleX()` - `scaleY()` - combined: `scale(, )` *the second number is optional and defaults to the value of the first one* - `rotate()` *units for angles are *rad* (default), *deg* or *grad*.* - `skewX()` - `skewY()` - `matrix(, , , , , )`* *`matrix` gives you more control about the resulting transformation, using a [matrix construction set](http://www.useragentman.com/matrix/). When using it in animations however, it makes it impossible to predict how the current and target transformations are going to be interpolated; there is no way to tell whether elements are going to rotate clockwise or anti-clockwise for instance. Get transform ------------- returns a computed transform matrix. $(elem).css('transform') == 'matrix(0,1,-1,0,100,50)'; Relative animations ------------------- Relative animations are possible by prepending "+=" to the transform string. $(elem).css('transform', 'rotate(45deg)'); // using the following syntax, elem will always rotate 90deg anticlockwise $(elem).animate({transform: '+=rotate(-90deg)'}); 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 IE**, - transformOrigin is not accessible. Why such restrictions with 'translate'? --------------------------------------- Since translate is unavailable in IE<9, we have to emulate it using *top* and *left* properties of the element style. This can, of course, only work if the elements are absolutely positionned in a relatively positionned wrapper. Other plugins position the elements and wrap them transparently. I think that transparently messing with the DOM often introduces unpredictible behavior. Unpredictible behavior leads developpers to fear plugins. *Fear leads to anger. Anger leads to hate. Hate leads to suffering.* I prefer leaving this up to you. License ======= Dual licensed under GPL and MIT licenses. Copyright (c) 2010 [Louis-Rémi Babé](http://twitter.com/louis_remi). ================================================ FILE: bower.json ================================================ { "name":"jquery.transform.js", "version":"1.0.0", "main": [ "jquery.transform2d.js", "jquery.transform3d.js" ], "description":"", "license":"Dual licensed under GPL and MIT licenses.", "ignore":[ ], "dependencies":{ "jquery":"~1.7.2" }, "devDependencies":{} } ================================================ FILE: jquery.transform2d.js ================================================ /* * 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 ); ================================================ FILE: jquery.transform3d.js ================================================ /* * transform: A jQuery cssHooks adding 2D/3D transform capabilities to $.fn.css() and $.fn.animate() * * Requirements: * - jQuery 1.5.1+ * - jquery.transition.js for animations * - browser implementing W3C's CSS 2DTransforms for 2D tranform * - browser implementing W3C's CSS 3DTransforms for 3D tranform * * 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 ) { "use strict"; var div = document.createElement("div"), divStyle = div.style, prefixes = [ "O", "ms", "Webkit", "Moz" ], prefix, i = prefixes.length, properties = [ "transform", "transformOrigin", "transformStyle", "perspective", "perspectiveOrigin", "backfaceVisibility" ], property, j = prefixes.length; // Find the right prefix while ( i-- ) { if ( prefixes[i] + leadingUppercase( properties[0] ) in divStyle ) { prefix = prefixes[i]; continue; } } // This browser is not compatible with transforms if ( !prefix ) { return; } // Build cssHooks for each property while ( j-- ) { property = prefix + leadingUppercase( properties[j] ); if ( property in divStyle ) { // px isn't the default unit of this property $.cssNumber[ properties[j] ] = true; // populate cssProps $.cssProps[ properties[j] ] = property; // MozTranform requires a complete hook because "px" is required in translate property === "MozTransform" && ($.cssHooks[ properties[j] ] = { get: function( elem, computed ) { return (computed ? // remove "px" from the computed matrix $.css( elem, property ).split("px").join(""): elem.style[property] ); }, set: function( elem, value ) { // add "px" to matrices /matrix\([^)p]*\)/.test(value) && ( value = value.replace(/matrix((?:[^,]*,){4})([^,]*),([^)]*)/, "matrix$1$2px,$3px") ); elem.style[property] = value; } }); } } function leadingUppercase( word ) { return word.slice(0,1).toUpperCase() + word.slice(1); } })( jQuery, window, document ); ================================================ FILE: package.json ================================================ { "name": "transform", "version": "1.0", "title": "Add a transform property to $.fn.css() and $.fn.animate()", "author": { "name": "Louis-Rémi", "url": "http://twitter.com/louis_remi" }, "licenses": [ { "type": "MIT", "url": "http://louisremi.mit-license.org" } ], "dependencies": { "jquery": ">=1.4.3", "transition": "*" }, "description": "This plugins makes it possible to use and animate 2D Transforms in all browsers, as well as 3D Transforms in compatible browsers.", "keywords": [ "DOM", "animate", "transform", "2D", "3D", "CSS3", "cssHooks" ], "homepage": "https://github.com/louisremi/jquery.transform.js", "files": [ "jquery.transform2d.js", "jquery.transform3d.js" ] } ================================================ FILE: test/example3d.html ================================================

Something you cannot do with CSS only

================================================ FILE: test/transform2d.html ================================================

Visual tests of jquery.transform2d.js

$elem.css("transform", "rotate(45deg)");

$elem.css("transform", "skewX(30deg) skewY(10deg)");

$elem.css("transform", "scale(2, .5)");

$elem.css("transform", "translate(100px, 10px)");

$elem.css("transform", "matrix(.5, .433, -.5, 1.033, 50, -10)");

$elem.css("transform", "rotate(45deg) translate(38px)");

$elem.css("transform", "rotate(45deg) translateY(-68px) skewX(-30deg) scale(1.2) matrix(.5, .433, -.5, 1.033, 50, -10)");

$elem.animate({"transform": "rotate(45deg)"});

$elem.animate({"transform": "rotate(45deg)"}, 0);

$elem.animate({"transform": "rotate(45deg) translateY(-68px) skewX(-30deg) scale(1.2) matrix(.5, .433, -.5, 1.033, 50, -10)}");

$elem.css("transform", "rotate(45deg) translateY(-68px) skewX(-30deg) scale(1.2)").animate({"transform": "+=rotate(45deg) translateY(-68px) skewX(-30deg) scale(1.2)");

================================================ FILE: test/transform3d.html ================================================

Visual tests of jquery.transform3d.js

$elem.css("transform", "rotate(45deg)");

$elem.css("transform", "skewX(30deg) skewY(10deg)");

$elem.css("transform", "scale(2, .5)");

$elem.css("transform", "translate(100px, 10px)");

$elem.css("transform", "matrix(.5, .433, -.5, 1.033, 50, -10)");

$elem.css("transform", "rotate(45deg) translate(38px)");

$elem.css("transform", "rotate(45deg) translateY(-68px) skewX(-30deg) scale(1.2) matrix(.5, .433, -.5, 1.033, 50, -10)");

$elem.css("transform", "rotateZ(45deg)");

$elem.css("transform", "matrix3d(.5,.433,0,0,-.5,1,0,0,0,0,1,0,50,-10,0,1)");

$elem.animate({"transform": "rotate(45deg)"});

$elem.animate({"transform": "scale(.5,1)"});

$elem.animate({"transform": "rotate(45deg) translateY(-68px) skewX(-30deg) scale(1.2) matrix(.5, .433, -.5, 1.033, 50, -10)}");

================================================ FILE: test/transformOrigin.html ================================================

Visual tests of jquery.transformOrigin.js

$elem.css({transformOrigin: "100% 100%", transform: "rotate(110deg)"});