Skip to content

Commit 419a9fc

Browse files
Merge pull request #16 from mojs/react-examples
This close #14 and close #15.
2 parents fb7729f + f92e818 commit 419a9fc

File tree

2 files changed

+347
-0
lines changed

2 files changed

+347
-0
lines changed

docs/.vuepress/config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ module.exports = {
4040
'/tutorials/getting-started.md',
4141
'/tutorials/shape-swirl/',
4242
'/tutorials/burst/',
43+
'/tutorials/usage-with-react.md',
4344
]
4445
},
4546
{

docs/tutorials/usage-with-react.md

Lines changed: 346 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,346 @@
1+
---
2+
title: Usage with React
3+
sidebarDepth: 0
4+
---
5+
6+
# Usage with React
7+
8+
> ### In this tutorial your will learn how to use MoJS in React using React's Hooks API.
9+
10+
#### Author: [Jonas Sandstedt](https://twitter.com/jonassandstedt)
11+
12+
[Fork example on CodeSandbox](https://codesandbox.io/s/mojs-react-example-kbikb?file=/src/MojsExample.js)
13+
14+
::: tip TLDR;
15+
Assign your animation to an `useRef` immutable object inside a `useEffect` hook. Then you can use this ref to control the animation, like this: `myRef.current.play()`
16+
:::
17+
18+
---
19+
20+
## Install packages
21+
22+
Start by installing React, ReactDOM and @mojs/core from your favorite package manager. Here we use npm:
23+
24+
```bash
25+
npm i react react-dom @mojs/core
26+
```
27+
28+
## Create the animation component
29+
30+
Here is the final component that we're gonna build:
31+
32+
```js
33+
import React, { useRef, useEffect, useState, useCallback } from "react";
34+
import mojs from "@mojs/core";
35+
36+
/**
37+
* Usage:
38+
* import MojsExample from './MojsExample';
39+
*
40+
* <MojsExample duration={1000}/>
41+
*/
42+
43+
const MojsExample = ({ duration }) => {
44+
const animDom = useRef();
45+
const bouncyCircle = useRef();
46+
const [isOpen, setIsOpen] = useState(false);
47+
const [isAnimating, setIsAnimating] = useState(false);
48+
49+
useEffect(() => {
50+
// Prevent multiple instansiations on hot reloads
51+
if (bouncyCircle.current) return;
52+
53+
// Assign a Shape animation to a ref
54+
bouncyCircle.current = new mojs.Shape({
55+
parent: animDom.current,
56+
shape: "circle",
57+
fill: { "#FC46AD": "#F64040" },
58+
radius: { 50: 200 },
59+
duration: duration,
60+
isShowStart: true,
61+
easing: "elastic.inout",
62+
onStart() {
63+
setIsAnimating(true);
64+
},
65+
onComplete() {
66+
setIsAnimating(false);
67+
},
68+
});
69+
});
70+
71+
// Update the animation values when the prop changes
72+
useEffect(() => {
73+
if (!bouncyCircle.current) return;
74+
bouncyCircle.current.tune({ duration: duration });
75+
isOpen
76+
? bouncyCircle.current.replayBackward()
77+
: bouncyCircle.current.replay();
78+
setIsOpen(!isOpen);
79+
}, [duration]);
80+
81+
const clickHandler = useCallback(() => {
82+
// If the "modal" is open, play the animation backwards, else play it forwards
83+
isOpen ? bouncyCircle.current.playBackward() : bouncyCircle.current.play();
84+
setIsOpen(!isOpen);
85+
}, [isOpen]);
86+
87+
return (
88+
<div ref={animDom} className="MojsExample">
89+
<div className="content">
90+
<h1>MoJS React Example</h1>
91+
<p>Using hooks</p>
92+
<button className="button" onClick={clickHandler}>
93+
{isAnimating && isOpen ? "Animating" : isOpen ? "Close" : "Open"}
94+
</button>
95+
</div>
96+
</div>
97+
);
98+
};
99+
100+
export default MojsExample;
101+
```
102+
103+
---
104+
105+
### Let's break it down!
106+
107+
First we create a `useRef` hooks, and takes advantage of its immutable object and assign our MoJS `Shape` animation to it. This way we can later use it to control our animation:
108+
109+
```js{2,5}
110+
const MojsExample = () => {
111+
const bouncyCircle = useRef();
112+
113+
useEffect(() => {
114+
bouncyCircle.current = new mojs.Shape({
115+
shape: "circle",
116+
fill: { "#FC46AD": "#F64040" },
117+
radius: { 50: 200 },
118+
duration: 1000,
119+
isShowStart: true,
120+
easing: "elastic.inout",
121+
});
122+
});
123+
124+
return;
125+
};
126+
```
127+
128+
By checking if the ref already has been assigned, we can prevent the animation from being instantiated again on re-renders (very useful when using hot reloads):
129+
130+
```js{5}
131+
const MojsExample = () => {
132+
const bouncyCircle = useRef();
133+
134+
useEffect(() => {
135+
if (bouncyCircle.current) return;
136+
137+
bouncyCircle.current = new mojs.Shape({
138+
shape: "circle",
139+
fill: { "#FC46AD": "#F64040" },
140+
radius: { 50: 200 },
141+
duration: 1000,
142+
isShowStart: true,
143+
easing: "elastic.inout",
144+
});
145+
});
146+
147+
return;
148+
};
149+
```
150+
151+
To get the reference to DOM element we want to append our animation to, we can use a `useRef` hook, and attach it to a container:
152+
153+
```js{3,9,21}
154+
const MojsExample = () => {
155+
const bouncyCircle = useRef();
156+
const animDom = useRef();
157+
158+
useEffect(() => {
159+
if (bouncyCircle.current) return;
160+
161+
bouncyCircle.current = new mojs.Shape({
162+
parent: animDom.current,
163+
shape: "circle",
164+
fill: { "#FC46AD": "#F64040" },
165+
radius: { 50: 200 },
166+
duration: 1000,
167+
isShowStart: true,
168+
easing: "elastic.inout",
169+
});
170+
171+
bouncyCircle.current.play();
172+
});
173+
174+
return <div ref={animDom} className="MojsExample" />;
175+
};
176+
177+
export default MojsExample;
178+
```
179+
180+
Now lets add a button to play the animation when we click it. To control the animation, we can now reference the MoJS animation using `bouncyCircle.current.play();`
181+
182+
```js{19-21,25-27}
183+
const MojsExample = () => {
184+
const bouncyCircle = useRef();
185+
const animDom = useRef();
186+
187+
useEffect(() => {
188+
if (bouncyCircle.current) return;
189+
190+
bouncyCircle.current = new mojs.Shape({
191+
parent: animDom.current,
192+
shape: "circle",
193+
fill: { "#FC46AD": "#F64040" },
194+
radius: { 50: 200 },
195+
duration: 1000,
196+
isShowStart: true,
197+
easing: "elastic.inout",
198+
});
199+
});
200+
201+
const clickHandler = useCallback(() => {
202+
bouncyCircle.current.play();
203+
});
204+
205+
return (
206+
<div ref={animDom} className="MojsExample">
207+
<button className="button" onClick={clickHandler}>
208+
Play animation
209+
</button>
210+
</div>
211+
);
212+
};
213+
214+
export default MojsExample;
215+
```
216+
217+
::: tip
218+
If we instead would want to play our animation directly, we could add `bouncyCircle.current.play();` directly after the bouncyCircle declaration.
219+
:::
220+
221+
By passing props to our `MojsExample` function, we can control the initial values of the animation, and also `.tune()` the animation when they change.
222+
223+
```js{7,19,25-29}
224+
/**
225+
* Usage:
226+
* import MojsExample from './MojsExample';
227+
*
228+
* <MojsExample duration={1000}/>
229+
*/
230+
const MojsExample = ({ duration }) => {
231+
const animDom = useRef();
232+
const bouncyCircle = useRef();
233+
234+
useEffect(() => {
235+
if (bouncyCircle.current) return;
236+
237+
bouncyCircle.current = new mojs.Shape({
238+
parent: animDom.current,
239+
shape: "circle",
240+
fill: { "#FC46AD": "#F64040" },
241+
radius: { 50: 200 },
242+
duration: duration,
243+
isShowStart: true,
244+
easing: "elastic.inout",
245+
});
246+
});
247+
248+
useEffect(() => {
249+
if (!bouncyCircle.current) return;
250+
bouncyCircle.current.tune({ duration: duration });
251+
bouncyCircle.current.replay();
252+
}, [duration]);
253+
254+
const clickHandler = useCallback(() => {
255+
bouncyCircle.current.play();
256+
});
257+
258+
return (
259+
<div ref={animDom} className="MojsExample">
260+
<button className="button" onClick={clickHandler}>
261+
Play animation
262+
</button>
263+
</div>
264+
);
265+
};
266+
```
267+
268+
As a final touch, lets add some methods to listen animation events, and use Reacts useState to save it as a local state:
269+
270+
```js{4-5,18-23,30-33,38-40,45}
271+
const MojsExample = ({ duration }) => {
272+
const animDom = useRef();
273+
const bouncyCircle = useRef();
274+
const [isOpen, setIsOpen] = useState(false);
275+
const [isAnimating, setIsAnimating] = useState(false);
276+
277+
useEffect(() => {
278+
if (bouncyCircle.current) return;
279+
280+
bouncyCircle.current = new mojs.Shape({
281+
parent: animDom.current,
282+
shape: "circle",
283+
fill: { "#FC46AD": "#F64040" },
284+
radius: { 50: 200 },
285+
duration: duration,
286+
isShowStart: true,
287+
easing: "elastic.inout",
288+
onStart() {
289+
setIsAnimating(true);
290+
},
291+
onComplete() {
292+
setIsAnimating(false);
293+
},
294+
});
295+
});
296+
297+
useEffect(() => {
298+
if (!bouncyCircle.current) return;
299+
bouncyCircle.current.tune({ duration: duration });
300+
isOpen
301+
? bouncyCircle.current.replayBackward()
302+
: bouncyCircle.current.replay();
303+
setIsOpen(!isOpen);
304+
}, [duration]);
305+
306+
const clickHandler = useCallback(() => {
307+
// If the "modal" is open, play the animation backwards, else play it forwards
308+
isOpen ? bouncyCircle.current.playBackward() : bouncyCircle.current.play();
309+
setIsOpen(!isOpen);
310+
}, [isOpen]);
311+
312+
return (
313+
<div ref={animDom} className="MojsExample">
314+
<button className="button" onClick={clickHandler}>
315+
{isAnimating && isOpen ? "Animating" : isOpen ? "Close" : "Open"}
316+
</button>
317+
</div>
318+
);
319+
};
320+
```
321+
322+
---
323+
324+
## Create the root App and render the page
325+
326+
Finally we import our component and add it to the root of our site.
327+
328+
```js
329+
import React from "react";
330+
import ReactDOM from "react-dom";
331+
332+
import MojsExample from "./MojsExample";
333+
334+
const rootElement = document.getElementById("root");
335+
ReactDOM.render(
336+
<React.StrictMode>
337+
<MojsExample duration={2000} />
338+
</React.StrictMode>,
339+
rootElement
340+
);
341+
```
342+
343+
You can see the full example and try it out here: [CodeSandbox](https://codesandbox.io/s/mojs-react-example-kbikb?file=/src/MojsExample.js).
344+
There is also a [button example](https://codesandbox.io/s/mojs-react-example-kbikb?file=/src/Button.js) with a `Burst` animation using an object pooling array.
345+
346+
Happy animating!_

0 commit comments

Comments
 (0)