Skip to content

Commit

Permalink
Stage 6: Multiple component mount will have their separate state and …
Browse files Browse the repository at this point in the history
…behave as expected.

Added options gravity and windspeed.
On component unmount the code will break and animation won't be cancelled as the state wont be preserved until that.
  • Loading branch information
A M Akankshit authored and A M Akankshit committed Oct 31, 2020
1 parent a10dedd commit 8f7607b
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 43 deletions.
32 changes: 24 additions & 8 deletions src/App.js
Expand Up @@ -4,10 +4,10 @@ import Confetti from './confetti';
function App() {
const [showConfetti, setShowConfetti] = useState(false);
const [streamAnimation, toggleStreamAnimation] = useState(false);
const [streamAnimation2, toggleStreamAnimation2] = useState(false);

const [dummyState, setDummyState] = useState(false);
const rerender = () => {
setDummyState(!dummyState);
};
const [dummyState2, setDummyState2] = useState(false);

return (
<div className="App">
Expand All @@ -23,11 +23,27 @@ function App() {
Click Me to mount/unmount confetti component
</button>
{showConfetti && (
<div style={{ width: '100vh', height: '100vh', background: 'pink' }}>
<button onClick={() => toggleStreamAnimation(true)}>start</button>
<button onClick={() => toggleStreamAnimation(false)}>stop</button>
<button onClick={rerender}>rerender</button>
<Confetti options={{ count: 50 }} streamAnimation={streamAnimation} />
<div style={{ display: 'flex', 'justify-content': 'space-between' }}>
<div style={{ width: '100vh', height: '80vh', background: 'pink' }}>
<button onClick={() => toggleStreamAnimation(true)}>start</button>
<button onClick={() => toggleStreamAnimation(false)}>stop</button>
<button onClick={() => setDummyState(!dummyState)}>rerender</button>
<Confetti
options={{ count: 50 }}
streamAnimation={streamAnimation}
/>
</div>
<div style={{ width: '100vh', height: '80vh', background: 'pink' }}>
<button onClick={() => toggleStreamAnimation2(true)}>start</button>
<button onClick={() => toggleStreamAnimation2(false)}>stop</button>
<button onClick={() => setDummyState2(!dummyState2)}>
rerender
</button>
<Confetti
options={{ count: 50, gravity: 20 }}
streamAnimation={streamAnimation2}
/>
</div>
</div>
)}
</div>
Expand Down
69 changes: 34 additions & 35 deletions src/confetti/index.js
@@ -1,9 +1,9 @@
import React, { useEffect, useRef } from 'react';
import React, { useEffect, useRef, useState } from 'react';

const globalThis = window;
const { requestAnimationFrame, cancelAnimationFrame } = globalThis;

const confettiDefault = {
const confettiDefaultOptions = {
colors: [
'DodgerBlue',
'OliveDrab',
Expand All @@ -21,13 +21,14 @@ const confettiDefault = {
count: 200,
waveAngle: 0,
timeout: null,
speed: 8
gravity: 10,
windSpeed: 1
};
class BridalConfetti {
constructor(canvas, options = {}) {
this.canvas = canvas;
this.options = {
...confettiDefault,
...confettiDefaultOptions,
...options
};
this.height = 0;
Expand All @@ -40,17 +41,17 @@ class BridalConfetti {
updateAndDrawParticles(context) {
this.options.waveAngle = this.options.waveAngle + 0.01;
let x2, y2;

for (let i = 0; i < this.particles.length; i++) {
const particle = this.particles[i];
//update particle
particle.tiltAngle += particle.tiltAngleIncrement;
particle.x = particle.x + Math.sin(particle.tiltAngle) * 2 - 1;
particle.x =
particle.x + Math.sin(particle.tiltAngle) * 2 - this.options.windSpeed;
particle.y =
particle.y +
(Math.cos(this.options.waveAngle) +
particle.diameter +
this.options.speed) *
this.options.gravity) *
0.2;
particle.tilt = Math.sin(particle.tiltAngle);
if (
Expand Down Expand Up @@ -137,41 +138,39 @@ class BridalConfetti {
}
}

// IIFE
const Confetti = (() => {
let canvas = null;

// The component to be exported and used outside
return ({ styles = {}, options = {}, streamAnimation }) => {
const canvasRef = useRef(null);
const Confetti = ({ styles = {}, options = {}, streamAnimation }) => {
const canvasRef = useRef(null);
const [canvas, setCanvas] = useState(null);

useEffect(() => {
canvas = new BridalConfetti(canvasRef.current, options);
useEffect(() => {
setCanvas(new BridalConfetti(canvasRef.current, options));

return () => {
canvas.ummountCanvas();
};
}, []);
return () => {
canvas.ummountCanvas();
};
}, []);

useEffect(() => {
useEffect(() => {
if (canvas) {
if (streamAnimation) {
canvas.startAnimation();
} else {
// This will not work. Because canvas will be set to null befor this is invoked.
canvas.stopAnimation();
}
}, [streamAnimation]);

return (
<canvas
ref={canvasRef}
style={{
height: '100%',
width: '100%',
...styles
}}
/>
);
};
})();
}
}, [streamAnimation, canvas]);

return (
<canvas
ref={canvasRef}
style={{
height: '100%',
width: '100%',
...styles
}}
/>
);
};

export default Confetti;

0 comments on commit 8f7607b

Please sign in to comment.