Skip to content

Commit 34065b5

Browse files
committed
added docs, improved the peerDependency versioning
1 parent 0decb08 commit 34065b5

File tree

3 files changed

+178
-18
lines changed

3 files changed

+178
-18
lines changed

README.md

Lines changed: 175 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,191 @@
22

33
# React CSS Transform
44

5-
A helper library to handle nested 2D and 3D CSS transforms using matrix multiplication,
5+
A React helper library to handle nested 2D and 3D CSS transforms using matrix multiplication,
66
drastically reducing the number of nested DOM elements required and making complex
77
combinations of nested transformations trivial.
88

99
`<Transform2d>` was initially developed while I was working at [Pest Pulse](https://www.pestpulse.com/)
1010
for a zoomable/pannable map with markers.
1111

12-
Get started:
12+
## Install
13+
14+
`react-css-transform` uses the [gl-matrix library](https://github.com/toji/gl-matrix)
15+
under the hood. It's a peer dependency so install like so:
1316

1417
`npm install gl-matrix react-css-transform --save`
1518

16-
More docs coming soon!
19+
## Examples
1720

1821
[3D Cubes Example](https://baseten.github.io/react-css-transform/3d-cubes/index.html)
1922

2023
![3D Cubes GIF](https://baseten.github.io/react-css-transform/3d-cubes.gif)
24+
25+
## Usage
26+
27+
In general it is best to make sure that the HTML elements you are transforming have styles
28+
set like so:
29+
30+
```css
31+
position: absolute;
32+
left: 0;
33+
top: 0;
34+
```
35+
36+
If you are using 3D Transforms you also most likely want to set the containing HTML Element
37+
to have `preserve-3d` and some `perspective`:
38+
39+
```css
40+
transform-style: preserve-3d;
41+
perspective: 500px;
42+
```
43+
44+
### <Transform2d />
45+
46+
The styles provided to the `<div />` below will be merged with the computed CSS `matrix` transform
47+
48+
```javascript
49+
<Transform2d translate={{x:10, y:20}} scale={2} rotate={Math.PI} />
50+
<Transform2d translate={{x: -50, y:-50}}>
51+
// ... as much nesting as you like
52+
<div style={{width: 100, height: 100, margin: -50, background: '#f00'}} />
53+
</Transform2d>
54+
</Transform2d>
55+
```
56+
57+
#### Props
58+
59+
##### - multiplicationOrder
60+
61+
**Optional**. An enum: either `MULTIPLICATION_ORDER.PRE` or `MULTIPLICATION_ORDER.POST`.
62+
This determines the order an object's local matrix is multiplied with it's parent's matrix world.
63+
The default is `MULTIPLICATION_ORDER.POST`. You can only set this at the most outer `Transform2d`
64+
component. `PRE` will mimic how the transforms would be applied if you were doing them as
65+
actual nested DOM elements. `POST` is much more natural mathematically and way more useful.
66+
You should use `POST` :)
67+
68+
##### - translate
69+
70+
**Optional**. An object describing translation. Either a plain JS object or a gl-matrix
71+
`vec2`. If you pass in a JS object without all dimensions, missing dimensions will be
72+
given `0` as the default value. If nothing is supplied no translation occurs.
73+
74+
##### - scale
75+
76+
**Optional**. The transform's scale. either a number, a plain JS object or a gl-matrix
77+
`vec2`. A number will apply the same scale to `x` and `y`. If you pass in a JS object
78+
without all dimensions, missing dimensions will be given `1` as the default value. If
79+
nothing is supplied no scaling occurs.
80+
81+
##### - rotate
82+
83+
**Optional**. The transform's rotation. A number provided in **radians**. If nothing is
84+
supplied no rotation occurs.
85+
86+
### <Transform3d />
87+
88+
The styles provided to the `<div />` below will be merged with the CSS `matrix3d` transform
89+
90+
```javascript
91+
const translateToCentre = {
92+
x: window.innerWidth / 2,
93+
y: window.innerHeight / 2
94+
};
95+
96+
const theta = Math.PI / 2;
97+
const yAxis = { x: 0, y: 1, z: 0 };
98+
const zAxis = { x: 0, y: 0, z: 1 };
99+
100+
<Transform3d translate={translateToCentre} rotate={theta} rotateAxis={yAxis}>
101+
<Transform3d rotate={theta} rotateAxis={zAxis}>
102+
// ... as much nesting as you like
103+
<div style={{width: 100, height: 100, margin: -50, background: '#f00'}} />
104+
</Transform3d>
105+
<Transform3d translate={cubeGroup1Translate}>
106+
```
107+
108+
#### Props
109+
110+
##### - multiplicationOrder
111+
112+
**Optional**. An enum: either `MULTIPLICATION_ORDER.PRE` or `MULTIPLICATION_ORDER.POST`.
113+
This determines the order an object's local matrix is multiplied with it's parent's matrix world.
114+
The default is `MULTIPLICATION_ORDER.POST`. You can only set this at the most outer `Transform2d`
115+
component. `PRE` will mimic how the transforms would be applied if you were doing them as
116+
actual nested DOM elements. `POST` is much more natural mathematically and way more useful.
117+
You should use `POST` :)
118+
119+
##### - translate
120+
121+
**Optional**. An object describing translation. Either a plain JS object or a gl-matrix
122+
`vec3`. If you pass in a JS object without all dimensions, missing dimensions will be
123+
given `0` as the default value. If nothing is supplied no translation occurs.
124+
125+
##### - scale
126+
127+
**Optional**. The transform's scale. either a number, a plain JS object or a gl-matrix
128+
`vec3`. A number will apply the same scale to `x`, `y` and `z`. If you pass in a JS object
129+
without all dimensions, missing dimensions will be given `1` as the default value. If
130+
nothing is supplied no scaling occurs.
131+
132+
##### - rotate
133+
134+
**Optional**. The transform's rotation. A number provided in **radians**. If nothing is
135+
supplied no rotation occurs.
136+
137+
##### - rotateAxis
138+
139+
**Optional**. The axis to rotate around. Either a plain JS object or a gl-matrix `vec3`.
140+
It can be any arbitrary axis, but it must be normalized (a unit vector). If nothing is
141+
supplied it defaults to the z-axis: `{x: 0, y: 0, z: 1}`.
142+
143+
## Gotchas
144+
145+
### 2D and 3D Transforms together
146+
147+
Make sure you don't mix `Transform2d` and `Transform3d` components together! You can use
148+
`Transform3d` almost in exactly the same way as `Transform2d` if you want to force a 3D
149+
CSS transform, or combine with other `Transform3d`s. The only caveat here is it's better
150+
to pass scale in as an object rather than a number (otherwise z will be scaled too).
151+
`Transform3d` sets the default `rotateAxis` as the z axis, so it will rotate like a
152+
`Transform2d`:
153+
154+
`<Transform3d translate={{x:10, y:10}} scale={{x:2, y:2}} rotate={Math.PI} />`
155+
156+
### Performance
157+
158+
With standard React apps you've probably got used to declaring lambdas and object literals
159+
inline in your render method. People sometimes talk about how this is a performance hit,
160+
but realistically for standard UI where renders are mostly limited to user interaction,
161+
it's going to make no noticeable difference and the readability is worth it.
162+
163+
If you're gonna do 3D transformations every frame, or more specifically call `render` as
164+
the result of a `requestAnimationFrame` callback, you *may* want to reconsider this.
165+
Modern browsers can create and dispose of objects and arrays very quickly, but when they're
166+
disposed of the garbage collector *may* cause a janky animation. As with all
167+
performance optimization don't do it unless you need to. Just sayin' :)
168+
169+
### IE10 and IE11
170+
171+
IE10 and IE11 famously don't support `preserve-3d`. To a certain extent this library can
172+
help with issues here because it won't create any nested DOM elements, but will compute
173+
the correct matrices to use on a single set of children. Realistically doing complex 3D
174+
transformations in these browsers is not possible - but is nothing to do with this library :)
175+
176+
## How it works
177+
178+
Under the hood `Transform2d` and `Transform3d` work in pretty much exactly the same way.
179+
The `gl-matrix` library is used to handle all the vector and matrix maths, calculating
180+
an object's local matrix based on the supplied props (`translate`, `scale`, `rotate` and
181+
for `Transform3d` `rotateAxis`). These are multiplied together in the standard `T * R * S`
182+
order. This local matrix is multiplied with the `parentMatrixWorld` (which is automatically
183+
passed down from any parent Transforms) to produce the object's `matrixWorld`. This
184+
`matrixWorld` can then be used in an HTML element's [CSS transform property](https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function/matrix), either as
185+
`matrix` or `matrix3d` depending on the transform. The `matrixWorld` is then passed down
186+
to any nested Transforms as the next `parentMatrixWorld`.
187+
188+
`Transform2d` and `Transform3d` handle passing down these props internally as well as
189+
setting the `style` property (while merging in any predefined styles) on their child HTML
190+
elements. But if you need to add any custom React Components in between them and the HTML
191+
element you wish to transform, you will need to manually pass the props through, as you
192+
can see [here](https://github.com/baseten/react-css-transform/blob/master/examples/3d-cubes/src/components/CubeGroup.js),

examples/3d-cubes/src/styles.css

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,3 @@ body {
2929
top: 0;
3030
backface-visibility: hidden;
3131
}
32-
33-
.-red {
34-
background-color: #f00;
35-
}
36-
37-
.-green {
38-
background-color: #0f0;
39-
}
40-
41-
.-blue {
42-
background-color: #00f;
43-
}

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,9 @@
5353
],
5454
"sideEffects": false,
5555
"peerDependencies": {
56-
"react": "^16.0",
57-
"prop-types": "^15.0",
58-
"gl-matrix": "^3.0.0"
56+
"react": ">= 16.0",
57+
"prop-types": ">= 15.0",
58+
"gl-matrix": ">= 3.0.0"
5959
},
6060
"dependencies": {
6161
"@babel/runtime": "^7.4.4"

0 commit comments

Comments
 (0)