├── README.md ├── currentTransform.js ├── license.txt └── test └── test.html /README.md: -------------------------------------------------------------------------------- 1 | # Canvas-currentTransform 2 | 3 | HTML canvas element's CanvasRenderingContext2D has a property [currentTransform](http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-currenttransform) defined. This represents a transformation matrix as a SVGMatrix object. 4 | 5 | There seems to be no browser [implementation activity](https://code.google.com/p/chromium/issues/detail?id=277107) at [current](https://bugzilla.mozilla.org/show_bug.cgi?id=928150). 6 | 7 | Having this property natively supported would be of great help, as we can modify the transformation matrix by `setTransform`, `transform`, `translate`, ... methods, but we are not able to access the current transform matrix element values. 8 | 9 | This polyfill uses the experimental `mozCurrentTransform` or `webkitCurrentTransform` properties if available or implements a lightweight transformation matrix stack otherwise. 10 | -------------------------------------------------------------------------------- /currentTransform.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview Implement 'currentTransform' of CanvasRenderingContext2D prototype (polyfill) 3 | * @author Stefan Goessner (c) 2015 4 | */ 5 | 6 | /** 7 | * extend CanvasRenderingContext2D.prototype by current transformation matrix access. 8 | */ 9 | if (!("currentTransform" in CanvasRenderingContext2D.prototype)) { 10 | /** 11 | * define property 'currentTransform' 12 | */ 13 | if ("mozCurrentTransform" in CanvasRenderingContext2D.prototype) { 14 | Object.defineProperty(CanvasRenderingContext2D.prototype, "currentTransform", { 15 | get : function() { var m = this.mozCurrentTransform; return {a:m[0],b:m[1],c:m[2],d:m[3],e:m[4],f:m[5]}; }, 16 | set : function(x) { this.mozCurrentTransform = [x.a,x.b,x.c,x.d,x.e,x.f]; }, 17 | enumerable : true, 18 | configurable : false 19 | }); 20 | } 21 | else if ("webkitCurrentTransform" in CanvasRenderingContext2D.prototype) { 22 | Object.defineProperty(CanvasRenderingContext2D.prototype, "currentTransform", { 23 | get : function() { return this.webkitCurrentTransform; }, 24 | set : function(x) { this.webkitCurrentTransform = x; }, 25 | enumerable : true, 26 | configurable : false 27 | }); 28 | } 29 | else { // fully implement it ... hmm ... 'currentTransform', 'save()', 'restore()', 'transform()', 'setTransform()', 'resetTransform()' 30 | Object.defineProperty(CanvasRenderingContext2D.prototype, "currentTransform", { 31 | get : function() {return this._t2stack && this._t2stack[this._t2stack.length-1] || {a:1,b:0,c:0,d:1,e:0,f:0};}, 32 | set : function(x) { 33 | if (!this._t2stack) 34 | this._t2stack = [{}]; 35 | this._t2stack[this._t2stack.length-1] = {a:x.a,b:x.b,c:x.c,d:x.d,e:x.e,f:x.f}; 36 | }, 37 | enumerable : true, 38 | configurable : false 39 | }); 40 | CanvasRenderingContext2D.prototype.save = function() { 41 | var save = CanvasRenderingContext2D.prototype.save; 42 | return function() { 43 | if (!this._t2stack) 44 | this._t2stack = [{a:1,b:0,c:0,d:1,e:0,f:0}]; 45 | var t = this._t2stack[this._t2stack.length-1]; 46 | this._t2stack.push(t && {a:t.a,b:t.b,c:t.c,d:t.d,e:t.e,f:t.f}); 47 | save.call(this); 48 | } 49 | }(); 50 | CanvasRenderingContext2D.prototype.restore = function() { 51 | var restore = CanvasRenderingContext2D.prototype.restore; 52 | return function() { 53 | if (this._t2stack) this._t2stack.pop(); 54 | restore.call(this); 55 | } 56 | }(); 57 | CanvasRenderingContext2D.prototype.transform = function() { 58 | var transform = CanvasRenderingContext2D.prototype.transform; 59 | return function(a,b,c,d,e,f) { 60 | if (!this._t2stack) 61 | this._t2stack = [{a:1,b:0,c:0,d:1,e:0,f:0}]; 62 | var t = this._t2stack[this._t2stack.length-1], q; 63 | 64 | var na = t.a*a + t.c * b; 65 | var nb = t.b*a + t.d * b; 66 | 67 | var nc = t.a*c + t.c * d; 68 | var nd = t.b*c + t.d * d; 69 | 70 | var ne = t.e + t.a*e + t.c*f; 71 | var nf = t.f + t.b*e + t.d*f; 72 | 73 | t.a = na; 74 | t.b = nb; 75 | t.c = nc; 76 | t.d = nd; 77 | t.e = ne; 78 | t.f = nf; 79 | transform.call(this,a,b,c,d,e,f); 80 | } 81 | }(); 82 | CanvasRenderingContext2D.prototype.setTransform = function() { 83 | var setTransform = CanvasRenderingContext2D.prototype.setTransform; 84 | return function(a,b,c,d,e,f) { 85 | if (!this._t2stack) 86 | this._t2stack = [{}]; 87 | this._t2stack[this._t2stack.length-1] = {a:a,b:b,c:c,d:d,e:e,f:f}; 88 | setTransform.call(this,a,b,c,d,e,f); 89 | } 90 | }(); 91 | CanvasRenderingContext2D.prototype.resetTransform = function() { 92 | var resetTransform = CanvasRenderingContext2D.prototype.resetTransform; 93 | return function() { 94 | if (!this._t2stack) 95 | this._t2stack = [{}]; 96 | this._t2stack[this._t2stack.length-1] = {a:1,b:0,c:0,d:1,e:0,f:0}; 97 | resetTransform && resetTransform.call(this); 98 | } 99 | }(); 100 | CanvasRenderingContext2D.prototype.scale = function() { 101 | var scale = CanvasRenderingContext2D.prototype.scale; 102 | return function(sx,sy) { 103 | if (!this._t2stack) 104 | this._t2stack = [{a:1,b:0,c:0,d:1,e:0,f:0}]; 105 | var t = this._t2stack[this._t2stack.length-1]; 106 | sx = sx || 1; 107 | sy = sy || sx; 108 | t.a *= sx; t.c *= sy; 109 | t.b *= sx; t.d *= sy; 110 | scale.call(this,sx,sy); 111 | } 112 | }(); 113 | CanvasRenderingContext2D.prototype.rotate = function() { 114 | var rotate = CanvasRenderingContext2D.prototype.rotate; 115 | return function(w) { 116 | if (!this._t2stack) 117 | this._t2stack = [{a:1,b:0,c:0,d:1,e:0,f:0}]; 118 | var t = this._t2stack[this._t2stack.length-1]; 119 | 120 | var cw = Math.cos(-w); 121 | var sw = Math.sin(-w); 122 | 123 | var a = t.a*cw - t.c*sw; 124 | var b = t.b*cw - t.d*sw; 125 | var c = t.c*cw + t.a*sw; 126 | var d = t.d*cw + t.b*sw; 127 | 128 | t.a = a; 129 | t.b = b; 130 | t.c = c; 131 | t.d = d; 132 | 133 | return rotate.call(this,w); 134 | } 135 | }(); 136 | CanvasRenderingContext2D.prototype.translate = function() { 137 | var translate = CanvasRenderingContext2D.prototype.translate; 138 | return function(x,y) { 139 | if (!this._t2stack) 140 | this._t2stack = [{a:1,b:0,c:0,d:1,e:0,f:0}]; 141 | var t = this._t2stack[this._t2stack.length-1]; 142 | t.e += x*t.a + y*t.c; 143 | t.f += x*t.b + y*t.d; 144 | return translate.call(this,x,y); 145 | } 146 | }(); 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Stefan Goessner 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /test/test.html: -------------------------------------------------------------------------------- 1 | 2 | 118 | 119 | 120 |

please run this test under firefox

121 | 122 | --------------------------------------------------------------------------------