Skip to content
This repository

Added Transformation Support #148

Open
wants to merge 4 commits into from

3 participants

Hannes Papenberg Jason Johnston Oleg I.
Hannes Papenberg

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?

Jason Johnston
Owner
lojjic commented June 19, 2011

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.

Hannes Papenberg

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.

Jason Johnston
Owner
lojjic commented June 19, 2011

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. :)

Jason Johnston
Owner
lojjic commented June 19, 2011

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

Hannes Papenberg

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. ;-)

Oleg I.
Boss32 commented June 23, 2011

PIE.JS with IE 7-8 not working.

Hannes Papenberg

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

Hannes Papenberg

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.

Jason Johnston
Owner
lojjic commented June 24, 2011

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! :)

Hannes Papenberg

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

Hannes Papenberg

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...

Hannes Papenberg

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... :-)

Jason Johnston
Owner
lojjic commented July 09, 2011

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
This page is out of date. Refresh to see the latest.
3  build.xml
@@ -25,6 +25,7 @@
25 25
             <fileset file="${src_dir}/BgPosition.js" />
26 26
             <fileset file="${src_dir}/Angle.js" />
27 27
             <fileset file="${src_dir}/Color.js" />
  28
+            <fileset file="${src_dir}/Matrix.js" />
28 29
             <fileset file="${src_dir}/Tokenizer.js" />
29 30
             <fileset file="${src_dir}/BoundsInfo.js" />
30 31
             <fileset file="${src_dir}/StyleInfoBase.js" />
@@ -33,6 +34,7 @@
33 34
             <fileset file="${src_dir}/BorderRadiusStyleInfo.js" />
34 35
             <fileset file="${src_dir}/BorderImageStyleInfo.js" />
35 36
             <fileset file="${src_dir}/BoxShadowStyleInfo.js" />
  37
+            <fileset file="${src_dir}/TransformStyleInfo.js" />
36 38
             <fileset file="${src_dir}/VisibilityStyleInfo.js" />
37 39
             <fileset file="${src_dir}/RendererBase.js" />
38 40
             <fileset file="${src_dir}/RootRenderer.js" />
@@ -40,6 +42,7 @@
40 42
             <fileset file="${src_dir}/BorderRenderer.js" />
41 43
             <fileset file="${src_dir}/BorderImageRenderer.js" />
42 44
             <fileset file="${src_dir}/BoxShadowOutsetRenderer.js" />
  45
+            <fileset file="${src_dir}/TransformRenderer.js" />
43 46
             <!--<fileset file="${src_dir}/BoxShadowInsetRenderer.js" />-->
44 47
             <fileset file="${src_dir}/ImgRenderer.js" />
45 48
             <fileset file="${src_dir}/IE9BackgroundRenderer.js" />
5  sources/Element.js
@@ -88,6 +88,7 @@ PIE.Element = (function() {
88 88
                             borderImageInfo: new PIE.BorderImageStyleInfo( el ),
89 89
                             borderRadiusInfo: new PIE.BorderRadiusStyleInfo( el ),
90 90
                             boxShadowInfo: new PIE.BoxShadowStyleInfo( el ),
  91
+                            transformInfo: new PIE.TransformStyleInfo( el ),
91 92
                             visibilityInfo: new PIE.VisibilityStyleInfo( el )
92 93
                         };
93 94
                         styleInfosArr = [
@@ -96,6 +97,7 @@ PIE.Element = (function() {
96 97
                             styleInfos.borderImageInfo,
97 98
                             styleInfos.borderRadiusInfo,
98 99
                             styleInfos.boxShadowInfo,
  100
+                            styleInfos.transformInfo,
99 101
                             styleInfos.visibilityInfo
100 102
                         ];
101 103
 
@@ -105,7 +107,8 @@ PIE.Element = (function() {
105 107
                             new PIE.BackgroundRenderer( el, boundsInfo, styleInfos, rootRenderer ),
106 108
                             //new PIE.BoxShadowInsetRenderer( el, boundsInfo, styleInfos, rootRenderer ),
107 109
                             new PIE.BorderRenderer( el, boundsInfo, styleInfos, rootRenderer ),
108  
-                            new PIE.BorderImageRenderer( el, boundsInfo, styleInfos, rootRenderer )
  110
+                            new PIE.BorderImageRenderer( el, boundsInfo, styleInfos, rootRenderer ),
  111
+                            new PIE.TransformRenderer( el, boundsInfo, styleInfos, rootRenderer )
109 112
                         ];
110 113
                         if( el.tagName === 'IMG' ) {
111 114
                             childRenderers.push( new PIE.ImgRenderer( el, boundsInfo, styleInfos, rootRenderer ) );
175  sources/Matrix.js
... ...
@@ -0,0 +1,175 @@
  1
+PIE.Matrix = function( a, b, c, d, e, f ) {
  2
+    var me = this;
  3
+    me['a'] = a,
  4
+    me['b'] = b,
  5
+    me['c'] = c,
  6
+    me['d'] = d,
  7
+    me['e'] = e,
  8
+    me['f'] = f;
  9
+};
  10
+
  11
+PIE.Matrix.prototype = {
  12
+
  13
+    ////////// CSSMatrix interface methods: //////////
  14
+
  15
+    /**
  16
+     * The setMatrixValue method replaces the existing matrix with one computed from parsing the passed
  17
+     * string as though it had been assigned to the transform property in a CSS style rule.
  18
+     * @param {String} cssString The string to parse.
  19
+     * @throws {DOMException} SYNTAX_ERR Thrown when the provided string can not be parsed into a CSSMatrix.
  20
+     */
  21
+    setMatrixValue: function( cssString ) {
  22
+        // TODO
  23
+        // The problem here is how to handle relative/percent units in translate() functions. Those type
  24
+        // of values mean nothing without the context of a particular element to resolve them to pixel
  25
+        // numbers. They cannot be multiplied until that conversion has been performed.
  26
+    },
  27
+
  28
+    /**
  29
+     * The multiply method returns a new CSSMatrix which is the result of this matrix multiplied by the
  30
+     * passed matrix, with the passed matrix to the right. This matrix is not modified.
  31
+     * @param {CSSMatrix} secondMatrix The matrix to multiply.
  32
+     * @return {CSSMatrix} The result matrix.
  33
+     */
  34
+    multiply: function( secondMatrix ) {
  35
+        var matrix = this;
  36
+        return new PIE.Matrix(
  37
+            matrix['a'] * secondMatrix['a'] + matrix['c'] * secondMatrix['b'],
  38
+            matrix['b'] * secondMatrix['a'] + matrix['d'] * secondMatrix['b'],
  39
+            matrix['a'] * secondMatrix['c'] + matrix['c'] * secondMatrix['d'],
  40
+            matrix['b'] * secondMatrix['c'] + matrix['d'] * secondMatrix['d'],
  41
+            matrix['a'] * secondMatrix['e'] + matrix['c'] * secondMatrix['f'] + matrix['e'],
  42
+            matrix['b'] * secondMatrix['e'] + matrix['d'] * secondMatrix['f'] + matrix['f']
  43
+        )
  44
+    },
  45
+
  46
+    /**
  47
+     * The multiplyLeft method returns a new CSSMatrix which is the result of this matrix multiplied
  48
+     * by the passed matrix, with the passed matrix to the left. This matrix is not modified.
  49
+     * @param {CSSMatrix} secondMatrix The matrix to multiply.
  50
+     * @return {CSSMatrix} The result matrix.
  51
+     */
  52
+    multiplyLeft: function( secondMatrix ) {
  53
+        return secondMatrix.multiply( this );
  54
+    },
  55
+
  56
+    /**
  57
+     * The inverse method returns a new matrix which is the inverse of this matrix. This matrix is not modified.
  58
+     * @return {CSSMatrix} The inverted matrix.
  59
+     * @throws {DOMException} NOT_SUPPORTED_ERR Thrown when the CSSMatrix can not be inverted.
  60
+     */
  61
+    inverse: function() {
  62
+    	var me = this, det, res = [], mult = 1,
  63
+    	m = [me['a'], me['b'], me['e'], me['c'], me['d'], me['f'], 0, 0, 1];
  64
+    	var det=m[0]*m[4]-m[3]*m[1];
  65
+    	if(det<0) {
  66
+    		mult = -1;
  67
+    		det=-1*det;
  68
+    	}
  69
+   		res[0]=-1*mult*m[4]/det;
  70
+   		res[1]=mult*m[1]/det;
  71
+   		res[2]=-1*mult*(m[1]*m[5]-m[2]*m[4])/det;
  72
+   		res[3]=mult*m[3]/det;
  73
+   		res[4]=-1*mult*m[0]/det;
  74
+   		res[5]=mult*(m[0]*m[5]-m[2]*m[3])/det;
  75
+    	
  76
+    	return new PIE.Matrix(res[0], res[1], res[3], res[4], res[2], res[5]);
  77
+    },
  78
+
  79
+    /**
  80
+     * The translate method returns a new matrix which is this matrix post multiplied by a translation
  81
+     * matrix containing the passed values. This matrix is not modified.
  82
+     * @param {Number} x The X component of the translation value.
  83
+     * @param {Number} y The Y component of the translation value.
  84
+     * @return {CSSMatrix} The result matrix.
  85
+     */
  86
+    translate: function( x, y ) {
  87
+        return this.multiply( new PIE.Matrix( 1, 0, 0, 1, x, y ) );
  88
+    },
  89
+
  90
+    /**
  91
+     * The scale method returns a new matrix which is this matrix post multiplied by a scale matrix
  92
+     * containing the passed values. If the y component is undefined, the x component value is used in its
  93
+     * place. This matrix is not modified.
  94
+     * @param {Number} x The X component of the scale value.
  95
+     * @param {Number} y The (optional) Y component of the scale value.
  96
+     * @return {CSSMatrix} The result matrix.
  97
+     */
  98
+    scale: function( x, y, undef ) {
  99
+        return this.multiply( new PIE.Matrix( x, 0, 0, y === undef ? x : y, 0, 0 ) );
  100
+    },
  101
+
  102
+    /**
  103
+     * The rotate method returns a new matrix which is this matrix post multiplied by a rotation matrix.
  104
+     * The rotation value is in degrees. This matrix is not modified.
  105
+     * @param {Number} angle The angle of rotation (in degrees).
  106
+     * @return {CSSMatrix} The result matrix.
  107
+     */
  108
+    rotate: function( angle ) {
  109
+        var rad = -angle / 180 * Math.PI,
  110
+            cosine = Math.cos( rad ),
  111
+            sine = Math.sin( rad );
  112
+        return this.multiply( new PIE.Matrix( cosine, -sine, sine, cosine, 0, 0 ) );
  113
+    },
  114
+
  115
+    /**
  116
+     * The skew method returns a new matrix which is this matrix post multiplied by a skew matrix. The
  117
+     * rotation value is in degrees. This matrix is not modified.
  118
+     * @param {Number} angleX The angle of skew along the X axis.
  119
+     * @param {Number} angleY The angle of skew along the Y axis.
  120
+     * @return {CSSMatrix} The result matrix.
  121
+     */
  122
+    skew: function( angleX, angleY ) {
  123
+        var maths = Math,
  124
+            pi = maths.PI;
  125
+        return this.multiply( new PIE.Matrix( 1, maths.tan( angleX / 180 * pi ), maths.tan(angleY / 180 * pi ), 1, 0, 0 ) );
  126
+    },
  127
+
  128
+    toString: function() {
  129
+        var me = this;
  130
+        return 'matrix(' + [me['a'], me['b'], me['c'], me['d'], me['e'], me['f']] + ')';
  131
+    },
  132
+
  133
+
  134
+
  135
+    ////////// PIE private methods: //////////
  136
+
  137
+    /**
  138
+     * Transform a given x/y point according to this matrix.
  139
+     * @return {Object.<{x:number, y:number}>} The transformed x/y point
  140
+     */
  141
+    applyToPoint: function( x, y ) {
  142
+        var matrix = this;
  143
+        return {
  144
+            x: matrix['a'] * x + matrix['c'] * y + matrix['e'],
  145
+            y: matrix['b'] * x + matrix['d'] * y + matrix['f']
  146
+        }
  147
+    },
  148
+
  149
+    /**
  150
+     * Transform a given bounding box according to this matrix and return the bounding box of the result
  151
+     * @param {Object.<{x:number, y:number, w:number, h:number}>} bounds The bounding box to transform
  152
+     * @return {Object.<{x:number, y:number, w:number, h:number}>} The resulting bounding box
  153
+     */
  154
+    getTransformedBounds: function( bounds ) {
  155
+        var matrix = this,
  156
+            tl = matrix.applyToPoint( 0, 0 ),
  157
+            tr = matrix.applyToPoint( bounds.w, 0 ),
  158
+            br = matrix.applyToPoint( bounds.w, bounds.h ),
  159
+            bl = matrix.applyToPoint( 0, bounds.h ),
  160
+            min = Math.min,
  161
+            max = Math.max,
  162
+            left = min( tl.x, tr.x, br.x, bl.x ),
  163
+            top = min( tl.y, tr.y, br.y, bl.y );
  164
+        return {
  165
+            x: left,
  166
+            y: top,
  167
+            w: max( tl.x, tr.x, br.x, bl.x ) - left,
  168
+            h: max( tl.y, tr.y, br.y, bl.y ) - top
  169
+        }
  170
+    }
  171
+
  172
+};
  173
+
  174
+// Alias to global CSSMatrix interface
  175
+window.CSSMatrix = PIE.Matrix;
132  sources/RendererBase.js
@@ -225,8 +225,9 @@ PIE.RendererBase = {
225 225
     getBoxPath: function( shrink, mult, radii ) {
226 226
         mult = mult || 1;
227 227
 
228  
-        var r, str,
229  
-            bounds = this.boundsInfo.getBounds(),
  228
+        var r, str, box,
  229
+        	m = this.styleInfos.transformInfo.getProps().m,
  230
+            bounds = m.getTransformedBounds(this.boundsInfo.getBounds()),
230 231
             w = bounds.w * mult,
231 232
             h = bounds.h * mult,
232 233
             radInfo = this.styleInfos.borderRadiusInfo,
@@ -234,37 +235,110 @@ PIE.RendererBase = {
234 235
             shrinkT = shrink ? shrink.t * mult : 0,
235 236
             shrinkR = shrink ? shrink.r * mult : 0,
236 237
             shrinkB = shrink ? shrink.b * mult : 0,
237  
-            shrinkL = shrink ? shrink.l * mult : 0,
238  
-            tlX, tlY, trX, trY, brX, brY, blX, blY;
239  
-
  238
+            shrinkL = shrink ? shrink.l * mult : 0;
  239
+        	
240 240
         if( radii || radInfo.isActive() ) {
241  
-            r = this.getRadiiPixels( radii || radInfo.getProps() );
242  
-
243  
-            tlX = r.x['tl'] * mult;
244  
-            tlY = r.y['tl'] * mult;
245  
-            trX = r.x['tr'] * mult;
246  
-            trY = r.y['tr'] * mult;
247  
-            brX = r.x['br'] * mult;
248  
-            brY = r.y['br'] * mult;
249  
-            blX = r.x['bl'] * mult;
250  
-            blY = r.y['bl'] * mult;
251  
-
252  
-            str = 'm' + floor( shrinkL ) + ',' + floor( tlY ) +
253  
-                'qy' + floor( tlX ) + ',' + floor( shrinkT ) +
254  
-                'l' + ceil( w - trX ) + ',' + floor( shrinkT ) +
255  
-                'qx' + ceil( w - shrinkR ) + ',' + floor( trY ) +
256  
-                'l' + ceil( w - shrinkR ) + ',' + ceil( h - brY ) +
257  
-                'qy' + ceil( w - brX ) + ',' + ceil( h - shrinkB ) +
258  
-                'l' + floor( blX ) + ',' + ceil( h - shrinkB ) +
259  
-                'qx' + floor( shrinkL ) + ',' + ceil( h - blY ) + ' x e';
  241
+        	var start, end, corner1, corner2, min = Math.min, max = Math.max,abs=Math.abs,s,e,rad,c,round = Math.round;
  242
+        	r = this.getRadiiPixels( radii || radInfo.getProps() );
  243
+        	
  244
+        	function getArcBox(st1,st2,en1,en2) {
  245
+        		var b = {}, rad,ort1 = {}, ort2 = {}, center = {};
  246
+        		
  247
+        		st1 = m.applyToPoint(st1.x, st1.y);
  248
+        		st2 = m.applyToPoint(st2.x, st2.y);
  249
+        		
  250
+        		en1 = m.applyToPoint(en1.x, en1.y);
  251
+        		en2 = m.applyToPoint(en2.x, en2.y);
  252
+        		
  253
+        		b.start = st1;
  254
+        		b.end = en1;
  255
+        		
  256
+        		ort1.x = st2.x - ((st1.x*st2.x + st1.y*st2.y)/(st1.x*st1.x+st1.y*st1.y))*st1.x;
  257
+        		ort1.y = st2.y - ((st1.x*st2.x + st1.y*st2.y)/(st1.x*st1.x+st1.y*st1.y))*st1.y;
  258
+        		
  259
+        		ort2.x = en2.x - ((en1.x*en2.x + en1.y*en2.y)/(en1.x*en1.x+en1.y*en1.y))*en1.x;
  260
+        		ort2.y = en2.y - ((en1.x*en2.x + en1.y*en2.y)/(en1.x*en1.x+en1.y*en1.y))*en1.y;
  261
+
  262
+        	    //copied from sylvester
  263
+            var PsubQ1 = st1.x - en1.x, PsubQ2 = st1.y - en1.y;
  264
+            var XdotQsubP = - ort1.x*PsubQ1 - ort1.y*PsubQ2;
  265
+            var YdotPsubQ = ort2.x*PsubQ1 + ort2.y*PsubQ2;
  266
+            var XdotX = ort1.x*ort1.x + ort1.y*ort1.y;
  267
+            var YdotY = ort2.x*ort2.x + ort2.y*ort2.y;
  268
+            var XdotY = ort1.x*ort2.x + ort1.y*ort2.y;
  269
+            var k = (XdotQsubP * YdotY / XdotX + XdotY * YdotPsubQ) / (YdotY - XdotY * XdotY);
  270
+            center = {x:st1.x + k*ort1.x, y:st1.y + k*ort1.y};
  271
+            	var val1, val2;
  272
+            	if((center.x < 0 && st1.x > 0) || (center.x > 0 && st1.x < 0)) {
  273
+            		val1 = Math.abs(center.x) + Math.abs(st1.x); 
  274
+            	} else {
  275
+            		val1 = Math.abs(center.x - st1.x);
  276
+            	}
  277
+            	
  278
+            	if((center.y < 0 && st1.y > 0) || (center.y > 0 && st1.y < 0)) {
  279
+            		val2 = Math.abs(center.y) + Math.abs(st1.y); 
  280
+            	} else {
  281
+            		val2 = Math.abs(center.y - st1.y);
  282
+            	}
  283
+            	
  284
+            	rad = Math.sqrt(val1*val1+val2*val2);
  285
+            	
  286
+            	b.l = round(center.x-rad);
  287
+            	b.t = round(center.y-rad);
  288
+            	b.r = round(center.x+rad);
  289
+            	b.b = round(center.y+rad);
  290
+        		
  291
+        		return b;
  292
+        	}
  293
+        	s1 = {x: -shrinkT * mult, y: (r.y['tl'] - shrinkL) * mult};
  294
+        	s2 = {x: -shrinkB * mult, y: bounds.h - (shrinkL + r.y['bl']) * mult};
  295
+        	e1 = {x: (r.x['tl'] - shrinkT) * mult, y: -shrinkL * mult};
  296
+        	e2 = {x: bounds.w - (shrinkT + r.x['tr']) * mult, y: -shrinkR * mult};
  297
+        	box = getArcBox(s1,s2,e1,e2);
  298
+        	str = 'm' + floor(box.start.x) + ',' + floor(box.start.y) + 
  299
+        		'wa' + box.l + ',' + box.t + ',' + box.r + ',' + box.b + ',' +
  300
+        		floor(box.start.x) + ',' + floor(box.start.y) + ',' + ceil(box.end.x) + ',' + ceil(box.end.y);
  301
+        	
  302
+        	s2 = e1;
  303
+        	s1 = e2;
  304
+        	e1 = {x: bounds.w - shrinkT * mult, y: (r.y['tr'] - shrinkR) * mult};
  305
+        	e2 = {x: bounds.w - shrinkB * mult, y: bounds.h - (shrinkR + r.y['br']) * mult};
  306
+        	box = getArcBox(s1,s2,e1,e2);
  307
+        	str += 'l' + floor(box.start.x) + ',' + floor(box.start.y) + 
  308
+        		'wa' + box.l + ',' + box.t + ',' + box.r + ',' + box.b + ',' +
  309
+        		floor(box.start.x) + ',' + floor(box.start.y) + ',' + floor(box.end.x) + ',' + floor(box.end.y);
  310
+
  311
+        	s2 = e1;
  312
+        	s1 = e2;
  313
+        	e1 = {x: bounds.w - (shrinkB + r.x['br']) * mult, y: bounds.h - shrinkR * mult};
  314
+        	e2 = {x: (r.x['bl'] - shrinkB) * mult, y: bounds.h - shrinkL * mult};
  315
+        	box = getArcBox(s1,s2,e1,e2);
  316
+        	str += 'l' + floor(box.start.x) + ',' + floor(box.start.y) + 
  317
+        		'wa' + box.l + ',' + box.t + ',' + box.r + ',' + box.b + ',' +
  318
+        		floor(box.start.x) + ',' + floor(box.start.y) + ',' + floor(box.end.x) + ',' + floor(box.end.y);
  319
+
  320
+        	s2 = e1;
  321
+        	s1 = e2;
  322
+        	e1 = {x: bounds.w - (shrinkB + r.x['br']) * mult, y: bounds.h - shrinkR * mult};
  323
+        	e2 = {x: -shrinkT * mult, y: (r.y['tl'] - shrinkL) * mult};
  324
+        	box = getArcBox(s1,s2,e1,e2);
  325
+        	str += 'l' + floor(box.start.x) + ',' + floor(box.start.y) + 
  326
+        		'wa' + box.l + ',' + box.t + ',' + box.r + ',' + box.b + ',' +
  327
+        		floor(box.start.x) + ',' + floor(box.start.y) + ',' + floor(box.end.x) + ',' + floor(box.end.y) + ' x e';
260 328
         } else {
261  
-            // simplified path for non-rounded box
262  
-            str = 'm' + floor( shrinkL ) + ',' + floor( shrinkT ) +
263  
-                  'l' + ceil( w - shrinkR ) + ',' + floor( shrinkT ) +
264  
-                  'l' + ceil( w - shrinkR ) + ',' + ceil( h - shrinkB ) +
265  
-                  'l' + floor( shrinkL ) + ',' + ceil( h - shrinkB ) +
  329
+            tl = m.applyToPoint(0 - shrinkT * mult, 0 - shrinkL * mult);
  330
+            tr = m.applyToPoint(bounds.w - shrinkT * mult, 0 - shrinkR * mult);
  331
+            br = m.applyToPoint(bounds.w - shrinkB * mult, bounds.h - shrinkR * mult);
  332
+            bl = m.applyToPoint(0 - shrinkB * mult, bounds.h - shrinkL * mult);
  333
+            
  334
+        	// simplified path for non-rounded box
  335
+            str = 'm' + floor( tl.x ) + ',' + floor( tl.y ) +
  336
+                  'l' + ceil( tr.x ) + ',' + floor( tr.y ) +
  337
+                  'l' + ceil( br.x ) + ',' + ceil( br.y ) +
  338
+                  'l' + floor( bl.x ) + ',' + ceil( bl.y ) +
266 339
                   'xe';
267 340
         }
  341
+
268 342
         return str;
269 343
     },
270 344
 
38  sources/TransformRenderer.js
... ...
@@ -0,0 +1,38 @@
  1
+/**
  2
+ * Renderer for transform.
  3
+ * @constructor
  4
+ * @param {Element} el The target element
  5
+ * @param {Object} styleInfos The StyleInfo objects
  6
+ * @param {PIE.RootRenderer} parent
  7
+ */
  8
+PIE.TransformRenderer = PIE.RendererBase.newRenderer( {
  9
+
  10
+	isActive: function() {
  11
+        var si = this.styleInfos;
  12
+        return si.transformInfo.isActive();
  13
+    },
  14
+	
  15
+    /**
  16
+	* Apply the transforms
  17
+	*/
  18
+	draw: function() {
  19
+		var el = this.targetElement,
  20
+			rotate = this.styleInfos.transformInfo.getProps().rotate, 
  21
+			m = this.styleInfos.transformInfo.getProps().m,
  22
+			oH = el.offsetHeight,
  23
+			oW = el.offsetWidth, origin;
  24
+
  25
+		this.boundsInfo.lock();
  26
+		var b = this.boundsInfo.getBounds();
  27
+		//alert(b.x+','+b.y+','+b.w+','+b.h);
  28
+		
  29
+		m = m.rotate(-2*rotate);
  30
+		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\')';
  31
+
  32
+		el._PIE = true;
  33
+	},
  34
+
  35
+	destroy: function() {
  36
+		PIE.RendererBase.destroy.call( this );
  37
+	}
  38
+} );
71  sources/TransformStyleInfo.js
... ...
@@ -0,0 +1,71 @@
  1
+/**
  2
+ * Handles parsing, caching, and detecting changes to transform CSS
  3
+ * @constructor
  4
+ * @param {Element} el the target element
  5
+ */
  6
+PIE.TransformStyleInfo = PIE.StyleInfoBase.newStyleInfo( {
  7
+
  8
+	cssProperty: 'transform',
  9
+	styleProperty: 'Transform',
  10
+
  11
+	singleIdents: {'rotate': 1, 'matrix': 1},
  12
+	scaleIdents: {'scale': 1, 'scaleX': 1, 'scaleY': 1},
  13
+	skewIdents: {'skew': 1, 'skewX': 1, 'skewY': 1},
  14
+	translateIdents: {'translate': 1, 'translateX': 1, 'translateY': 1},
  15
+
  16
+	deg2rad: Math.PI * 2 / 360,
  17
+    
  18
+	parseCss: function( css ) {
  19
+		var props = {m: {}, matrix:{}, rotate: 0,
  20
+				skew: {x:0,y:0}, translate: {x:0,y:0},
  21
+				scale: {x:0,y:0}}, prop = {}, el = this.targetElement,
  22
+			rect = el.getBoundingClientRect(),
  23
+        	Type = PIE.Tokenizer.Type,
  24
+			tokenizer, token, tokType, tokVal,
  25
+			m = new PIE.Matrix(1,0,0,1,0,0);		
  26
+
  27
+		m.translate((rect.right - rect.left)/2, (rect.bottom - rect.top) / 2);
  28
+		tokenizer = new PIE.Tokenizer( css );
  29
+		while( token = tokenizer.next()) {
  30
+			tokType = token.tokenType;
  31
+			tokVal = token.tokenValue;
  32
+			
  33
+			if(tokType == Type.FUNCTION) {
  34
+				prop.f = tokVal;
  35
+				prop.v = [];
  36
+			} else if(tokType & Type.ANGLE || tokType & Type.NUMBER || tokType & Type.PERCENT || tokType & Type.LENGTH) {
  37
+				prop.v.push(parseFloat(tokVal));
  38
+			} else if(tokType == Type.CHARACTER) {
  39
+				if(prop.f == 'rotate') {
  40
+					m = m.rotate(prop.v[0]);
  41
+					props.rotate += parseFloat(prop.v[0]);
  42
+				} else if(prop.f in this.scaleIdents) {
  43
+					m = m.scale(prop.v[0], prop.v[1] || prop.v[0]);
  44
+					props.scale.x *= prop.v[0];
  45
+					props.scale.y *= prop.v[1] || prop.v[0]
  46
+				} else if(prop.f in this.skewIdents) {
  47
+					m = m.skew(prop.v[0], prop.v[1] || prop.v[0]);
  48
+					props.skew.x += prop.v[0];
  49
+					props.skew.y += prop.v[1] || prop.v[0];
  50
+				} else if(prop.f in this.translateIdents) {
  51
+					m = m.translate(prop.v[0], prop.v[1] || prop.v[0]);
  52
+					props.translate.x += parseInt(prop.v[0]);
  53
+					props.translate.y += parseInt(prop.v[1] || prop.v[0]);
  54
+				} else if(prop.f == 'matrix') {
  55
+					m = m.multiply(new PIE.Matrix(
  56
+							prop.v[0], prop.v[1], 
  57
+							prop.v[2], prop.v[3], 
  58
+							prop.v[4], prop.v[5]));
  59
+					props.matrix = new PIE.Matrix(
  60
+							prop.v[0], prop.v[1], 
  61
+							prop.v[2], prop.v[3], 
  62
+							prop.v[4], prop.v[5]);
  63
+				}
  64
+				prop = {};
  65
+			}
  66
+		}
  67
+		m.translate(-(rect.right - rect.left)/2, -(rect.bottom - rect.top) / 2);
  68
+		props.m = m;
  69
+		return props;
  70
+	}	
  71
+} );
Commit_comment_tip

Tip: You can add notes to lines in a file. Hover to the left of a line to make a note

Something went wrong with that request. Please try again.