/* * based off of Louis-Rémi Babé rotate plugin (https://github.com/lrbabe/jquery.rotate.js) * * cssTransforms: jQuery cssHooks adding a cross browser, animatible transforms * * Author Bobby Schultz */ // (function($) { var div = document.createElement('div'), divStyle = div.style; // give props to those who dont have them $.cssProps.transform = divStyle.MozTransform === '' ? 'MozTransform' : (divStyle.msTransform === '' ? 'msTransform' : (divStyle.WebkitTransform === '' ? 'WebkitTransform' : (divStyle.OTransform === '' ? 'OTransform' : (divStyle.Transform === '' ? 'Transform' : false)))); $.cssProps.transformOrigin = divStyle.MozTransformOrigin === '' ? 'MozTransformOrigin' : (divStyle.msTransformOrigin === '' ? 'msTransformOrigin' : (divStyle.WebkitTransformOrigin === '' ? 'WebkitTransformOrigin' : (divStyle.OTransformOrigin === '' ? 'OTransformOrigin' : (divStyle.TransformOrigin === '' ? 'TransformOrigin' : false)))); // define supported or not $.support.transform = $.cssProps.transform !== false || divStyle.filter === '' ? true : false; $.support.transformOrigin = $.cssProps.transformOrigin !== false ? true : false; // if ONLY IE matrixes are supported (IE9 beta6 will use css3) $.support.matrixFilter = (divStyle.filter === '' && $.cssProps.transform === false) ? true : false; div = null; // stop if no form of transforms are supported if ($.support.transform === false) { return; } // opt out of letting jquery handle the units for custom setters/getters $.cssNumber.skew = $.cssNumber.skewX = $.cssNumber.skewY = $.cssNumber.scale = $.cssNumber.scaleX = $.cssNumber.scaleY = $.cssNumber.rotate = $.cssNumber.matrix = true; $.cssNumber.transformOrigin = $.cssNumber.transformOriginX = $.cssNumber.transformOriginY = true; if ($.support.matrixFilter) { $.cssNumber.transformOrigin = $.cssNumber.transformOriginX = $.cssNumber.transformOriginY = true; $.cssProps.transformOrigin = 'matrixFilter'; } $.cssHooks.transform = { set : function(elem, val, unit) { if ($.support.matrixFilter) { elem.style.filter = [ val ].join(''); } else { elem.style[$.cssProps.transform] = val + '%'; } }, get : function(elem, computed) { if ($.support.matrixFilter) { return elem.style.filter; } else { return elem.style[$.cssProps.transform]; } } }; $.cssHooks.transformOrigin = { set : function(elem, val, unit) { if (!$.support.matrixFilter) { val = (typeof val === 'string') ? val : val + (unit || '%'); elem.style[$.cssProps.transformOrigin] = val; } else { val = val.split(","); $.cssHooks.transformOriginX.set(elem, val[0]); if (val.length > 1) { $.cssHooks.transformOriginY.set(elem, val[1]); } } }, get : function(elem, computed) { if (!$.support.matrixFilter) { return elem.style[$.cssProps.transformOrigin]; } else { var originX = $.data(elem, 'transformOriginX'); var originY = $.data(elem, 'transformOriginY'); return originX && originY && originX === originY ? originX : '50%'; } } }; $.fx.step.transformOrigin = function(fx) { $.cssHooks.transformOrigin.set(fx.elem, fx.now, fx.unit); }; $.cssHooks.transformOriginX = { set : function(elem, val, unit) { if (!$.support.matrixFilter) { val = (typeof val === 'string') ? val : val + (unit || '%'); elem.style[$.cssProps.transformOrigin + 'X'] = val; } else { $.data(elem, 'transformOriginX', unit ? val + unit : val); setIEMatrix(elem); } }, get : function(elem, computed) { if (!$.support.matrixFilter) { return elem.style[$.cssProps.transformOrigin + 'X']; } else { var originX = $.data(elem, 'transformOriginX'); switch (originX) { case 'left': return '0%'; case 'center': return '50%'; case 'right': return '100%'; } return originX ? originX : '50%'; } } }; $.fx.step.transformOriginX = function(fx) { $.cssHooks.transformOriginX.set(fx.elem, fx.now, fx.unit); }; $.cssHooks.transformOriginY = { set : function(elem, val, unit) { if (!$.support.matrixFilter) { val = (typeof val === 'string') ? val : val + (unit || '%'); elem.style[$.cssProps.transformOrigin + 'Y'] = val; } else { $.data(elem, 'transformOriginY', unit ? val + unit : val); setIEMatrix(elem); } }, get : function(elem, computed) { if (!$.support.matrixFilter) { return elem.style[$.cssProps.transformOrigin + 'Y']; } else { var originY = $.data(elem, 'transformOriginY'); switch (originY) { case 'top': return '0%'; case 'center': return '50%'; case 'bottom': return '100%'; } return originY ? originY : '50%'; } } }; $.fx.step.transformOriginY = function(fx) { $.cssHooks.transformOriginY.set(fx.elem, fx.now, fx.unit); }; // create hooks for css transforms var rtn = function(v) { return v; }; var xy = [ [ 'X', 'Y' ], 'X', 'Y' ]; var abcdxy = [ [ 'A', 'B', 'C', 'D', 'X', 'Y' ], 'A', 'B', 'C', 'D', 'X', 'Y' ]; var props = [ { prop : 'rotate', matrix : [ function(v) { return Math.cos(v); }, function(v) { return -Math.sin(v); }, function(v) { return Math.sin(v); }, function(v) { return Math.cos(v); } ], unit : 'rad', subProps : [ '' ], fnc : toRadian }, { prop : 'scale', matrix : [ [ rtn, 0, 0, rtn ], [ rtn, 0, 0, 1 ], [ 1, 0, 0, rtn ] ], unit : '', subProps : xy, fnc : parseFloat, _default : 1 }, { prop : 'skew', matrix : [ [ 1, rtn, rtn, 1 ], [ 1, rtn, 0, 1 ], [ 1, 0, rtn, 1 ] ], unit : 'rad', subProps : xy, fnc : toRadian }, { prop : 'translate', matrix : [ [ 1, 0, 0, 1, rtn, rtn ], [ 1, 0, 0, 1, rtn, 0 ], [ 1, 0, 0, 1, 0, rtn ] ], standardUnit : 'px', subProps : xy, fnc : parseFloat }, { prop : 'matrix', matrix : [ [ rtn, rtn, rtn, rtn, rtn, rtn ], [ rtn, 0, 0, 1, 0, 0 ], [ 1, rtn, 0, 1, 0, 0 ], [ 1, 0, rtn, 1, 0, 0 ], [ 1, 0, 0, rtn, 0, 0 ], [ 1, 0, 0, 1, 0, rtn ] ], subProps : abcdxy, fnc : parseFloat } ]; jQuery.each(props, function(n, prop) { jQuery.each(prop.subProps, function(num, sub) { var _cssProp, _prop = prop; if ($.isArray(sub)) { // composite transform _cssProp = _prop.prop; var _sub = sub; $.cssHooks[_cssProp] = { set : function(elem, val, unit) { jQuery.each(_sub, function(num, x) { $.cssHooks[_cssProp + x].set(elem, val, unit); }); }, get : function(elem, computed) { var val = []; jQuery.each(_sub, function(num, x) { val.push($.cssHooks[_cssProp + x].get(elem, val)); }); // hack until jQuery supports animating multiple properties return val[0] || val[1]; } }; } else { // independent transfrom _cssProp = _prop.prop + sub; $.cssHooks[_cssProp] = { set : function(elem, val, unit) { $.data(elem, _cssProp, unit ? val + unit : val); setCSSTransform(elem, _prop.fnc(unit ? val + unit : val), _cssProp, _prop.unit || unit || _prop.standardUnit); }, get : function(elem, computed) { var p = $.data(elem, _cssProp); // console.log(_cssProp+'get:'+p); return p && p !== undefined ? p : _prop._default || 0; } }; } $.fx.step[_cssProp] = function(fx) { fx.unit = fx.unit === 'px' && $.cssNumber[_cssProp] ? _prop.standardUnit : fx.unit; var unit = ($.cssNumber[_cssProp] ? '' : fx.unit); $.cssHooks[_cssProp].set(fx.elem, fx.now, fx.unit); }; }); }); function setCSSTransform(elem, val, prop, unit) { if ($.support.matrixFilter) { return setIEMatrix(elem, val); } // parse css string var allProps = parseCSSTransform(elem); // check for value to be set var a = /[X|Y]/.exec(prop); a = (a === null ? '' : a[0] ? a[0] : a); prop = /.*[^XY]/.exec(prop)[0]; unit = unit === undefined ? '' : unit; // create return string var result = ''; var wasUpdated = false; var arr; if (allProps !== null) { for ( var item in allProps) { arr = allProps[item]; if (prop === item) { // update parsed data with new value if (prop !== 'matrix') { result += prop + '('; result += a === 'X' || a === '' ? val + unit : (arr[0] !== '' ? arr[0] : $.cssHooks[prop + 'X'].get(elem) + unit); result += a === 'Y' ? ', ' + val + unit : (arr[1] !== '' ? ', ' + arr[1] : (prop + 'Y' in $.cssHooks ? ', ' + $.cssHooks[prop + 'Y'].get(elem) + unit : '')); result += ') '; } else { result += val + ' '; } wasUpdated = true; } else { // dump parsed data to string result += item + '('; for ( var i = 0; i < arr.length; i++) { result += arr[i]; if (i < arr.length - 1 && arr[i + 1] !== '') { result += ', '; } else { break; } } result += ') '; } } } // if prop was not found to be updated, then dump data if (!wasUpdated) { result += prop + a + '(' + val + unit + ') '; } // set all transform properties elem.style[$.cssProps.transform] = result; } function parseCSSTransform(elem) { var props, prop, name, transform; // break up into single transform calls $(elem.style[$.cssProps.transform].replace(/(?:\,\s|\)|\()/g, "|").split(" ")) // read each data point for the transform call .each(function(i, item) { if (item !== '') { if (props === undefined) { props = {}; } prop = item.split("|"); name = prop.shift(); transform = /.*[^XY]/.exec(name)[0]; if (!props[transform]) { props[transform] = [ '', '', '', '', '', '' ]; } if (!/Y/.test(name)) { props[transform][0] = prop[0]; } if (!/X/.test(name)) { props[transform][1] = prop[1]; } if (prop.length == 6) { props[transform][2] = prop[2]; props[transform][3] = prop[3]; props[transform][4] = prop[4]; props[transform][5] = prop[5]; } } }); return props !== undefined ? props : null; } function ieOrigin(o, n, percent) { return percent * (o - n); } function toRadian(value) { if (typeof value === 'number') { return parseFloat(value); } if (value.indexOf("deg") != -1) { return parseInt(value, 10) * (Math.PI * 2 / 360); } else if (value.indexOf("grad") != -1) { return parseInt(value, 10) * (Math.PI / 200); } } $.rotate = { radToDeg : function radToDeg(rad) { return rad * 180 / Math.PI; } }; // special case for IE matrix function setIEMatrix(elem, mat) { var inverse, current, ang, org, originX, originY, runTransform = $.cssProps.transformOrigin === 'matrixFilter' ? true : false; current = [ $.cssHooks.scaleX.get(elem), toRadian($.cssHooks.skewY.get(elem)), toRadian($.cssHooks.skewX.get(elem)), $.cssHooks.scaleY.get(elem), $.cssHooks.translateX.get(elem), $.cssHooks.translateY.get(elem) ]; // start by multiply inverse of transform origin by matrix if (runTransform) { elem.style.filter = [ "progid:DXImageTransform.Microsoft.Matrix" + "(M11=1,M12=0,M21=0,M22=1,SizingMethod='auto expand')" ].join(''); var Wp = $.cssHooks.transformOriginX.get(elem); var Hp = $.cssHooks.transformOriginY.get(elem); Wp = Wp.indexOf('%') > 0 ? (/[\d]*/.exec(Wp) / 100) : Wp; Hp = Hp.indexOf('%') > 0 ? (/[\d]*/.exec(Hp) / 100) : Hp; var Wb = elem.offsetWidth; var Hb = elem.offsetHeight; } // multiply old matrix to new matrix if (typeof mat !== 'array' || mat.length !== 6) { mat = current; } else { mat = [ ((current[0] * mat[0]) + (current[1] * mat[2])), ((current[0] * mat[1]) + (current[1] * mat[3])), ((current[2] * mat[0]) + (current[3] * mat[2])), ((current[2] * mat[1]) + (current[3] * mat[3])), mat[4], mat[5] ]; } // multiply the transform and rotation matrixes ang = $.data(elem, 'rotate'); if (ang) { ang = toRadian(ang); var cos = Math.cos(ang); var sin = Math.sin(ang); ang = [ cos, -sin, sin, cos ]; mat = [ ((mat[0] * ang[0]) + (mat[1] * ang[2])), ((mat[0] * ang[1]) + (mat[1] * ang[3])), ((mat[2] * ang[0]) + (mat[3] * ang[2])), ((mat[2] * ang[1]) + (mat[3] * ang[3])), mat[4], mat[5] ]; } // apply the matrix as a IE filter elem.style.filter = [ "progid:DXImageTransform.Microsoft.Matrix(", "M11=" + mat[0] + ", ", "M12=" + mat[1] + ", ", "M21=" + mat[2] + ", ", "M22=" + mat[3] + ", ", "SizingMethod='auto expand'", ")" ].join(''); if (runTransform) { var Wo = elem.offsetWidth; var Ho = elem.offsetHeight; elem.style.position = 'relative'; elem.style.left = Wp * (Wb - Wo) + (parseInt(mat[4]) || 0); elem.style.top = Hp * (Hb - Ho) + (parseInt(mat[5]) || 0); } // $('#console').append('