Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Added Transformation Support #148

Open
wants to merge 4 commits into from

3 participants

@Hackwar

Hi Jason,
in this commit I again tried to implement transformation support and succeeded pretty much. It now supports matrix, skew, scale and rotate. So far the translation is still missing, which seems to be due to an error in my translation algorithm. This is also the reason, why its not rotating around the correct transformation origin. This is still something that needs to be fixed to have this working. But I thought, commit early, commit often... :-)

The supported code would be:
transformation: skew(30px) rotate(20deg) scale(1.2);

Maybe you have an idea for the translation?

@lojjic
Owner

I've only looked at this briefly but it looks like a good start! Thanks for spending time on this. :)

Nothing jumps out at me right away with the translate issue, I'll try to take a closer look soon.

Have you thought about how to handle transforming the rendering elements for other PIE effects like border-radius and box-shadow? Currently the element foreground gets transformed but the box stays in place. Matrix transforms can be done on VML without quality loss (see the 'skew' element) but it's a bit difficult.

If you haven't already you might take a look at the 'transforms' branch in GitHub, I started on some of this same stuff a while ago, don't really remember how far I got or if it'll help at all.

@Hackwar

I thought maybe to simply just apply the filter to the VML element, too... :-) I haven't tried that though. I didn't know how to select the VML that belongs to the targetelement. Regarding the translation: I think the only way that is going to work is calculating all this by hand and moving the element around by positioning it absolute. The translate function of the MS filter does not seem to work right... I'll see what I can come up with.

@lojjic
Owner

You can get a reference to the css3-container element from within your TransformRenderer like so:

this.parent.getBox()

Go ahead and try applying the matrix filter to that element, but I think you'll find that it will first make all semi-transparent pixels fully opaque, which will look awful on e.g. the antialiasing of rounded corners and the opacity gradient of box-shadow. I could be wrong though. :)

@lojjic
Owner

Regarding the translate shifting, I've found this article by Brendan Kenny helpful: http://extremelysatisfactorytotalitarianism.com/blog/?p=922

@Hackwar

It looks like we might be able to apply the same matrix to the VML elements like to the filter. Lets see how we can implement all of this. ;-)

@Boss32

PIE.JS with IE 7-8 not working.

@Hackwar

I see why you didn't implement transform yet. Its a $%§# to do. Still working on this.

@Hackwar

Let me describe what I've tried so far and maybe someone can help me.
1. I've added your matrix class from your transform branch to my code.
2. I'm parsing the transform CSS and apply it to the matrix so that I got my skewed, scaled and rotated matrix.
3. Then I'm adding the filter to the original HTML element. All fine so far.
Now comes the difficult part:
4. I think the getBoxPath() function from RendererBase is the point to solve the issue of applying the transformation to the elements created by PIE. So I calculate the start and ending points of the straight lines in case of rounded corners and the radii of the corners.
5. With that information I'm calculating two control points for each corner to draw a bezier curve from the end of a line to the start of the next one.
6. Then I send all of this through my transformation matrix and then render the VML accordingly.

From my limited understanding, this should allow us to skew a box without a problem, since the control points would also be properly transformed and thus we would have the whole thing solved. Even with two different radii for the X and Y axis... But of course its not working... And I don't have the slightest clue why that is the case.

@lojjic
Owner

LOL no kidding, it's a pain for sure. :)

I've played around a bit in the past with the VML 'skew' element:
http://msdn.microsoft.com/en-us/library/bb229470%28v=vs.85%29.aspx

This seems to basically do what you're trying to do (transform each of the points of the box path and keep the curves angled correctly) but should be easier than messing with beziers. There's a catch though (and this is something you'd run into with your approach anyway): it transforms the path, but does not transform the fill. This means that background images and gradients will stay at their original size and position!

How do we fix this? I'm not sure I have a 100% answer, but here's what I've been thinking:
1) Scaling can be handled pretty easily just by changing the size of the gradient or bg image
2) Rotation can be handled via the 'rotation' CSS property: http://msdn.microsoft.com/en-us/library/bb263877%28v=VS.85%29.aspx -- I tested this and it does seem to work on VML elements and brings the image or gradient fill along with the rotation. But if you apply rotation this way then you'll have to remove that amount of rotation from your matrix.
3) Other transforms (skew) I'm not sure about. For gradients you might be able to adjust the angle and length of the gradient to match the skew. For images... I don't know, maybe in this case we fall back to applying the matrix as a filter (just to the VML element showing the image) and we document the side effects that brings?

(A lot of this requires decomposition of the matrix into its constituent transforms (scale, rotate, skew) and incidentally that's the reason that in my branch the TransformStyleInfo maintains the original individual transform instructions in addition to the combined matrix.)

Let me know if you come up with any better ideas. Fun fun fun! :)

@Hackwar

After finding some bugs, its more or less rendering most of this stuff correctly now in the sense that a box now has four corners and that they are somewhat roundish. Working with the bezier curves showed a different issue. A border-radius of 50px looked good, but a radius of 5px just didn't create a real curve...

I'm gonne keep on trying here. :P

@Hackwar

I just made another commit. This is really not a nice feature to implement... maybe I should look for something easier, like animations or something. ;-)

So, neither using the quarter circles nor the bezier curves was the solution for me so far... Right now I'm describing an elipse that is creating a correct quarter circle and it even seems as if it would behave together with skew correctly... :-) However the starting and end point of the curves are not correct yet and I'm still not accounting correctly for the displacement that IE is creating and the rotation origin. Still a lot of work, but I'm getting closer...

@Hackwar

This is the theory where I am right now and what I implemented so far, but it doesn't work... Anyway, here it goes:

I got the start and end points of each side of the box to the respective end of the quarter circle of the corners. I'm drawing a shape along that path, where I use the VML function clockwisearcto to draw a quarter circle to have those fancy rounded corners. That was working pretty well with the Hackwar@7ca7672 commit. If we were just talking about rotation, all of this wouldn't be such a big issue, but we also got the skew transformation, which changes the radius of the corners. This means, we have to recalculate the center of the corners circle and its radius to calculate the correct box for the clockwisearcto function. Fortunately, we got those two points where the lines of the two sides almost meet and between which we want to draw parts of a circle. Thos points are on the radius of our circle and the sides of our elements box describe tangents to that circle. By doing a Gram-Schmidt-process, we get orthogonal lines that run through those two endpoints and then through the center of our corner circle. So I calculate the meeting point of those two lines and by calculating the distance between one endpoint and the center, I got the radius. Then I can describe a box around the corner that should make VML draw the right rounded corner. but as often, its doing everything but the stuff that I was expecting... :-)

Maybe you got an idea? If this is solved, the next issue would be the actual size of the box and positioning all those elements correctly over each other, still the issue of the rotation origin and when you are scrolling the page, the VML element sticks to the viewport... :-)

@lojjic
Owner

Sounds like quite the rabbit hole!

Did you try the VML 'skew' element? I believe it handles the corner curves correctly. Something like the following seems to give a properly skewed/rotated/scaled shape (with the gradient/image fill caveats I mentioned earlier):

<v:shape coordsize="408,208" coordorigin="1,1" path=" m2,32 qy32,2 l376,2 qx406,32 l406,176 qy376,206 l32,206 qx2,176 x e" stroked="true" filled="false"
    style="POSITION: absolute; WIDTH: 204px; HEIGHT: 104px; TOP: 0px; LEFT: 0px">
  <v:fill type="solid" color="white" color2="white" colors="" opacity="1" src="" position="0,0" angle="0" method="any" focusposition="0,0" focussize="0,0" />
  <v:stroke color="#00c" weight="1.5" dashstyle="solid" linestyle="single" />
  <v:skew on="t" origin="0,0" matrix="1 .5 .2 1.5 0 0" />
</v:shape>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Jun 19, 2011
  1. @Hackwar
Commits on Jul 8, 2011
  1. @Hackwar

    Improvements...

    Hackwar authored
Commits on Jul 9, 2011
  1. @Hackwar
  2. @Hackwar

    Orthogonal approach...

    Hackwar authored
This page is out of date. Refresh to see the latest.
View
3  build.xml
@@ -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" />
View
5 sources/Element.js
@@ -88,6 +88,7 @@ PIE.Element = (function() {
borderImageInfo: new PIE.BorderImageStyleInfo( el ),
borderRadiusInfo: new PIE.BorderRadiusStyleInfo( el ),
boxShadowInfo: new PIE.BoxShadowStyleInfo( el ),
+ transformInfo: new PIE.TransformStyleInfo( el ),
visibilityInfo: new PIE.VisibilityStyleInfo( el )
};
styleInfosArr = [
@@ -96,6 +97,7 @@ PIE.Element = (function() {
styleInfos.borderImageInfo,
styleInfos.borderRadiusInfo,
styleInfos.boxShadowInfo,
+ styleInfos.transformInfo,
styleInfos.visibilityInfo
];
@@ -105,7 +107,8 @@ PIE.Element = (function() {
new PIE.BackgroundRenderer( el, boundsInfo, styleInfos, rootRenderer ),
//new PIE.BoxShadowInsetRenderer( el, boundsInfo, styleInfos, rootRenderer ),
new PIE.BorderRenderer( el, boundsInfo, styleInfos, rootRenderer ),
- new PIE.BorderImageRenderer( el, boundsInfo, styleInfos, rootRenderer )
+ new PIE.BorderImageRenderer( el, boundsInfo, styleInfos, rootRenderer ),
+ new PIE.TransformRenderer( el, boundsInfo, styleInfos, rootRenderer )
];
if( el.tagName === 'IMG' ) {
childRenderers.push( new PIE.ImgRenderer( el, boundsInfo, styleInfos, rootRenderer ) );
View
175 sources/Matrix.js
@@ -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;
View
132 sources/RendererBase.js
@@ -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,110 @@ 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,abs=Math.abs,s,e,rad,c,round = Math.round;
+ r = this.getRadiiPixels( radii || radInfo.getProps() );
+
+ function getArcBox(st1,st2,en1,en2) {
+ var b = {}, rad,ort1 = {}, ort2 = {}, center = {};
+
+ st1 = m.applyToPoint(st1.x, st1.y);
+ st2 = m.applyToPoint(st2.x, st2.y);
+
+ en1 = m.applyToPoint(en1.x, en1.y);
+ en2 = m.applyToPoint(en2.x, en2.y);
+
+ b.start = st1;
+ b.end = en1;
+
+ ort1.x = st2.x - ((st1.x*st2.x + st1.y*st2.y)/(st1.x*st1.x+st1.y*st1.y))*st1.x;
+ ort1.y = st2.y - ((st1.x*st2.x + st1.y*st2.y)/(st1.x*st1.x+st1.y*st1.y))*st1.y;
+
+ ort2.x = en2.x - ((en1.x*en2.x + en1.y*en2.y)/(en1.x*en1.x+en1.y*en1.y))*en1.x;
+ ort2.y = en2.y - ((en1.x*en2.x + en1.y*en2.y)/(en1.x*en1.x+en1.y*en1.y))*en1.y;
+
+ //copied from sylvester
+ var PsubQ1 = st1.x - en1.x, PsubQ2 = st1.y - en1.y;
+ var XdotQsubP = - ort1.x*PsubQ1 - ort1.y*PsubQ2;
+ var YdotPsubQ = ort2.x*PsubQ1 + ort2.y*PsubQ2;
+ var XdotX = ort1.x*ort1.x + ort1.y*ort1.y;
+ var YdotY = ort2.x*ort2.x + ort2.y*ort2.y;
+ var XdotY = ort1.x*ort2.x + ort1.y*ort2.y;
+ var k = (XdotQsubP * YdotY / XdotX + XdotY * YdotPsubQ) / (YdotY - XdotY * XdotY);
+ center = {x:st1.x + k*ort1.x, y:st1.y + k*ort1.y};
+ var val1, val2;
+ if((center.x < 0 && st1.x > 0) || (center.x > 0 && st1.x < 0)) {
+ val1 = Math.abs(center.x) + Math.abs(st1.x);
+ } else {
+ val1 = Math.abs(center.x - st1.x);
+ }
+
+ if((center.y < 0 && st1.y > 0) || (center.y > 0 && st1.y < 0)) {
+ val2 = Math.abs(center.y) + Math.abs(st1.y);
+ } else {
+ val2 = Math.abs(center.y - st1.y);
+ }
+
+ rad = Math.sqrt(val1*val1+val2*val2);
+
+ b.l = round(center.x-rad);
+ b.t = round(center.y-rad);
+ b.r = round(center.x+rad);
+ b.b = round(center.y+rad);
+
+ return b;
+ }
+ s1 = {x: -shrinkT * mult, y: (r.y['tl'] - shrinkL) * mult};
+ s2 = {x: -shrinkB * mult, y: bounds.h - (shrinkL + r.y['bl']) * mult};
+ e1 = {x: (r.x['tl'] - shrinkT) * mult, y: -shrinkL * mult};
+ e2 = {x: bounds.w - (shrinkT + r.x['tr']) * mult, y: -shrinkR * mult};
+ box = getArcBox(s1,s2,e1,e2);
+ str = 'm' + floor(box.start.x) + ',' + floor(box.start.y) +
+ 'wa' + box.l + ',' + box.t + ',' + box.r + ',' + box.b + ',' +
+ floor(box.start.x) + ',' + floor(box.start.y) + ',' + ceil(box.end.x) + ',' + ceil(box.end.y);
+
+ s2 = e1;
+ s1 = e2;
+ e1 = {x: bounds.w - shrinkT * mult, y: (r.y['tr'] - shrinkR) * mult};
+ e2 = {x: bounds.w - shrinkB * mult, y: bounds.h - (shrinkR + r.y['br']) * mult};
+ box = getArcBox(s1,s2,e1,e2);
+ str += 'l' + floor(box.start.x) + ',' + floor(box.start.y) +
+ 'wa' + box.l + ',' + box.t + ',' + box.r + ',' + box.b + ',' +
+ floor(box.start.x) + ',' + floor(box.start.y) + ',' + floor(box.end.x) + ',' + floor(box.end.y);
+
+ s2 = e1;
+ s1 = e2;
+ e1 = {x: bounds.w - (shrinkB + r.x['br']) * mult, y: bounds.h - shrinkR * mult};
+ e2 = {x: (r.x['bl'] - shrinkB) * mult, y: bounds.h - shrinkL * mult};
+ box = getArcBox(s1,s2,e1,e2);
+ str += 'l' + floor(box.start.x) + ',' + floor(box.start.y) +
+ 'wa' + box.l + ',' + box.t + ',' + box.r + ',' + box.b + ',' +
+ floor(box.start.x) + ',' + floor(box.start.y) + ',' + floor(box.end.x) + ',' + floor(box.end.y);
+
+ s2 = e1;
+ s1 = e2;
+ e1 = {x: bounds.w - (shrinkB + r.x['br']) * mult, y: bounds.h - shrinkR * mult};
+ e2 = {x: -shrinkT * mult, y: (r.y['tl'] - shrinkL) * mult};
+ box = getArcBox(s1,s2,e1,e2);
+ str += 'l' + floor(box.start.x) + ',' + floor(box.start.y) +
+ 'wa' + box.l + ',' + box.t + ',' + box.r + ',' + box.b + ',' +
+ floor(box.start.x) + ',' + floor(box.start.y) + ',' + floor(box.end.x) + ',' + floor(box.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;
},
View
38 sources/TransformRenderer.js
@@ -0,0 +1,38 @@
+/**
+ * Renderer for transform.
+ * @constructor
+ * @param {Element} el The target element
+ * @param {Object} styleInfos The StyleInfo objects
+ * @param {PIE.RootRenderer} parent
+ */
+PIE.TransformRenderer = PIE.RendererBase.newRenderer( {
+
+ isActive: function() {
+ var si = this.styleInfos;
+ return si.transformInfo.isActive();
+ },
+
+ /**
+ * Apply the transforms
+ */
+ draw: function() {
+ var el = this.targetElement,
+ rotate = this.styleInfos.transformInfo.getProps().rotate,
+ m = this.styleInfos.transformInfo.getProps().m,
+ oH = el.offsetHeight,
+ 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;
+ },
+
+ destroy: function() {
+ PIE.RendererBase.destroy.call( this );
+ }
+} );
View
71 sources/TransformStyleInfo.js
@@ -0,0 +1,71 @@
+/**
+ * Handles parsing, caching, and detecting changes to transform CSS
+ * @constructor
+ * @param {Element} el the target element
+ */
+PIE.TransformStyleInfo = PIE.StyleInfoBase.newStyleInfo( {
+
+ cssProperty: 'transform',
+ styleProperty: 'Transform',
+
+ singleIdents: {'rotate': 1, 'matrix': 1},
+ scaleIdents: {'scale': 1, 'scaleX': 1, 'scaleY': 1},
+ skewIdents: {'skew': 1, 'skewX': 1, 'skewY': 1},
+ translateIdents: {'translate': 1, 'translateX': 1, 'translateY': 1},
+
+ deg2rad: Math.PI * 2 / 360,
+
+ parseCss: function( css ) {
+ 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,
+ 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) {
+ prop.f = tokVal;
+ prop.v = [];
+ } else if(tokType & Type.ANGLE || tokType & Type.NUMBER || tokType & Type.PERCENT || tokType & Type.LENGTH) {
+ prop.v.push(parseFloat(tokVal));
+ } else if(tokType == Type.CHARACTER) {
+ 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]);
+ }
+ prop = {};
+ }
+ }
+ m.translate(-(rect.right - rect.left)/2, -(rect.bottom - rect.top) / 2);
+ props.m = m;
+ return props;
+ }
+} );
Something went wrong with that request. Please try again.