Skip to content

Commit 0cd71ee

Browse files
committed
improved prop type testing, README improvements, 100% test coverage!
1 parent 572b572 commit 0cd71ee

File tree

13 files changed

+804
-393
lines changed

13 files changed

+804
-393
lines changed

README.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,10 @@ to pass scale in as an object rather than a number (otherwise z will be scaled t
156156

157157
### Performance
158158

159+
When running in development mode, `propTypes` checking can cause significant performance
160+
bottlenecks particularly if running `render` in a `requestAnimationFrame`. If you're
161+
seeing performance issues, try running a production build first.
162+
159163
With standard React apps you've probably got used to declaring lambdas and object literals
160164
inline in your render method. People sometimes talk about how this is a performance hit,
161165
but realistically for standard UI where renders are mostly limited to user interaction,
@@ -171,8 +175,9 @@ performance optimization don't do it unless you need to. Just sayin' :)
171175

172176
IE10 and IE11 famously don't support `preserve-3d`. To a certain extent this library can
173177
help with issues here because it won't create any nested DOM elements, but will compute
174-
the correct matrices to use on a single set of children. Realistically doing complex 3D
175-
transformations in these browsers is not possible - but is nothing to do with this library :)
178+
the correct matrices to use on a single set of children. However, you will still very
179+
likely run into z order issues as IE will maintain DOM / z-index order over 3D z position,
180+
so doing complex 3D transformations in these browsers is not really possible.
176181

177182
## How it works
178183

examples/3d-cubes/package-lock.json

Lines changed: 2 additions & 17 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

examples/3d-cubes/package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
"gl-matrix": "^3.0.0",
77
"prop-types": "^15.7.2",
88
"react": "^16.8.6",
9-
"react-css-transform": "^1.1.0",
109
"react-dom": "^16.8.6"
1110
},
1211
"devDependencies": {

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Transform2d.js

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,17 @@ import React from 'react';
22
import PropTypes from 'prop-types';
33
import { mat2d, vec2 } from 'gl-matrix';
44

5-
import { MULTIPLICATION_ORDER, vec2Shape, glMatrixType } from './constants';
5+
import { MULTIPLICATION_ORDER, vec2Obj, vec2GlMatrix, mat2dGlMatrix } from './constants';
66
import { setVec2FromProp } from './utils';
77

88
export default class Transform2d extends React.Component {
99
static propTypes = {
10-
parentMatrixWorld: glMatrixType,
10+
parentMatrixWorld: mat2dGlMatrix,
1111
multiplicationOrder: PropTypes.oneOf([MULTIPLICATION_ORDER.PRE, MULTIPLICATION_ORDER.POST]),
12-
translate: PropTypes.oneOfType([glMatrixType, vec2Shape]),
13-
scale: PropTypes.oneOfType([glMatrixType, vec2Shape, PropTypes.number]),
12+
translate: PropTypes.oneOfType([vec2GlMatrix, vec2Obj]),
13+
scale: PropTypes.oneOfType([vec2GlMatrix, vec2Obj, PropTypes.number]),
1414
rotate: PropTypes.number,
15-
children: PropTypes.node.isRequired,
15+
children: PropTypes.oneOfType([PropTypes.node, PropTypes.func]).isRequired,
1616
};
1717

1818
static defaultProps = {
@@ -43,7 +43,7 @@ export default class Transform2d extends React.Component {
4343

4444
if (multiplicationOrder === MULTIPLICATION_ORDER.PRE) {
4545
mat2d.multiply(matrixWorld, matrix, parentMatrixWorld);
46-
} else if (multiplicationOrder === MULTIPLICATION_ORDER.POST) {
46+
} else {
4747
mat2d.multiply(matrixWorld, parentMatrixWorld, matrix);
4848
}
4949

@@ -52,7 +52,7 @@ export default class Transform2d extends React.Component {
5252
parentMatrixWorld: matrixWorld,
5353
multiplicationOrder,
5454
style: {
55-
transform: matrixWorld.join(','),
55+
transform: `matrix(${matrixWorld.join(',')}`,
5656
},
5757
});
5858
}

src/Transform3d.js

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,17 @@ import React from 'react';
22
import PropTypes from 'prop-types';
33
import { mat4, vec3 } from 'gl-matrix';
44

5-
import { MULTIPLICATION_ORDER, vec3Shape, glMatrixType } from './constants';
5+
import { MULTIPLICATION_ORDER, vec3Obj, vec3GlMatrix, mat4GlMatrix } from './constants';
66
import { setVec3FromProp } from './utils';
77

88
export default class Transform3d extends React.Component {
99
static propTypes = {
10-
parentMatrixWorld: glMatrixType,
10+
parentMatrixWorld: mat4GlMatrix,
1111
multiplicationOrder: PropTypes.oneOf([MULTIPLICATION_ORDER.PRE, MULTIPLICATION_ORDER.POST]),
12-
translate: PropTypes.oneOfType([glMatrixType, vec3Shape]),
13-
scale: PropTypes.oneOfType([glMatrixType, vec3Shape, PropTypes.number]),
12+
translate: PropTypes.oneOfType([vec3GlMatrix, vec3Obj]),
13+
scale: PropTypes.oneOfType([vec3GlMatrix, vec3Obj, PropTypes.number]),
1414
rotate: PropTypes.number,
15-
rotateAxis: PropTypes.oneOfType([glMatrixType, vec3Shape]),
15+
rotateAxis: PropTypes.oneOfType([vec3GlMatrix, vec3Obj]),
1616
children: PropTypes.oneOfType([PropTypes.node, PropTypes.func]).isRequired,
1717
};
1818

@@ -51,7 +51,7 @@ export default class Transform3d extends React.Component {
5151

5252
if (multiplicationOrder === MULTIPLICATION_ORDER.PRE) {
5353
mat4.multiply(matrixWorld, matrix, parentMatrixWorld);
54-
} else if (multiplicationOrder === MULTIPLICATION_ORDER.POST) {
54+
} else {
5555
mat4.multiply(matrixWorld, parentMatrixWorld, matrix);
5656
}
5757

@@ -60,7 +60,7 @@ export default class Transform3d extends React.Component {
6060
parentMatrixWorld: matrixWorld,
6161
multiplicationOrder,
6262
style: {
63-
transform: matrixWorld.join(','),
63+
transform: `matrix3d(${matrixWorld.join(',')})`,
6464
},
6565
});
6666
}

src/constants.js

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
import PropTypes from 'prop-types';
2-
import { glMatrix } from 'gl-matrix';
1+
import { isVec2, isVec3, isMat2d, isMat4, isVec2Shape, isVec3Shape } from './utils';
32

43
export const MULTIPLICATION_ORDER = {
54
/**
@@ -41,15 +40,32 @@ export const MULTIPLICATION_ORDER = {
4140
PRE: 'PRE',
4241
};
4342

44-
export const vec2Shape = PropTypes.shape({
45-
x: PropTypes.number,
46-
y: PropTypes.number,
47-
});
43+
export const vec2Obj = (props, propName, componentName) => {
44+
const passes = isVec2Shape(props[propName]);
45+
return passes ? null : new Error(`${propName} in ${componentName} is not a vec2 shape`);
46+
};
4847

49-
export const vec3Shape = PropTypes.shape({
50-
x: PropTypes.number,
51-
y: PropTypes.number,
52-
z: PropTypes.number,
53-
});
48+
export const vec3Obj = (props, propName, componentName) => {
49+
const passes = isVec3Shape(props[propName]);
50+
return passes ? null : new Error(`${propName} in ${componentName} is not a vec3 shape`);
51+
};
5452

55-
export const glMatrixType = PropTypes.instanceOf(glMatrix.ARRAY_TYPE);
53+
export const vec2GlMatrix = (props, propName, componentName) => {
54+
const passes = isVec2(props[propName]);
55+
return passes ? null : new Error(`${propName} in ${componentName} is not a gl-matrix vec2`);
56+
};
57+
58+
export const vec3GlMatrix = (props, propName, componentName) => {
59+
const passes = isVec3(props[propName]);
60+
return passes ? null : new Error(`${propName} in ${componentName} is not a gl-matrix vec3`);
61+
};
62+
63+
export const mat2dGlMatrix = (props, propName, componentName) => {
64+
const passes = isMat2d(props[propName]);
65+
return passes ? null : new Error(`${propName} in ${componentName} is not a gl-matrix mat2d`);
66+
};
67+
68+
export const mat4GlMatrix = (props, propName, componentName) => {
69+
const passes = isMat4(props[propName]);
70+
return passes ? null : new Error(`${propName} in ${componentName} is not a gl-matrix mat4`);
71+
};

src/index.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1-
export { MULTIPLICATION_ORDER, glMatrixType, vec2Shape, vec3Shape } from './constants';
1+
export {
2+
MULTIPLICATION_ORDER,
3+
vec2Obj,
4+
vec3Obj,
5+
vec2GlMatrix,
6+
vec3GlMatrix,
7+
mat2dGlMatrix,
8+
mat4GlMatrix,
9+
} from './constants';
210
export { default as Transform2d } from './Transform2d';
311
export { default as Transform3d } from './Transform3d';

src/utils.js

Lines changed: 51 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,59 @@
11
import { vec2, vec3, glMatrix } from 'gl-matrix';
22

3+
export function isGlMatrixType(prop, length) {
4+
if (prop === null || prop === undefined) {
5+
return false;
6+
}
7+
8+
const usingTypedArray = glMatrix.ARRAY_TYPE === Float32Array;
9+
const isArray = usingTypedArray ? typeof prop === 'object' && prop.byteLength !== undefined : Array.isArray(prop);
10+
11+
return isArray && prop.length === length;
12+
}
13+
14+
export const isVec2 = prop => isGlMatrixType(prop, 2);
15+
export const isVec3 = prop => isGlMatrixType(prop, 3);
16+
export const isMat2d = prop => isGlMatrixType(prop, 6);
17+
export const isMat4 = prop => isGlMatrixType(prop, 16);
18+
19+
export function isVec2Shape(prop) {
20+
if (prop === null || prop === undefined) {
21+
return false;
22+
}
23+
24+
const allowedKeys = ['x', 'y'];
25+
26+
return !!Object.keys(prop).filter(key => allowedKeys.includes(key)).length;
27+
}
28+
29+
export function isVec3Shape(prop) {
30+
if (prop === null || prop === undefined) {
31+
return false;
32+
}
33+
34+
const allowedKeys = ['x', 'y', 'z'];
35+
36+
return !!Object.keys(prop).filter(key => allowedKeys.includes(key)).length;
37+
}
38+
339
export function setVec2FromProp(v, prop, defaultValue = 0) {
440
let x = defaultValue;
541
let y = defaultValue;
642

7-
if (!prop) {
43+
if (prop === undefined) {
844
return vec2.set(v, x, y);
945
}
1046

1147
if (typeof prop === 'number') {
1248
x = prop;
1349
y = prop;
14-
} else {
15-
const usingTypedArray = glMatrix.ARRAY_TYPE === Float32Array;
16-
const isArray = usingTypedArray ? prop.byteLength !== undefined : Array.isArray(prop);
17-
18-
if (isArray) {
19-
[x, y] = prop;
20-
} else if (typeof prop === 'object') {
21-
x = prop.x !== undefined ? prop.x : defaultValue;
22-
y = prop.y !== undefined ? prop.y : defaultValue;
50+
} else if (typeof prop === 'object') {
51+
if (isVec2(prop)) {
52+
return vec2.copy(v, prop);
2353
}
54+
55+
x = prop.x !== undefined ? prop.x : defaultValue;
56+
y = prop.y !== undefined ? prop.y : defaultValue;
2457
}
2558

2659
return vec2.set(v, x, y);
@@ -31,25 +64,22 @@ export function setVec3FromProp(v, prop, defaultX = 0, defaultY = 0, defaultZ =
3164
let y = defaultY;
3265
let z = defaultZ;
3366

34-
if (!prop) {
67+
if (prop === undefined) {
3568
return vec3.set(v, x, y, z);
3669
}
3770

3871
if (typeof prop === 'number') {
3972
x = prop;
4073
y = prop;
4174
z = prop;
42-
} else {
43-
const usingTypedArray = glMatrix.ARRAY_TYPE === Float32Array;
44-
const isArray = usingTypedArray ? prop.byteLength !== undefined : Array.isArray(prop);
45-
46-
if (isArray) {
47-
[x, y, z] = prop;
48-
} else if (typeof prop === 'object') {
49-
x = prop.x !== undefined ? prop.x : defaultX;
50-
y = prop.y !== undefined ? prop.y : defaultY;
51-
z = prop.z !== undefined ? prop.z : defaultZ;
75+
} else if (typeof prop === 'object') {
76+
if (isVec3(prop)) {
77+
return vec3.copy(v, prop);
5278
}
79+
80+
x = prop.x !== undefined ? prop.x : defaultX;
81+
y = prop.y !== undefined ? prop.y : defaultY;
82+
z = prop.z !== undefined ? prop.z : defaultZ;
5383
}
5484

5585
return vec3.set(v, x, y, z);

0 commit comments

Comments
 (0)