Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Improvements...

  • Loading branch information...
commit 9e766346f1927a4c1a5e1181be40cab0940f7667 1 parent 275c2ab
Hannes Papenberg authored
3  build.xml
View
@@ -25,6 +25,7 @@
<fileset file="${src_dir}/BgPosition.js" />
<fileset file="${src_dir}/Angle.js" />
<fileset file="${src_dir}/Color.js" />
+ <fileset file="${src_dir}/Matrix.js" />
<fileset file="${src_dir}/Tokenizer.js" />
<fileset file="${src_dir}/BoundsInfo.js" />
<fileset file="${src_dir}/StyleInfoBase.js" />
@@ -33,6 +34,7 @@
<fileset file="${src_dir}/BorderRadiusStyleInfo.js" />
<fileset file="${src_dir}/BorderImageStyleInfo.js" />
<fileset file="${src_dir}/BoxShadowStyleInfo.js" />
+ <fileset file="${src_dir}/TransformStyleInfo.js" />
<fileset file="${src_dir}/VisibilityStyleInfo.js" />
<fileset file="${src_dir}/RendererBase.js" />
<fileset file="${src_dir}/RootRenderer.js" />
@@ -40,6 +42,7 @@
<fileset file="${src_dir}/BorderRenderer.js" />
<fileset file="${src_dir}/BorderImageRenderer.js" />
<fileset file="${src_dir}/BoxShadowOutsetRenderer.js" />
+ <fileset file="${src_dir}/TransformRenderer.js" />
<!--<fileset file="${src_dir}/BoxShadowInsetRenderer.js" />-->
<fileset file="${src_dir}/ImgRenderer.js" />
<fileset file="${src_dir}/IE9BackgroundRenderer.js" />
175 sources/Matrix.js
View
@@ -0,0 +1,175 @@
+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() {
+ var me = this, det, res = [], mult = 1,
+ m = [me['a'], me['b'], me['e'], me['c'], me['d'], me['f'], 0, 0, 1];
+ var det=m[0]*m[4]-m[3]*m[1];
+ if(det<0) {
+ mult = -1;
+ det=-1*det;
+ }
+ res[0]=-1*mult*m[4]/det;
+ res[1]=mult*m[1]/det;
+ res[2]=-1*mult*(m[1]*m[5]-m[2]*m[4])/det;
+ res[3]=mult*m[3]/det;
+ res[4]=-1*mult*m[0]/det;
+ res[5]=mult*(m[0]*m[5]-m[2]*m[3])/det;
+
+ return new PIE.Matrix(res[0], res[1], res[3], res[4], res[2], res[5]);
+ },
+
+ /**
+ * 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 rad = -angle / 180 * Math.PI,
+ cosine = Math.cos( rad ),
+ sine = Math.sin( rad );
+ 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;
93 sources/RendererBase.js
View
@@ -225,8 +225,9 @@ PIE.RendererBase = {
getBoxPath: function( shrink, mult, radii ) {
mult = mult || 1;
- var r, str,
- bounds = this.boundsInfo.getBounds(),
+ var r, str, box,
+ m = this.styleInfos.transformInfo.getProps().m,
+ bounds = m.getTransformedBounds(this.boundsInfo.getBounds()),
w = bounds.w * mult,
h = bounds.h * mult,
radInfo = this.styleInfos.borderRadiusInfo,
@@ -234,37 +235,71 @@ PIE.RendererBase = {
shrinkT = shrink ? shrink.t * mult : 0,
shrinkR = shrink ? shrink.r * mult : 0,
shrinkB = shrink ? shrink.b * mult : 0,
- shrinkL = shrink ? shrink.l * mult : 0,
- tlX, tlY, trX, trY, brX, brY, blX, blY;
-
+ shrinkL = shrink ? shrink.l * mult : 0;
+
if( radii || radInfo.isActive() ) {
- r = this.getRadiiPixels( radii || radInfo.getProps() );
-
- tlX = r.x['tl'] * mult;
- tlY = r.y['tl'] * mult;
- trX = r.x['tr'] * mult;
- trY = r.y['tr'] * mult;
- brX = r.x['br'] * mult;
- brY = r.y['br'] * mult;
- 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';
+ var start, end, corner1, corner2, min = Math.min, max = Math.max;
+ r = this.getRadiiPixels( radii || radInfo.getProps() );
+
+ function getArcBox(cor1, cor2) {
+ var b = {};
+
+ b.tl = m.applyToPoint(min(cor1.x, cor2.x), min(cor1.y, cor2.y));
+ b.tr = m.applyToPoint(max(cor1.x, cor2.x), min(cor1.y, cor2.y));
+ b.bl = m.applyToPoint(max(cor1.x, cor2.x), max(cor1.y, cor2.y));
+ b.br = m.applyToPoint(min(cor1.x, cor2.x), max(cor1.y, cor2.y));
+
+ return {t: floor(min(b.tl.y, b.tr.y, b.bl.y, b.br.y)),
+ l: floor(min(b.tl.x, b.tr.x, b.bl.x, b.br.x)),
+ b: ceil(max(b.tl.y, b.tr.y, b.bl.y, b.br.y)),
+ r: ceil(max(b.tl.x, b.tr.x, b.bl.x, b.br.x))};
+ }
+
+ start = m.applyToPoint(0 - shrinkT * mult, (r.y['tl'] - shrinkL) * mult);
+ end = m.applyToPoint((r.x['tl'] - shrinkT) * mult, -shrinkL * mult);
+ box = getArcBox({x: 0 - shrinkT * mult, y: -shrinkL * mult},
+ {x:(r.x['tl'] - shrinkT) * 2 * mult, y: (r.y['tl'] - shrinkL) * 2 * mult});
+ str = 'm' + floor(start.x) + ',' + floor(start.y) +
+ 'wa' + box.l + ',' + box.t + ',' + box.r + ',' + box.b + ',' +
+ floor(start.x) + ',' + floor(start.y) + ',' + ceil(end.x) + ',' + floor(end.y);
+
+ start = m.applyToPoint(bounds.w - (shrinkT + r.x['tr']) * mult, -shrinkR * mult);
+ end = m.applyToPoint(bounds.w - shrinkT * mult, (r.y['tr'] - shrinkR) * mult);
+ box = getArcBox({x: bounds.w - (shrinkT + r.x['tr']) * 2 * mult, y: -shrinkR * mult},
+ {x: bounds.w - shrinkT * mult, y: (r.y['tr'] - shrinkR) * 2 * mult});
+ str += 'l' + ceil(start.x) + ',' + ceil(start.y) +
+ 'wa' + box.l + ',' + box.t + ',' + box.r + ',' + box.b + ',' +
+ ceil(start.x) + ',' + ceil(start.y) + ',' + ceil(end.x) + ',' + ceil(end.y);
+
+ start = m.applyToPoint(bounds.w - shrinkB * mult, bounds.h - (shrinkR + r.y['br']) * mult);
+ end = m.applyToPoint(bounds.w - (shrinkB + r.x['br']) * mult, bounds.h - shrinkR * mult);
+ box = getArcBox({x: bounds.w - shrinkB * mult, y: bounds.h - (shrinkR + r.y['br']) * 2 * mult},
+ {x: bounds.w - (shrinkB + r.x['br']) * 2 * mult, y: bounds.h - shrinkR * mult});
+ str += 'l' + ceil(start.x) + ',' + ceil(start.y) +
+ 'wa' + box.l + ',' + box.t + ',' + box.r + ',' + box.b + ',' +
+ ceil(start.x) + ',' + ceil(start.y) + ',' + ceil(end.x) + ',' + ceil(end.y);
+
+ start = m.applyToPoint((r.x['bl'] - shrinkB) * mult, bounds.h - shrinkL * mult);
+ end = m.applyToPoint(-shrinkB * mult, bounds.h - (shrinkL + r.y['bl']) * mult);
+ box = getArcBox({x: (r.x['bl'] - shrinkB) * 2 * mult, y: bounds.h - shrinkL * mult},
+ {x: -shrinkB * mult, y: bounds.h - (shrinkL + r.y['bl']) * 2 * mult});
+ str += 'l' + ceil(start.x) + ',' + ceil(start.y) +
+ 'wa' + box.l + ',' + box.t + ',' + box.r + ',' + box.b + ',' +
+ ceil(start.x) + ',' + ceil(start.y) + ',' + ceil(end.x) + ',' + ceil(end.y) + ' 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 ) +
+ tl = m.applyToPoint(0 - shrinkT * mult, 0 - shrinkL * mult);
+ tr = m.applyToPoint(bounds.w - shrinkT * mult, 0 - shrinkR * mult);
+ br = m.applyToPoint(bounds.w - shrinkB * mult, bounds.h - shrinkR * mult);
+ bl = m.applyToPoint(0 - shrinkB * mult, bounds.h - shrinkL * mult);
+
+ // simplified path for non-rounded box
+ str = 'm' + floor( tl.x ) + ',' + floor( tl.y ) +
+ 'l' + ceil( tr.x ) + ',' + floor( tr.y ) +
+ 'l' + ceil( br.x ) + ',' + ceil( br.y ) +
+ 'l' + floor( bl.x ) + ',' + ceil( bl.y ) +
'xe';
}
+
return str;
},
20 sources/TransformRenderer.js
View
@@ -17,17 +17,17 @@ PIE.TransformRenderer = PIE.RendererBase.newRenderer( {
*/
draw: function() {
var el = this.targetElement,
- props = this.styleInfos.transformInfo.getProps(),
- matrix = props.matrix,
+ rotate = this.styleInfos.transformInfo.getProps().rotate,
+ m = this.styleInfos.transformInfo.getProps().m,
oH = el.offsetHeight,
- oW = el.offsetWidth, m, origin;
- /**
- * TODO: Correct for transformation origin
- m = [1,0,-oW/2,0,1,-oH/2,0,0,1];
- m = this.styleInfos.transformInfo.matrixMult(m, matrix);
- */
- m = matrix;
- el.style.filter = 'progid:DXImageTransform.Microsoft.Matrix( M11=' + m[0] + ', M12=' + m[1] + ', M21=' + m[3] + ', M22=' + m[4] + ', Dx=' + m[2] + ', Dy=' + m[5] + ', sizingMethod=\'auto expand\')';
+ oW = el.offsetWidth, origin;
+
+ this.boundsInfo.lock();
+ var b = this.boundsInfo.getBounds();
+ //alert(b.x+','+b.y+','+b.w+','+b.h);
+
+ m = m.rotate(-2*rotate);
+ el.style.filter = 'progid:DXImageTransform.Microsoft.Matrix( M11=' + m['a'] + ', M12=' + m['b'] + ', M21=' + m['c'] + ', M22=' + m['d'] + ', Dx=' + m['e'] + ', Dy=' + m['f'] + ', sizingMethod=\'auto expand\')';
el._PIE = true;
},
124 sources/TransformStyleInfo.js
View
@@ -16,102 +16,56 @@ PIE.TransformStyleInfo = PIE.StyleInfoBase.newStyleInfo( {
deg2rad: Math.PI * 2 / 360,
parseCss: function( css ) {
- var props = {}, property = {},
+ var props = {m: {}, matrix:{}, rotate: 0,
+ skew: {x:0,y:0}, translate: {x:0,y:0},
+ scale: {x:0,y:0}}, prop = {}, el = this.targetElement,
+ rect = el.getBoundingClientRect(),
Type = PIE.Tokenizer.Type,
- tokenizer, token, tokType, tokVal;
- props.matrix = [1,0,0,0,1,0,0,0,1];
-
+ tokenizer, token, tokType, tokVal,
+ m = new PIE.Matrix(1,0,0,1,0,0);
+
+ m.translate((rect.right - rect.left)/2, (rect.bottom - rect.top) / 2);
tokenizer = new PIE.Tokenizer( css );
while( token = tokenizer.next()) {
tokType = token.tokenType;
tokVal = token.tokenValue;
if(tokType == Type.FUNCTION) {
- property.func = tokVal;
- property.values = [];
+ prop.f = tokVal;
+ prop.v = [];
} else if(tokType & Type.ANGLE || tokType & Type.NUMBER || tokType & Type.PERCENT || tokType & Type.LENGTH) {
- property.values.push(tokVal);
+ prop.v.push(parseFloat(tokVal));
} else if(tokType == Type.CHARACTER) {
- if(property.func == 'rotate') {
- props.matrix = this.rotateCalc(props, property);
- } else if(property.func in this.scaleIdents) {
- props.matrix = this.scaleCalc(props, property);
- } else if(property.func in this.skewIdents) {
- props.matrix = this.skewCalc(props, property);
- } else if(property.func in this.translateIdents) {
- props.matrix = this.translateCalc(props, property);
- } else if(property.func == 'matrix') {
- props.matrix = this.matrixCalc(props, property);
+ if(prop.f == 'rotate') {
+ m = m.rotate(prop.v[0]);
+ props.rotate += parseFloat(prop.v[0]);
+ } else if(prop.f in this.scaleIdents) {
+ m = m.scale(prop.v[0], prop.v[1] || prop.v[0]);
+ props.scale.x *= prop.v[0];
+ props.scale.y *= prop.v[1] || prop.v[0]
+ } else if(prop.f in this.skewIdents) {
+ m = m.skew(prop.v[0], prop.v[1] || prop.v[0]);
+ props.skew.x += prop.v[0];
+ props.skew.y += prop.v[1] || prop.v[0];
+ } else if(prop.f in this.translateIdents) {
+ m = m.translate(prop.v[0], prop.v[1] || prop.v[0]);
+ props.translate.x += parseInt(prop.v[0]);
+ props.translate.y += parseInt(prop.v[1] || prop.v[0]);
+ } else if(prop.f == 'matrix') {
+ m = m.multiply(new PIE.Matrix(
+ prop.v[0], prop.v[1],
+ prop.v[2], prop.v[3],
+ prop.v[4], prop.v[5]));
+ props.matrix = new PIE.Matrix(
+ prop.v[0], prop.v[1],
+ prop.v[2], prop.v[3],
+ prop.v[4], prop.v[5]);
}
- property = {};
+ prop = {};
}
}
+ m.translate(-(rect.right - rect.left)/2, -(rect.bottom - rect.top) / 2);
+ props.m = m;
return props;
- },
-
- rotateCalc: function(props, property) {
- var m = [], rad, sin, cos;
- rad = parseFloat(property.values[0]) * this.deg2rad;
- cos = Math.cos(rad);
- sin = Math.sin(rad);
- return this.matrixMult(props.matrix,[cos, -sin, 0, sin, cos, 0, 0, 0, 1]);
- },
-
- scaleCalc: function(props, property) {
- var m = [1,0,0,0,1,0,0,0,1];
- if(property.func == 'scale') {
- m[0] = parseFloat(property.values[0]);
- if(property.values.length > 1) m[4] = parseFloat(property.values[1]);
- else m[4] = parseFloat(property.values[0]);
- }
- else if(property.func == 'scaleX') m[0] = parseFloat(property.values[0]);
- else if(property.func == 'scaleY') m[4] = parseFloat(property.values[0]);
- return this.matrixMult(props.matrix, m);
- },
-
- skewCalc: function(props, property) {
- var m = [1,0,0,0,1,0,0,0,1],
- val = Math.tan(parseFloat(property.values[0]) * this.deg2rad);
- if(property.func == 'skew') {
- m[1] = val;
- if(property.values.length > 1) m[3] = Math.tan(parseFloat(property.values[1]) * this.deg2rad);
- }
- else if(property.func == 'skewX') m[1] = val;
- else if(property.func == 'skewY') m[3] = val;
- return this.matrixMult(props.matrix, m);
- },
-
- translateCalc: function(props, property) {
- var m = [1,0,0,0,1,0,0,0,1];
- m[2] = parseInt(property.values[0]);
- if(property.func == 'translateY') {
- m[2] = 0;
- m[5] = parseInt(property.values[0]);
- }
- if(property.values.length > 1) m[5] = parseInt(property.values[1]);
-
- return this.matrixMult(props.matrix, m);
- },
-
- matrixCalc: function(props, property) {
- var m = [property.values[0], property.values[2], property.values[4],
- property.values[1], property.values[3], property.values[5],
- 0, 0, 1 ];
- return this.matrixMult(props.matrix, m);
- },
-
- matrixMult: function(matrix1, matrix2) {
- var result = [], i = 0, j, k, x;
- for(; i < 3; i++) {
- for(j = 0; j < 3; j++) {
- x = 0;
- for(k = 0; k < 3; k++) {
- x += matrix1[(i*3)+k] * matrix2[(k*3) + j];
- }
- result[(i*3)+j] = x;
- }
- }
- return result;
- }
-
+ }
} );
Please sign in to comment.
Something went wrong with that request. Please try again.