├── .gitignore ├── LICENSE.md ├── README.md ├── index.js ├── package.json └── test └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | bower_components 2 | node_modules 3 | *.log 4 | .DS_Store 5 | bundle.js 6 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2014 Jam3 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 20 | OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # css-transform-to-mat4 2 | 3 | [![experimental](http://badges.github.io/stability-badges/dist/experimental.svg)](http://github.com/badges/stability-badges) 4 | 5 | Will take a string which is a css transform value (2d or 3d) and return a mat4 or 3d transformation matrix from the string. 6 | 7 | ## Usage 8 | 9 | [![NPM](https://nodei.co/npm/css-transform-to-mat4.png)](https://www.npmjs.com/package/css-transform-to-mat4) 10 | 11 | ### Example: 12 | ```javascript 13 | var getMatrix = require('css-transform-to-mat4'); 14 | 15 | var matrix3DA = getMatrix('translate(40px, 20px)'); // works with 2d transforms 16 | var matrix3DB = getMatrix('perspective(1000px) translate3d(40px, 20px, -1000px)'); // and 3d 17 | ``` 18 | 19 | To see more examples check out: 20 | [http://requirebin.com/?gist=adf102de923cf6f1dc82](http://requirebin.com/?gist=adf102de923cf6f1dc82) 21 | 22 | ## License 23 | 24 | MIT, see [LICENSE.md](http://github.com/Jam3/css-transform-to-mat4/blob/master/LICENSE.md) for details. 25 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var mat4RotateX = require('gl-mat4/rotateX'); 2 | var mat4RotateY = require('gl-mat4/rotateY'); 3 | var mat4RotateZ = require('gl-mat4/rotateZ'); 4 | var mat4Rotate = require('gl-mat4/rotate'); 5 | var mat4Scale = require('gl-mat4/scale'); 6 | var mat4Translate = require('gl-mat4/translate'); 7 | var mat4Multiply = require('gl-mat4/multiply'); 8 | 9 | // Spec was used for reference http://www.w3.org/TR/2009/WD-css3-3d-transforms-20090320/ 10 | module.exports = function cssTransformToMatrix(value) { 11 | 12 | var functions = value.match(/[A-z3]+\([^\)]+/g) || []; 13 | var outMatrix = createMatrix(); 14 | var matrices = []; 15 | 16 | functions.forEach( function(func) { 17 | 18 | var split = func.split('('); 19 | var name = split[ 0 ] 20 | var value = split[ 1 ]; 21 | var matrix; 22 | 23 | switch(name) { 24 | 25 | /////// 2D FUNCTIONS /////// 26 | case 'matrix': 27 | // matrix( 28 | // a,b,c, 29 | // d,e,f 30 | // ) is equivalent to 31 | 32 | // matrix3d( 33 | // a, b, 0, 0, 34 | // c, d, 0, 0, 35 | // 0, 0, 1, 0, 36 | // e, f, 0, 1) 37 | 38 | 39 | value = value.split(',').map(parseFloat); 40 | matrix = [ 41 | value[ 0 ], value[ 1 ], 0, 0, 42 | value[ 2 ], value[ 3 ], 0, 0, 43 | 0, 0, 1, 0, 44 | value[ 4 ], value[ 5 ], 0, 1 45 | ]; 46 | break; 47 | 48 | case 'matrix3d': 49 | matrix = value.split(',').map(parseFloat); 50 | break; 51 | 52 | case 'translate': 53 | case 'translate3d': 54 | matrix = createMatrix(); 55 | value = value.split(',').map(parseFloat); 56 | 57 | if(value.length === 2) { 58 | value.push(0); 59 | } 60 | 61 | mat4Translate(matrix, matrix, value); 62 | break; 63 | 64 | case 'translateX': 65 | matrix = createMatrix(); 66 | value = [ parseFloat(value), 0, 0 ]; 67 | mat4Translate(matrix, matrix, value); 68 | break; 69 | 70 | case 'translateY': 71 | matrix = createMatrix(); 72 | value = [ 0, parseFloat(value), 0 ]; 73 | mat4Translate(matrix, matrix, value); 74 | break; 75 | 76 | case 'translateZ': 77 | matrix = createMatrix(); 78 | value = [ 0, 0, parseFloat(value) ]; 79 | mat4Translate(matrix, matrix, value); 80 | break; 81 | 82 | case 'rotate': 83 | case 'rotateZ': 84 | matrix = createMatrix(); 85 | value = getRadian(value); 86 | mat4RotateZ(matrix, matrix, value); 87 | break; 88 | 89 | case 'scale': 90 | case 'scale3d': 91 | matrix = createMatrix(); 92 | value = value.split(',').map(parseFloat); 93 | 94 | if(value.length === 2) { 95 | value.push(1); 96 | } 97 | 98 | mat4Scale(matrix, matrix, value); 99 | break; 100 | 101 | case 'scaleX': 102 | matrix = createMatrix(); 103 | mat4Scale(matrix, matrix, [parseFloat(value), 1, 1]); 104 | break; 105 | 106 | case 'scaleY': 107 | matrix = createMatrix(); 108 | mat4Scale(matrix, matrix, [1, parseFloat(value), 1]); 109 | break; 110 | 111 | case 'scaleZ': 112 | matrix = createMatrix(); 113 | mat4Scale(matrix, matrix, [1, 1, parseFloat(value)]); 114 | break; 115 | 116 | case 'rotateX': 117 | matrix = createMatrix(); 118 | value = getRadian(value); 119 | mat4RotateX(matrix, matrix, value); 120 | break; 121 | 122 | case 'rotateY': 123 | matrix = createMatrix(); 124 | value = getRadian(value); 125 | mat4RotateY(matrix, matrix, value); 126 | break; 127 | 128 | case 'rotate3d': 129 | matrix = createMatrix(); 130 | value = value.split(','); 131 | mat4Rotate(matrix, matrix, getRadian(value[3]), value.slice(0, 3).map(parseFloat)); 132 | break; 133 | 134 | case 'perspective': 135 | // The matrix is computed by starting with an identity matrix and replacing the value at row 3, 136 | // column 4 with the value -1/depth. The value for depth must be greater than zero, otherwise 137 | // the function is invalid. 138 | value = parseFloat(value); 139 | 140 | matrix = [ 141 | 1, 0, 0, 0, 142 | 0, 1, 0, 0, 143 | 0, 0, 1, -1 / value, 144 | 0, 0, 0, 1 145 | ]; 146 | break; 147 | 148 | case 'skew': 149 | matrix = createMatrix(); 150 | value = value.split(',').map(getRadian); 151 | matrix = [ 152 | 1, Math.tan(value[ 0 ]), 0, 0, 153 | Math.tan(value[ 1 ]), 1, 0, 0, 154 | 0, 0, 1, 0, 155 | 0, 0, 0, 1 156 | ]; 157 | break; 158 | 159 | case 'skewX': 160 | matrix = createMatrix(); 161 | value = getRadian(value); 162 | matrix = [ 163 | 1, 0, 0, 0, 164 | Math.tan(value), 1, 0, 0, 165 | 0, 0, 1, 0, 166 | 0, 0, 0, 1 167 | ]; 168 | break; 169 | 170 | case 'skewY': 171 | matrix = createMatrix(); 172 | value = getRadian(value); 173 | matrix = [ 174 | 1, Math.tan(value), 0, 0, 175 | 0, 1, 0, 0, 176 | 0, 0, 1, 0, 177 | 0, 0, 0, 1 178 | ]; 179 | break; 180 | 181 | case 'none': 182 | case 'initial': 183 | break; 184 | 185 | default: 186 | throw new Error('unsupported transform function: ' + name); 187 | break; 188 | }; 189 | 190 | if(matrix) { 191 | mat4Multiply(outMatrix, outMatrix, matrix); 192 | } 193 | }); 194 | 195 | return outMatrix; 196 | }; 197 | 198 | function getRadian(value) { 199 | if(value.indexOf("deg") != -1) { 200 | return parseFloat(value,10) * (Math.PI * 2 / 360); 201 | } else if (value.indexOf("grad") != -1) { 202 | return parseFloat(value,10) * (Math.PI/200); 203 | } else { 204 | return parseFloat(value,10); 205 | } 206 | } 207 | 208 | function createMatrix() { 209 | return [ 210 | 1, 0, 0, 0, 211 | 0, 1, 0, 0, 212 | 0, 0, 1, 0, 213 | 0, 0, 0, 1 214 | ]; 215 | } 216 | 217 | 218 | 219 | // computeMatrix: function(ruleValue) { 220 | 221 | // //Split the webkit functions and loop through them 222 | // var functions = ruleValue.match(/[A-z]+\([^\)]+/g) || []; 223 | // var matrices = []; 224 | 225 | // for (var k=0; k < functions.length; k++) { 226 | 227 | // //Prepare the function name and its value 228 | // var func = functions[k].split('(')[0], 229 | // value = functions[k].split('(')[1]; 230 | 231 | // //Now we rotate through the functions and add it to our matrix 232 | // switch(func) { 233 | // case 'matrix': //Attention: Matrix in IE doesn't support e,f = tx,ty = translation 234 | // var values = value.split(','); 235 | // matrices.push($M([ 236 | // [values[0], values[2], 0], 237 | // [values[1], values[3], 0], 238 | // [0, 0, 1] 239 | // ])); 240 | // break; 241 | // case 'rotate': 242 | // var a = Transformie.toRadian(value); 243 | // matrices.push($M([ 244 | // [Math.cos(a), -Math.sin(a), 0], 245 | // [Math.sin(a), Math.cos(a), 0], 246 | // [0, 0, 1] 247 | // ])); 248 | // break; 249 | // case 'scale': 250 | // matrices.push($M([ 251 | // [value, 0, 0], 252 | // [0, value, 0], 253 | // [0, 0, 1] 254 | // ])); 255 | // break; 256 | // case 'scaleX': 257 | // matrices.push($M([ 258 | // [value, 0, 0], 259 | // [0, 1, 0], 260 | // [0, 0, 1] 261 | // ])); 262 | // break; 263 | // case 'scaleY': 264 | // matrices.push($M([ 265 | // [1, 0, 0], 266 | // [0, value, 0], 267 | // [0, 0, 1] 268 | // ])); 269 | // break; 270 | // case 'skew': 271 | // var a = Transformie.toRadian(value); 272 | // matrices.push($M([ 273 | // [1, 0, 0], 274 | // [Math.tan(a), 1, 0], 275 | // [0, 0, 1] 276 | // ])); 277 | // case 'skewX': 278 | // var a = Transformie.toRadian(value); 279 | // matrices.push($M([ 280 | // [1, Math.tan(a),0], 281 | // [0, 1, 0], 282 | // [0, 0, 1] 283 | // ])); 284 | // break; 285 | // case 'skewY': 286 | // var a = Transformie.toRadian(value); 287 | // matrices.push($M([ 288 | // [1, 0, 0], 289 | // [Math.tan(a), 1, 0], 290 | // [0, 0, 1] 291 | // ])); 292 | // break; 293 | // }; 294 | 295 | // }; 296 | 297 | // if(!matrices.length) 298 | // return; 299 | 300 | // //Calculate the resulting matrix 301 | // var matrix = matrices[0]; 302 | // for (var k=0; k < matrices.length; k++) { 303 | // if(matrices[k+1]) matrix = matrix.x(matrices[k+1]); 304 | // }; 305 | 306 | // return matrix; 307 | 308 | // } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "css-transform-to-mat4", 3 | "version": "1.0.4", 4 | "description": "Will take a string which is a css transform value (2d or 3d) and return a mat4 or 3d transformation matrix from the string.", 5 | "main": "index.js", 6 | "license": "MIT", 7 | "author": { 8 | "name": "Mikko Haapoja", 9 | "email": "me@mikkoh.com", 10 | "url": "https://github.com/mikkoh" 11 | }, 12 | "dependencies": { 13 | "gl-mat4": "^1.1.3" 14 | }, 15 | "devDependencies": { 16 | "budo": "^8.2.2" 17 | }, 18 | "scripts": { 19 | "dev": "budo test --live --open" 20 | }, 21 | "keywords": [ 22 | "css,transform,to,matrix,parse,parser,mat4,webgl" 23 | ], 24 | "repository": { 25 | "type": "git", 26 | "url": "git://github.com/Jam3/css-transform-to-mat4.git" 27 | }, 28 | "homepage": "https://github.com/Jam3/css-transform-to-mat4", 29 | "bugs": { 30 | "url": "https://github.com/Jam3/css-transform-to-mat4/issues" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | var cssTransformToMatrix = require('./..'); 2 | 3 | document.body.style[ 'text-align' ] = 'right'; 4 | document.body.style[ 'font-family' ] = 'Helvetica, sans serif'; 5 | document.body.style.width = '100%'; 6 | document.body.style.margin = '0'; 7 | document.body.innerHTML = '
The following is a test of converting css transform value strings to mat4 and then reapplying via css transform: matrix3d()
' + 8 | '
The left side is applying css transform value as is. The right side is applying returned mat4 via css transform: matrix3d()
'; 9 | 10 | createTest('none'); 11 | createTest('initial'); 12 | 13 | createTest('matrix(0.5, 0, 0, 0.5, 0, 0)'); 14 | createTest('matrix(0.353553390593274, 0.353553390593274, 0, -0.0707106781186548, 0.0707106781186548, 0)'); 15 | createTest('matrix3d(0.766044443118978, 0.642787609686539, 0, 0, -0.642787609686539, 0.766044443118978, 0, 0, 0, 0, 1, 0, 63.748692118167, 20.5996498310335, 0, 1)'); 16 | createTest('translate(40px, 20px)'); 17 | createTest('translateX(40px)'); 18 | createTest('translateY(40px)'); 19 | createTest('rotate(20deg)'); 20 | createTest('scale(0.5, 0.5)'); 21 | createTest('scaleX(0.5)'); 22 | createTest('scaleY(0.5)'); 23 | createTest('skew(30deg, 20deg)'); 24 | createTest('skewX(30deg)'); 25 | createTest('skewY(30deg)'); 26 | createTest('skewY(30deg)'); 27 | createTest('perspective(1000px) translate3d(40px, 20px, -1000px)'); 28 | createTest('translate3d(40px, 20px, -1000px)', true); 29 | createTest('translateZ(-1000px)', true); 30 | createTest('scale3d(0.5, 0.35, 0.3)', true); 31 | createTest('scaleZ(0.3)', true); 32 | createTest('rotateX(45deg)', true); 33 | createTest('rotateY(45deg)', true); 34 | createTest('rotateZ(45deg)', true); 35 | createTest('rotate3d(1, 1, 0, 45deg)', true); 36 | createTest('rotate(45deg) scale(0.5, 0.1)'); 37 | createTest('translateZ(-1000px) scale(1, 1.3) rotateX(30deg) scaleZ(0.5)', true); 38 | 39 | function createTest(transform, addPerspective) { 40 | 41 | var container = document.createElement('div'); 42 | var leftParent = getParent(); 43 | var rightParent = getParent(); 44 | var left = getToTransform('#CAFE00'); 45 | var right = getToTransform('#00BABE'); 46 | 47 | container.style.width = '100%'; 48 | container.style.height = '200px'; 49 | container.style.margin = '30px 0px 30px 0px'; 50 | 51 | rightParent.style.left = '50%'; 52 | 53 | if(!addPerspective) { 54 | left.style.transform = transform; 55 | right.style.transform = 'matrix3d(' + ( cssTransformToMatrix(transform).join(',') ) + ')'; 56 | } else { 57 | left.style.transform = 'perspective(1000px) ' + transform; 58 | right.style.transform = 'perspective(1000px) matrix3d(' + ( cssTransformToMatrix(transform).join(',') ) + ')'; 59 | } 60 | 61 | 62 | container.innerHTML = transform; 63 | 64 | 65 | leftParent.appendChild(left); 66 | rightParent.appendChild(right); 67 | container.appendChild(leftParent); 68 | container.appendChild(rightParent); 69 | document.body.appendChild(container); 70 | } 71 | 72 | function getParent() { 73 | var el = document.createElement('div'); 74 | 75 | el.style.position = 'absolute'; 76 | el.style.width = '50%'; 77 | el.style.height = '200px'; 78 | el.style.border = '1px solid #999'; 79 | 80 | return el; 81 | } 82 | 83 | function getToTransform(colour) { 84 | 85 | var el = document.createElement('div'); 86 | 87 | el.style.position = 'absolute'; 88 | el.style.left = '0px'; 89 | el.style.top = '0px'; 90 | el.style.background = colour; 91 | el.style.width = '100px'; 92 | el.style.height = '100px'; 93 | 94 | return el; 95 | } --------------------------------------------------------------------------------