Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Implement transform parsing, make getBoxPath accept x/y offsets to ma…

…ke transform origins of all shapes line up, add Matrix class that roughly implements CSSMatrix interface, add transforms test file
  • Loading branch information...
commit 92b9c78f6b570a182b5adfaa02fd4c63711cbd5c 1 parent 4671d18
Jason Johnston authored
View
17 sources/BoxShadowOutsetRenderer.js
@@ -34,11 +34,11 @@ PIE.BoxShadowOutsetRenderer = PIE.RendererBase.newRenderer( {
h = bounds.h,
clipAdjust = PIE.ieVersion === 8 ? 1 : 0, //workaround for IE8 bug where VML leaks out top/left of clip region by 1px
corners = [ 'tl', 'tr', 'br', 'bl' ], corner,
- shadowInfo, shape, fill, ss, xOff, yOff, spread, blur, shrink, color, alpha, path,
+ shadowInfo, shape, fill, ss, spread, blur, shrink, color, alpha, path,
totalW, totalH, focusX, focusY, isBottom, isRight;
- function getShadowShape( index, corner, xOff, yOff, color, blur, path ) {
+ function getShadowShape( index, corner, color, blur, path ) {
var shape = me.getShape( 'shadow' + index + corner, 'fill', box, len - index ),
fill = shape.fill;
@@ -61,8 +61,6 @@ PIE.BoxShadowOutsetRenderer = PIE.RendererBase.newRenderer( {
// This needs to go last for some reason, to prevent rendering at incorrect size
ss = shape.style;
- ss.left = xOff;
- ss.top = yOff;
ss.width = w;
ss.height = h;
@@ -72,8 +70,6 @@ PIE.BoxShadowOutsetRenderer = PIE.RendererBase.newRenderer( {
while( i-- ) {
shadowInfo = shadowInfos[ i ];
- xOff = shadowInfo.xOffset.pixels( el );
- yOff = shadowInfo.yOffset.pixels( el );
spread = shadowInfo.spread.pixels( el ),
blur = shadowInfo.blur.pixels( el );
color = shadowInfo.color;
@@ -84,7 +80,8 @@ PIE.BoxShadowOutsetRenderer = PIE.RendererBase.newRenderer( {
// round the corners of the expanded shadow shape rather than squaring them off.
radii = PIE.BorderRadiusStyleInfo.ALL_ZERO;
}
- path = this.getBoxPath( { t: shrink, r: shrink, b: shrink, l: shrink }, 2, radii );
+ path = this.getBoxPath( { t: shrink, r: shrink, b: shrink, l: shrink }, 2, radii,
+ shadowInfo.xOffset.pixels( el ), shadowInfo.yOffset.pixels( el ) );
if( blur ) {
totalW = ( spread + blur ) * 2 + w;
@@ -101,7 +98,7 @@ PIE.BoxShadowOutsetRenderer = PIE.RendererBase.newRenderer( {
corner = corners[j];
isBottom = corner.charAt( 0 ) === 'b';
isRight = corner.charAt( 1 ) === 'r';
- shape = getShadowShape( i, corner, xOff, yOff, color, blur, path );
+ shape = getShadowShape( i, corner, color, blur, path );
fill = shape.fill;
fill['focusposition'] = ( isRight ? 1 - focusX : focusX ) + ',' +
( isBottom ? 1 - focusY : focusY );
@@ -116,13 +113,13 @@ PIE.BoxShadowOutsetRenderer = PIE.RendererBase.newRenderer( {
}
} else {
// TODO delete old quadrant shapes if resizing expands past the barrier
- shape = getShadowShape( i, '', xOff, yOff, color, blur, path );
+ shape = getShadowShape( i, '', color, blur, path );
fill = shape.fill;
fill['focusposition'] = focusX + ',' + focusY;
fill['focussize'] = ( 1 - focusX * 2 ) + ',' + ( 1 - focusY * 2 );
}
} else {
- shape = getShadowShape( i, '', xOff, yOff, color, blur, path );
+ shape = getShadowShape( i, '', color, blur, path );
alpha = color.alpha();
if( alpha < 1 ) {
// shape.style.filter = 'alpha(opacity=' + ( alpha * 100 ) + ')';
View
162 sources/Matrix.js
@@ -0,0 +1,162 @@
+PIE.Matrix = function( a, b, c, d, e, f ) {
+ var me = this;
+ me['a'] = a,
+ me['b'] = b,
+ me['c'] = c,
+ me['d'] = d,
+ me['e'] = e,
+ me['f'] = f;
+};
+
+PIE.Matrix.prototype = {
+
+ ////////// CSSMatrix interface methods: //////////
+
+ /**
+ * The setMatrixValue method replaces the existing matrix with one computed from parsing the passed
+ * string as though it had been assigned to the transform property in a CSS style rule.
+ * @param {String} cssString The string to parse.
+ * @throws {DOMException} SYNTAX_ERR Thrown when the provided string can not be parsed into a CSSMatrix.
+ */
+ setMatrixValue: function( cssString ) {
+ // TODO
+ // The problem here is how to handle relative/percent units in translate() functions. Those type
+ // of values mean nothing without the context of a particular element to resolve them to pixel
+ // numbers. They cannot be multiplied until that conversion has been performed.
+ },
+
+ /**
+ * The multiply method returns a new CSSMatrix which is the result of this matrix multiplied by the
+ * passed matrix, with the passed matrix to the right. This matrix is not modified.
+ * @param {CSSMatrix} secondMatrix The matrix to multiply.
+ * @return {CSSMatrix} The result matrix.
+ */
+ multiply: function( secondMatrix ) {
+ var matrix = this;
+ return new PIE.Matrix(
+ matrix['a'] * secondMatrix['a'] + matrix['c'] * secondMatrix['b'],
+ matrix['b'] * secondMatrix['a'] + matrix['d'] * secondMatrix['b'],
+ matrix['a'] * secondMatrix['c'] + matrix['c'] * secondMatrix['d'],
+ matrix['b'] * secondMatrix['c'] + matrix['d'] * secondMatrix['d'],
+ matrix['a'] * secondMatrix['e'] + matrix['c'] * secondMatrix['f'] + matrix['e'],
+ matrix['b'] * secondMatrix['e'] + matrix['d'] * secondMatrix['f'] + matrix['f']
+ )
+ },
+
+ /**
+ * The multiplyLeft method returns a new CSSMatrix which is the result of this matrix multiplied
+ * by the passed matrix, with the passed matrix to the left. This matrix is not modified.
+ * @param {CSSMatrix} secondMatrix The matrix to multiply.
+ * @return {CSSMatrix} The result matrix.
+ */
+ multiplyLeft: function( secondMatrix ) {
+ return secondMatrix.multiply( this );
+ },
+
+ /**
+ * The inverse method returns a new matrix which is the inverse of this matrix. This matrix is not modified.
+ * @return {CSSMatrix} The inverted matrix.
+ * @throws {DOMException} NOT_SUPPORTED_ERR Thrown when the CSSMatrix can not be inverted.
+ */
+ inverse: function() {
+ // IS THIS USEFUL?
+ },
+
+ /**
+ * The translate method returns a new matrix which is this matrix post multiplied by a translation
+ * matrix containing the passed values. This matrix is not modified.
+ * @param {Number} x The X component of the translation value.
+ * @param {Number} y The Y component of the translation value.
+ * @return {CSSMatrix} The result matrix.
+ */
+ translate: function( x, y ) {
+ return this.multiply( new PIE.Matrix( 1, 0, 0, 1, x, y ) );
+ },
+
+ /**
+ * The scale method returns a new matrix which is this matrix post multiplied by a scale matrix
+ * containing the passed values. If the y component is undefined, the x component value is used in its
+ * place. This matrix is not modified.
+ * @param {Number} x The X component of the scale value.
+ * @param {Number} y The (optional) Y component of the scale value.
+ * @return {CSSMatrix} The result matrix.
+ */
+ scale: function( x, y, undef ) {
+ return this.multiply( new PIE.Matrix( x, 0, 0, y === undef ? x : y, 0, 0 ) );
+ },
+
+ /**
+ * The rotate method returns a new matrix which is this matrix post multiplied by a rotation matrix.
+ * The rotation value is in degrees. This matrix is not modified.
+ * @param {Number} angle The angle of rotation (in degrees).
+ * @return {CSSMatrix} The result matrix.
+ */
+ rotate: function( angle ) {
+ var maths = Math,
+ radians = angle / 180 * maths.PI,
+ cosine = maths.cos( radians ),
+ sine = maths.sin( radians );
+ return this.multiply( new PIE.Matrix( cosine, -sine, sine, cosine, 0, 0 ) );
+ },
+
+ /**
+ * The skew method returns a new matrix which is this matrix post multiplied by a skew matrix. The
+ * rotation value is in degrees. This matrix is not modified.
+ * @param {Number} angleX The angle of skew along the X axis.
+ * @param {Number} angleY The angle of skew along the Y axis.
+ * @return {CSSMatrix} The result matrix.
+ */
+ skew: function( angleX, angleY ) {
+ var maths = Math,
+ pi = maths.PI;
+ return this.multiply( new PIE.Matrix( 1, maths.tan( angleX / 180 * pi ), maths.tan(angleY / 180 * pi ), 1, 0, 0 ) );
+ },
+
+ toString: function() {
+ var me = this;
+ return 'matrix(' + [me['a'], me['b'], me['c'], me['d'], me['e'], me['f']] + ')';
+ },
+
+
+
+ ////////// PIE private methods: //////////
+
+ /**
+ * Transform a given x/y point according to this matrix.
+ * @return {Object.<{x:number, y:number}>} The transformed x/y point
+ */
+ applyToPoint: function( x, y ) {
+ var matrix = this;
+ return {
+ x: matrix['a'] * x + matrix['c'] * y + matrix['e'],
+ y: matrix['b'] * x + matrix['d'] * y + matrix['f']
+ }
+ },
+
+ /**
+ * Transform a given bounding box according to this matrix and return the bounding box of the result
+ * @param {Object.<{x:number, y:number, w:number, h:number}>} bounds The bounding box to transform
+ * @return {Object.<{x:number, y:number, w:number, h:number}>} The resulting bounding box
+ */
+ getTransformedBounds: function( bounds ) {
+ var matrix = this,
+ tl = matrix.applyToPoint( 0, 0 ),
+ tr = matrix.applyToPoint( bounds.w, 0 ),
+ br = matrix.applyToPoint( bounds.w, bounds.h ),
+ bl = matrix.applyToPoint( 0, bounds.h ),
+ min = Math.min,
+ max = Math.max,
+ left = min( tl.x, tr.x, br.x, bl.x ),
+ top = min( tl.y, tr.y, br.y, bl.y );
+ return {
+ x: left,
+ y: top,
+ w: max( tl.x, tr.x, br.x, bl.x ) - left,
+ h: max( tl.y, tr.y, br.y, bl.y ) - top
+ }
+ }
+
+};
+
+// Alias to global CSSMatrix interface
+window.CSSMatrix = PIE.Matrix;
View
32 sources/RendererBase.js
@@ -220,10 +220,14 @@ PIE.RendererBase = {
* @param {number=} mult If specified, all coordinates will be multiplied by this number
* @param {Object=} radii If specified, this will be used for the corner radii instead of the properties
* from this renderer's borderRadiusInfo object.
+ * @param {number=} xOffset If specified, all points will be shifted by this amount on the x-axis
+ * @param {number=} yOffset If specified, all points will be shifted by this amount on the y-axis
* @return {string} the VML path
*/
- getBoxPath: function( shrink, mult, radii ) {
- mult = mult || 1;
+ getBoxPath: function( shrink, mult, radii, xOffset, yOffset ) {
+ mult || ( mult = 1 );
+ xOffset = ( xOffset || 0 ) * mult;
+ yOffset = ( yOffset || 0 ) * mult;
var r, str,
bounds = this.boundsInfo.getBounds(),
@@ -249,20 +253,20 @@ PIE.RendererBase = {
blX = r.x['bl'] * mult;
blY = r.y['bl'] * mult;
- str = 'm' + floor( shrinkL ) + ',' + floor( tlY ) +
- 'qy' + floor( tlX ) + ',' + floor( shrinkT ) +
- 'l' + ceil( w - trX ) + ',' + floor( shrinkT ) +
- 'qx' + ceil( w - shrinkR ) + ',' + floor( trY ) +
- 'l' + ceil( w - shrinkR ) + ',' + ceil( h - brY ) +
- 'qy' + ceil( w - brX ) + ',' + ceil( h - shrinkB ) +
- 'l' + floor( blX ) + ',' + ceil( h - shrinkB ) +
- 'qx' + floor( shrinkL ) + ',' + ceil( h - blY ) + ' x e';
+ str = 'm' + ( floor( shrinkL ) + xOffset ) + ',' + ( floor( tlY ) + yOffset ) +
+ 'qy' + ( floor( tlX ) + xOffset ) + ',' + ( floor( shrinkT ) + yOffset ) +
+ 'l' + ( ceil( w - trX ) + xOffset ) + ',' + ( floor( shrinkT ) + yOffset ) +
+ 'qx' + ( ceil( w - shrinkR ) + xOffset ) + ',' + ( floor( trY ) + yOffset ) +
+ 'l' + ( ceil( w - shrinkR ) + xOffset ) + ',' + ( ceil( h - brY ) + yOffset ) +
+ 'qy' + ( ceil( w - brX ) + xOffset ) + ',' + ( ceil( h - shrinkB ) + yOffset ) +
+ 'l' + ( floor( blX ) + xOffset ) + ',' + ( ceil( h - shrinkB ) + yOffset ) +
+ 'qx' + ( floor( shrinkL ) + xOffset ) + ',' + ( ceil( h - blY ) + yOffset ) + ' x e';
} else {
// simplified path for non-rounded box
- str = 'm' + floor( shrinkL ) + ',' + floor( shrinkT ) +
- 'l' + ceil( w - shrinkR ) + ',' + floor( shrinkT ) +
- 'l' + ceil( w - shrinkR ) + ',' + ceil( h - shrinkB ) +
- 'l' + floor( shrinkL ) + ',' + ceil( h - shrinkB ) +
+ str = 'm' + ( floor( shrinkL ) + xOffset ) + ',' + ( floor( shrinkT ) + yOffset ) +
+ 'l' + ( ceil( w - shrinkR ) + xOffset ) + ',' + ( floor( shrinkT ) + yOffset ) +
+ 'l' + ( ceil( w - shrinkR ) + xOffset ) + ',' + ( ceil( h - shrinkB ) + yOffset ) +
+ 'l' + ( floor( shrinkL ) + xOffset ) + ',' + ( ceil( h - shrinkB ) + yOffset ) +
'xe';
}
return str;
View
200 sources/TransformStyleInfo.js
@@ -7,119 +7,145 @@ PIE.TransformStyleInfo = PIE.StyleInfoBase.newStyleInfo( {
cssProperty: 'transform',
styleProperty: 'transform',
- transformFunctions: {
- 'matrix': 1,
- 'translate': 1,
- 'translateX': 1,
- 'translateY': 1,
- 'scale': 1,
- 'scaleX': 1,
- 'scaleY': 1,
- 'rotate': 1,
- 'skew': 1,
- 'skewX': 1,
- 'skewY': 1
- },
+ transformFunctions: (function() {
+ var Type = PIE.Tokenizer.Type,
+ NUMBER = Type.NUMBER,
+ ANGLE = Type.ANGLE,
+ LENGTH = Type.LENGTH,
+ PERCENT = Type.PERCENT,
+ LengthCls = PIE.Length,
+ AngleCls = PIE.Angle;
+ return {
+ // function_name: [ allowed_type, min_values, max_values, wrapper_class ]
+ 'matrix': [ NUMBER, 6, 6 ], //TODO moz allows (requires?) a length w/units for the final 2 args, should we?
+ 'translate': [ LENGTH | PERCENT, 1, 2, LengthCls ],
+ 'translateX': [ LENGTH | PERCENT, 1, 1, LengthCls ],
+ 'translateY': [ LENGTH | PERCENT, 1, 1, LengthCls ],
+ 'scale': [ NUMBER, 1, 2 ],
+ 'scaleX': [ NUMBER, 1, 1 ],
+ 'scaleY': [ NUMBER, 1, 1 ],
+ 'rotate': [ ANGLE, 1, 1, AngleCls ],
+ 'skew': [ ANGLE, 1, 2, AngleCls ],
+ 'skewX': [ ANGLE, 1, 1, AngleCls ],
+ 'skewY': [ ANGLE, 1, 1, AngleCls ]
+ };
+ })(),
+
+ xyFunctionRE: /^(.+)(X|Y)$/,
/**
* Returns object in the format:
- * {
+ * {
* origin: <PIE.BgPosition>,
* transforms: [
- * // all named transforms are pre-translated to 2d matrices, except for translate since it
- * // can take percentages and therefore depends on the dimensions of the element at render time.
- * { type: 'matrix', value: [ a, b, c, d, e, f ] },
- * { type: 'translate', value: [ <PIE.Length>, <PIE.Length> ] },
- * ...
+ * { type: 'matrix', value: [ <Number>, <Number>, <Number>, <Number>, <Number>, <Number> ] },
+ * { type: 'translate', value: [ <PIE.Length>, <PIE.Length> ] },
+ * { type: 'skew', value: [ <PIE.Angle>, <PIE.Angle> ] },
+ * { type: 'scale', value: [ <Number>, <Number> ] },
+ * { type: 'rotate', value: <PIE.Angle> }
* ]
- * }
+ * }
+ *
+ * Note that XY-specific functions will be pre-combined into their single counterparts:
+ * scaleX(n) -> scale(n)
+ * scaleY(n) -> scale(1, n)
+ * skewX(a) -> skew(a)
+ * skewY(a) -> skew(0, a)
+ * translateX(l) -> translate(l)
+ * translateY(l) -> translate(0, l)
*/
parseCss: function( css ) {
var tokenizer = new PIE.Tokenizer( css ),
Type = PIE.Tokenizer.Type,
- token, type, fnName,
+ transformFunctions = this.transformFunctions,
+ xyFunctionRE = this.xyFunctionRE,
+ YEP = true,
+ NOPE = false,
+ NULL = null,
+ token, tokenType, tokenValue, fnSignature, fnName, fnArgs, hangingArg, hangingComma, xyMatch,
transforms = [];
- /**
- * Multiply two matrices together to get a resultant matrix.
- */
- function multiplyMatrices( a, b ) {
- return [
- a[0] * b[0] + a[2] * b[1],
- a[1] * b[0] + a[3] * b[1],
- a[0] * b[2] + a[2] * b[3],
- a[1] * b[2] + a[3] * b[3],
- a[0] * b[4] + a[2] * b[5] + a[4],
- a[1] * b[4] + a[3] * b[5] + a[5]
- ];
- }
+ while( token = tokenizer.next() ) {
+ tokenType = token.type;
+ tokenValue = token.value;
- /**
- * For a given x/y point, find its resulting x/y location after the given matrix transformation.
- */
- function transformPoint( x, y, matrix ) {
- return {
- x: matrix[0] * x + matrix[2] * y + matrix[4],
- y: matrix[1] * x + matrix[3] * y + matrix[5]
+ // Start of function - set up for collection of args
+ if( !fnName && ( tokenType & Type.FUNCTION ) && tokenValue in transformFunctions ) {
+ fnName = tokenValue;
+ fnSignature = transformFunctions[tokenValue];
+ fnArgs = [];
}
- }
- /**
- * Finds the top/left of the bounding rectangle of the element after being transformed by the given
- * matrix. This is used to shift the element to offset the fact that IE's matrix filter always aligns
- * the transformed bounding rect to the original top/left.
- */
- function getTransformedTopLeft( bounds, matrix ) {
- var tl = transformPoint( 0, 0, matrix ),
- tr = transformPoint( bounds.w, 0, matrix ),
- br = transformPoint( bounds.w, bounds.h, matrix ),
- bl = transformPoint( 0, bounds.h, matrix );
- return {
- x: Math.min( tl.x, tr.x, br.x, bl.x ),
- y: Math.min( tl.y, tr.y, br.y, bl.y )
- }
- }
-
- function getFnArgs() {
- var tokens = tokenizer.until( function(token ) {
- return token.type & Type.CHARACTER && token.value === ')';
- }, true );
-
- // verify tokens follow the pattern of allowed-datatype alternating with comma
-
- }
-
- while( token = tokenizer.next() ) {
- type = token.type;
- fnName = token.value;
- if( type & Type.FUNCTION && fnName in this.transformFunctions ) {
- if ( fnName === 'translate' ) {
+ // End of function - validate number of args is within allowed range, push onto transforms list, and reset
+ else if ( ( tokenType & Type.CHARACTER ) && tokenValue === ')' && fnName && !hangingComma &&
+ fnArgs.length >= fnSignature[1] && fnArgs.length <= fnSignature[2] ) {
+ // Combine XY-specific functions into single functions, e.g. scaleY(n) -> scale(1, n)
+ if ( xyMatch = fnName.match( xyFunctionRE ) ) {
+ fnName = xyMatch[1];
+ // For fooX we use the arg untouched, for fooY we push an x value onto the front of the args
+ if ( xyMatch[2] === 'Y' ) {
+ fnArgs.unshift( fnName === 'scale' ? 1 : 0 );
+ }
}
- else if( fnName.indexOf( 'skew' ) === 0 ) {
-
+ // Fill in optional second argument
+ else if ( fnArgs.length < 2 ) {
+ fnArgs[1] = fnName === 'scale' ? fnArgs[0] : 0;
}
- else if( fnName.indexOf( 'translate' ) === 0 ) {
- }
- else if( fnName.indexOf( 'scale' ) === 0 ) {
+ transforms.push( { type: fnName, value: fnArgs } );
+ fnSignature = fnName = fnArgs = hangingArg = hangingComma = NOPE;
+ }
- }
+ // Commas - allowed in between function args
+ else if ( ( tokenType & Type.CHARACTER ) && tokenValue === ',' && hangingArg ) {
+ hangingArg = NOPE;
+ hangingComma = YEP;
+ }
- transforms.push({type: fnName, args: []});
+ // Function args - allowed at start of function or after comma
+ else if ( fnName && ( hangingComma || !hangingArg ) && ( tokenType & fnSignature[0] ) ) {
+ fnArgs.push( fnSignature[3] ? new fnSignature[3]( tokenValue ) : parseFloat( tokenValue, 10 ) );
+ hangingArg = YEP;
+ hangingComma = NOPE;
}
-// else if( type & ( ANGLE | NUMBER ) ) {
-// props[props.length - 1].value = value;
-// }
+
+ // Something not allowed - FAIL!
else {
- // something unrecognized; fail!
- return null;
+ return NULL;
}
}
- return transforms[0] ? {
+ return transforms[0] && !fnName ? {
origin: [],
transforms: transforms
- } : null;
- }
+ } : NULL;
+ },
+
+
+ /**
+ * Return the {@link PIE.Matrix}
+ */
+ getMatrix: PIE.StyleInfoBase.cacheWhenLocked( function( bounds ) {
+ var me = this,
+ el = me.targetElement,
+ props = me.getProps(),
+ origin = props.origin.coords( el, bounds.w, bounds.h ),
+ transforms = props.transforms,
+ matrix = new PIE.Matrix( 1, 0, 0, 1, origin.x, origin.y ),
+ i = 0, len = transforms.length,
+ type, value;
+
+ // apply each transform in order
+ for( ; i < len; i++ ) {
+ type = transforms[i].type;
+ value = transforms[i].value;
+ matrix = matrix[ type ].apply(
+ matrix,
+ type === 'translate' ? [ value[0].pixels( el ), value[1].pixels( el ) ] : value
+ );
+ }
+
+ return matrix;
+ } )
} );
View
113 tests/2dtransforms.html
@@ -0,0 +1,113 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>CSS3 2D Transforms</title>
+
+ <style type="text/css">
+ .cont {
+ border: 100px solid #EEF;
+ width: 200px;
+ height: 200px;
+ margin: 50px;
+ float: left;
+ }
+
+ .test {
+ width: 180px;
+ height: 180px;
+ border: 1px solid #CCC;
+ padding: 9px;
+ background: rgba(200, 255, 200, 0.2);
+ -moz-border-radius: 10px;
+ -webkit-border-radius: 10px;
+ border-radius: 10px;
+ }
+
+ .shadow .test {
+ -moz-box-shadow: 0 3px 5px #000;
+ -webkit-box-shadow: 0 3px 5px #000;
+ box-shadow: 0 3px 5px #000;
+ }
+
+ .gradient .test {
+ background: -webkit-gradient(linear, 0 0, 100% 0, from(#FCC), to(#CCF));
+ background: -moz-linear-gradient(left, #FCC, #CCF);
+ background: linear-gradient(left, #FCC, #CCF);
+ -pie-background: linear-gradient(left, #FCC, #CCF);
+ }
+
+ .matrix {
+ display: block;
+ color: #999;
+ font-size: 10px;
+ margin-top: 1em;
+ }
+
+ #controls {
+ position: fixed;
+ top: 1em;
+ right: 1em;
+ }
+ </style>
+
+ <script type="text/javascript">
+ window.onload = function() {
+ var divs = document.getElementsByTagName("div"),
+ i = 0, len = divs.length;
+ for( ; i < len; i++ ) {
+ if (divs[i].className.match(/\btest\b/)) {
+ divs[i].style.MozTransform = divs[i].innerHTML;
+ divs[i].style.webkitTransform = divs[i].innerHTML;
+ divs[i].style.transform = divs[i].innerHTML;
+
+ divs[i].innerHTML += '<span class="matrix">' + (
+ getComputedStyle(divs[i], null).getPropertyValue('transform') ||
+ getComputedStyle(divs[i], null).getPropertyValue('-webkit-transform') ||
+ getComputedStyle(divs[i], null).getPropertyValue('-moz-transform') ||
+ 'could not get matrix value'
+ ) + '</span>'
+ }
+ }
+ };
+
+ function toggleBodyClass(cls) {
+ var re = new RegExp("\\b" + cls + "\\b", 'g'),
+ body = document.body;
+ if (re.test(body.className)) {
+ body.className = body.className.replace(re, '');
+ } else {
+ body.className += ' ' + cls;
+ }
+ }
+ </script>
+</head>
+<body>
+
+ <p id="controls">
+ <button onclick="toggleBodyClass('shadow')">Toggle Shadow</button>
+ <button onclick="toggleBodyClass('gradient')">Toggle Gradient</button>
+ </p>
+
+ <div class="cont"><div class="test">rotate(45deg)</div></div>
+ <div class="cont"><div class="test">rotate(1rad)</div></div>
+
+ <div class="cont"><div class="test">scale(1.5)</div></div>
+ <div class="cont"><div class="test">scaleX(1.5)</div></div>
+ <div class="cont"><div class="test">scaleY(1.5)</div></div>
+
+ <div class="cont"><div class="test">skew(25deg,25deg)</div></div>
+ <div class="cont"><div class="test">skewX(25deg)</div></div>
+ <div class="cont"><div class="test">skewY(25deg)</div></div>
+
+ <div class="cont"><div class="test">translate(100px,100px)</div></div>
+ <div class="cont"><div class="test">translateX(100px)</div></div>
+ <div class="cont"><div class="test">translateY(100px)</div></div>
+ <div class="cont"><div class="test">translate(2em, 50%)</div></div>
+
+ <div class="cont"><div class="test">translate(50px) rotate(45deg)</div></div>
+ <div class="cont"><div class="test">rotate(45deg) translate(50px)</div></div>
+
+ <!--<div class="cont"><div class="test"></div></div>-->
+
+</body>
+</html>
Please sign in to comment.
Something went wrong with that request. Please try again.