UNPKG

11.3 kBJavaScriptView Raw
1/*! @license Rematrix v0.2.1
2
3 Copyright 2017 Fisssion LLC.
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
13 all 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
21 THE SOFTWARE.
22*/
23(function (global, factory) {
24 typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
25 typeof define === 'function' && define.amd ? define(['exports'], factory) :
26 (factory((global.Rematrix = {})));
27}(this, (function (exports) { 'use strict';
28
29/**
30 * @module Rematrix
31 */
32
33/**
34* Transformation matrices in the browser come in two flavors:
35*
36* - `matrix` using 6 values (short)
37* - `matrix3d` using 16 values (long)
38*
39* This utility follows this [conversion guide](https://goo.gl/EJlUQ1)
40* to expand short form matrices to their equivalent long form.
41*
42* @param {array} source - Accepts both short and long form matrices.
43* @return {array}
44*/
45function format (source) {
46 if (source.constructor !== Array) {
47 throw new TypeError('Expected array.')
48 }
49 if (source.length === 16) {
50 return source
51 }
52 if (source.length === 6) {
53 var matrix = identity();
54 matrix[0] = source[0];
55 matrix[1] = source[1];
56 matrix[4] = source[2];
57 matrix[5] = source[3];
58 matrix[12] = source[4];
59 matrix[13] = source[5];
60 return matrix
61 }
62 throw new RangeError('Expected array with either 6 or 16 values.')
63}
64
65/**
66 * Returns a matrix representing no transformation. The product of any matrix
67 * multiplied by the identity matrix will be the original matrix.
68 *
69 * > **Tip:** Similar to how `5 * 1 === 5`, where `1` is the identity.
70 *
71 * @return {array}
72 */
73function identity () {
74 var matrix = [];
75 for (var i = 0; i < 16; i++) {
76 i % 5 == 0 ? matrix.push(1) : matrix.push(0);
77 }
78 return matrix
79}
80
81/**
82 * Returns a matrix describing the inverse transformation of the source
83 * matrix. The product of any matrix multiplied by its inverse will be the
84 * identity matrix.
85 *
86 * > **Tip:** Similar to how `5 * (1/5) === 1`, where `1/5` is the inverse.
87 *
88 * @param {array} source - Accepts both short and long form matrices.
89 * @return {array}
90 */
91function inverse (source) {
92 var m = format(source);
93
94 var s0 = m[0] * m[5] - m[4] * m[1];
95 var s1 = m[0] * m[6] - m[4] * m[2];
96 var s2 = m[0] * m[7] - m[4] * m[3];
97 var s3 = m[1] * m[6] - m[5] * m[2];
98 var s4 = m[1] * m[7] - m[5] * m[3];
99 var s5 = m[2] * m[7] - m[6] * m[3];
100
101 var c5 = m[10] * m[15] - m[14] * m[11];
102 var c4 = m[9] * m[15] - m[13] * m[11];
103 var c3 = m[9] * m[14] - m[13] * m[10];
104 var c2 = m[8] * m[15] - m[12] * m[11];
105 var c1 = m[8] * m[14] - m[12] * m[10];
106 var c0 = m[8] * m[13] - m[12] * m[9];
107
108 var determinant = 1 / (s0 * c5 - s1 * c4 + s2 * c3 + s3 * c2 - s4 * c1 + s5 * c0);
109
110 if (isNaN(determinant) || determinant === Infinity) {
111 throw new Error('Inverse determinant attempted to divide by zero.')
112 }
113
114 return [
115 (m[5] * c5 - m[6] * c4 + m[7] * c3) * determinant,
116 (-m[1] * c5 + m[2] * c4 - m[3] * c3) * determinant,
117 (m[13] * s5 - m[14] * s4 + m[15] * s3) * determinant,
118 (-m[9] * s5 + m[10] * s4 - m[11] * s3) * determinant,
119
120 (-m[4] * c5 + m[6] * c2 - m[7] * c1) * determinant,
121 (m[0] * c5 - m[2] * c2 + m[3] * c1) * determinant,
122 (-m[12] * s5 + m[14] * s2 - m[15] * s1) * determinant,
123 (m[8] * s5 - m[10] * s2 + m[11] * s1) * determinant,
124
125 (m[4] * c4 - m[5] * c2 + m[7] * c0) * determinant,
126 (-m[0] * c4 + m[1] * c2 - m[3] * c0) * determinant,
127 (m[12] * s4 - m[13] * s2 + m[15] * s0) * determinant,
128 (-m[8] * s4 + m[9] * s2 - m[11] * s0) * determinant,
129
130 (-m[4] * c3 + m[5] * c1 - m[6] * c0) * determinant,
131 (m[0] * c3 - m[1] * c1 + m[2] * c0) * determinant,
132 (-m[12] * s3 + m[13] * s1 - m[14] * s0) * determinant,
133 (m[8] * s3 - m[9] * s1 + m[10] * s0) * determinant ]
134}
135
136/**
137 * Returns a 4x4 matrix describing the combined transformations
138 * of both arguments.
139 *
140 * > **Note:** Order is very important. For example, rotating 45°
141 * along the Z-axis, followed by translating 500 pixels along the
142 * Y-axis... is not the same as translating 500 pixels along the
143 * Y-axis, followed by rotating 45° along on the Z-axis.
144 *
145 * @param {array} m - Accepts both short and long form matrices.
146 * @param {array} x - Accepts both short and long form matrices.
147 * @return {array}
148 */
149function multiply (m, x) {
150 var fm = format(m);
151 var fx = format(x);
152 var product = [];
153
154 for (var i = 0; i < 4; i++) {
155 var row = [fm[i], fm[i + 4], fm[i + 8], fm[i + 12]];
156 for (var j = 0; j < 4; j++) {
157 var k = j * 4;
158 var col = [fx[k], fx[k + 1], fx[k + 2], fx[k + 3]];
159 var result = row[0] * col[0] + row[1] * col[1] + row[2] * col[2] + row[3] * col[3];
160
161 product[i + k] = result;
162 }
163 }
164
165 return product
166}
167
168/**
169 * Attempts to return a 4x4 matrix describing the CSS transform
170 * matrix passed in, but will return the identity matrix as a
171 * fallback.
172 *
173 * **Tip:** In virtually all cases, this method is used to convert
174 * a CSS matrix (retrieved as a `string` from computed styles) to
175 * its equivalent array format.
176 *
177 * @param {string} source - String containing a valid CSS `matrix` or `matrix3d` property.
178 * @return {array}
179 */
180function parse (source) {
181 if (typeof source === 'string') {
182 var match = source.match(/matrix(3d)?\(([^)]+)\)/);
183 if (match) {
184 var raw = match[2].split(', ').map(parseFloat);
185 return format(raw)
186 }
187 }
188 return identity()
189}
190
191/**
192 * Returns a 4x4 matrix describing Z-axis rotation.
193 *
194 * @param {number} angle - Measured in degrees.
195 * @return {array}
196 */
197function rotate (angle) {
198 return rotateZ(angle)
199}
200
201/**
202 * Returns a 4x4 matrix describing X-axis rotation.
203 *
204 * @param {number} angle - Measured in degrees.
205 * @return {array}
206 */
207function rotateX (angle) {
208 var theta = Math.PI / 180 * angle;
209 var matrix = identity();
210
211 matrix[5] = matrix[10] = Math.cos(theta);
212 matrix[6] = matrix[9] = Math.sin(theta);
213 matrix[9] *= -1;
214
215 return matrix
216}
217
218/**
219 * Returns a 4x4 matrix describing Y-axis rotation.
220 *
221 * @param {number} angle - Measured in degrees.
222 * @return {array}
223 */
224function rotateY (angle) {
225 var theta = Math.PI / 180 * angle;
226 var matrix = identity();
227
228 matrix[0] = matrix[10] = Math.cos(theta);
229 matrix[2] = matrix[8] = Math.sin(theta);
230 matrix[2] *= -1;
231
232 return matrix
233}
234
235/**
236 * Returns a 4x4 matrix describing Z-axis rotation.
237 *
238 * @param {number} angle - Measured in degrees.
239 * @return {array}
240 */
241function rotateZ (angle) {
242 var theta = Math.PI / 180 * angle;
243 var matrix = identity();
244
245 matrix[0] = matrix[5] = Math.cos(theta);
246 matrix[1] = matrix[4] = Math.sin(theta);
247 matrix[4] *= -1;
248
249 return matrix
250}
251
252/**
253* Returns a 4x4 matrix describing 2D scaling. The first argument
254* is used for both X and Y-axis scaling, unless an optional
255* second argument is provided to explicitly define Y-axis scaling.
256*
257* @param {number} scalar - Decimal multiplier.
258* @param {number} [scalarY] - Decimal multiplier.
259* @return {array}
260*/
261function scale (scalar, scalarY) {
262 var matrix = identity();
263
264 matrix[0] = scalar;
265 matrix[5] = typeof scalarY === 'number' ? scalarY : scalar;
266
267 return matrix
268}
269
270/**
271* Returns a 4x4 matrix describing X-axis scaling.
272*
273* @param {number} scalar - Decimal multiplier.
274* @return {array}
275*/
276function scaleX (scalar) {
277 var matrix = identity();
278 matrix[0] = scalar;
279 return matrix
280}
281
282/**
283* Returns a 4x4 matrix describing Y-axis scaling.
284*
285* @param {number} scalar - Decimal multiplier.
286* @return {array}
287*/
288function scaleY (scalar) {
289 var matrix = identity();
290 matrix[5] = scalar;
291 return matrix
292}
293
294/**
295* Returns a 4x4 matrix describing Z-axis scaling.
296*
297* @param {number} scalar - Decimal multiplier.
298* @return {array}
299*/
300function scaleZ (scalar) {
301 var matrix = identity();
302 matrix[10] = scalar;
303 return matrix
304}
305
306/**
307* Returns a 4x4 matrix describing shear. The first argument
308* defines X-axis shearing, and an optional second argument
309* defines Y-axis shearing.
310*
311* @param {number} angleX - Measured in degrees.
312* @param {number} [angleY] - Measured in degrees.
313* @return {array}
314*/
315function skew (angleX, angleY) {
316 var thetaX = Math.PI / 180 * angleX;
317 var matrix = identity();
318
319 matrix[4] = Math.tan(thetaX);
320
321 if (angleY) {
322 var thetaY = Math.PI / 180 * angleY;
323 matrix[1] = Math.tan(thetaY);
324 }
325
326 return matrix
327}
328
329/**
330* Returns a 4x4 matrix describing X-axis shear.
331*
332* @param {number} angle - Measured in degrees.
333* @return {array}
334*/
335function skewX (angle) {
336 var theta = Math.PI / 180 * angle;
337 var matrix = identity();
338
339 matrix[4] = Math.tan(theta);
340
341 return matrix
342}
343
344/**
345* Returns a 4x4 matrix describing Y-axis shear.
346*
347* @param {number} angle - Measured in degrees
348* @return {array}
349*/
350function skewY (angle) {
351 var theta = Math.PI / 180 * angle;
352 var matrix = identity();
353
354 matrix[1] = Math.tan(theta);
355
356 return matrix
357}
358
359/**
360 * Returns a 4x4 matrix describing 2D translation. The first
361 * argument defines X-axis translation, and an optional second
362 * argument defines Y-axis translation.
363 *
364 * @param {number} distanceX - Measured in pixels.
365 * @param {number} [distanceY] - Measured in pixels.
366 * @return {array}
367 */
368function translate (distanceX, distanceY) {
369 var matrix = identity();
370 matrix[12] = distanceX;
371
372 if (distanceY) {
373 matrix[13] = distanceY;
374 }
375
376 return matrix
377}
378
379/**
380 * Returns a 4x4 matrix describing X-axis translation.
381 *
382 * @param {number} distance - Measured in pixels.
383 * @return {array}
384 */
385function translateX (distance) {
386 var matrix = identity();
387 matrix[12] = distance;
388 return matrix
389}
390
391/**
392 * Returns a 4x4 matrix describing Y-axis translation.
393 *
394 * @param {number} distance - Measured in pixels.
395 * @return {array}
396 */
397function translateY (distance) {
398 var matrix = identity();
399 matrix[13] = distance;
400 return matrix
401}
402
403/**
404 * Returns a 4x4 matrix describing Z-axis translation.
405 *
406 * @param {number} distance - Measured in pixels.
407 * @return {array}
408 */
409function translateZ (distance) {
410 var matrix = identity();
411 matrix[14] = distance;
412 return matrix
413}
414
415exports.format = format;
416exports.identity = identity;
417exports.inverse = inverse;
418exports.multiply = multiply;
419exports.parse = parse;
420exports.rotate = rotate;
421exports.rotateX = rotateX;
422exports.rotateY = rotateY;
423exports.rotateZ = rotateZ;
424exports.scale = scale;
425exports.scaleX = scaleX;
426exports.scaleY = scaleY;
427exports.scaleZ = scaleZ;
428exports.skew = skew;
429exports.skewX = skewX;
430exports.skewY = skewY;
431exports.translate = translate;
432exports.translateX = translateX;
433exports.translateY = translateY;
434exports.translateZ = translateZ;
435
436Object.defineProperty(exports, '__esModule', { value: true });
437
438})));