Skip to content

Commit

Permalink
Stage 4 (#3)
Browse files Browse the repository at this point in the history
* Stage 4: Start and stop functionality extracted outside of componenent and are controlled by 'ref' from parent
There are some problems with this model.
When the parent rerenders (because of parent state/prop change) the child (confetti component) will also rerender.
Then all the local properties with our Confetti component will reset (as it is a stateless functional component). And our component will give some undesired result.
1. Start and stop may not work.
2. On component unmount the animation callback won't be cancelled

* fix

Co-authored-by: A M Akankshit <akankshit.am@ET-C02YQFZGLVCF.local>
  • Loading branch information
N8mare and A M Akankshit committed Oct 31, 2020
1 parent 89669d9 commit 61446d5
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 29 deletions.
24 changes: 19 additions & 5 deletions src/App.js
@@ -1,9 +1,22 @@
import React, { useState } from 'react';
import React, { useState, useRef } from 'react';
import Confetti from './confetti';

function App() {
const startBtn = useRef(null);
const stopBtn = useRef(null);
const [showConfetti, setShowConfetti] = useState(false);
const [start, setStart] = useState(false);
const [dummyState, setDummyState] = useState(false);

const startAnimation = () => {
startBtn.current.click();
};
const stopAnimation = () => {
stopBtn.current.click();
};
const rerender = () => {
setDummyState(!dummyState);
};

return (
<div className="App">
<button
Expand All @@ -19,9 +32,10 @@ function App() {
</button>
{showConfetti && (
<div style={{ width: '100vh', height: '100vh', background: 'pink' }}>
<button onClick={() => setStart(true)}>start</button>
<button onClick={() => setStart(false)}>stop</button>
<Confetti playAnimation={start} />
<button onClick={startAnimation}>start</button>
<button onClick={stopAnimation}>stop</button>
<button onClick={rerender}>rerender</button>
<Confetti startRef={startBtn} stopRef={stopBtn} />
</div>
)}
</div>
Expand Down
54 changes: 30 additions & 24 deletions src/confetti/index.js
@@ -1,4 +1,4 @@
import React, { useEffect, useRef } from 'react';
import React, { Fragment, useEffect, useRef } from 'react';

const confetti = {
colors: [
Expand All @@ -21,17 +21,17 @@ const globalThis = window;

const { requestAnimationFrame, cancelAnimationFrame } = globalThis;

const Confetti = ({ playAnimation = false, styles = {} }) => {
const Confetti = ({ styles = {}, startRef, stopRef }) => {
const canvasRef = useRef(null);
let count = 200;
const particles = [];
let waveAngle = 0;
let animationId;
let height;
let width;
let stopStremingConfetti = false;

const updateAndDrawParticles = (context) => {
let stopStremingConfetti = !playAnimation;
waveAngle = waveAngle + 0.01;
let x2, y2;
for (let i = 0; i < particles.length; i++) {
Expand Down Expand Up @@ -85,19 +85,12 @@ const Confetti = ({ playAnimation = false, styles = {} }) => {
return particle;
};

const startAnimation = (ctx) => {
height = ctx.canvas.height;
width = ctx.canvas.width;
while (particles.length < count) {
particles.push(setParticle({}, width, height));
}
runAnimation(ctx);
};

useEffect(() => {
const startAnimation = () => {
cancelAnimationFrame(animationId);
stopStremingConfetti = false;
const canvas = canvasRef.current;
const context = canvas.getContext('2d');
const { width, height } = canvas.getBoundingClientRect();
({ width, height } = canvas.getBoundingClientRect());
if (canvas.width !== width || canvas.height !== height) {
const { devicePixelRatio: ratio = 1 } = globalThis;
/**
Expand All @@ -109,22 +102,35 @@ const Confetti = ({ playAnimation = false, styles = {} }) => {
count = parseInt(canvas.width / 10, 10);
context.scale(ratio, ratio);
}
startAnimation(context);
while (particles.length < count) {
particles.push(setParticle({}, canvas.width, canvas.height));
}
runAnimation(context);
};

const stopAnimation = () => {
stopStremingConfetti = true;
};

useEffect(() => {
return () => {
cancelAnimationFrame(animationId);
};
}, [playAnimation]);
}, []);

return (
<canvas
ref={canvasRef}
style={{
height: '100%',
width: '100%',
...styles
}}
/>
<Fragment>
<button onClick={startAnimation} hidden={true} ref={startRef}></button>
<button onClick={stopAnimation} hidden={true} ref={stopRef}></button>
<canvas
ref={canvasRef}
style={{
height: '100%',
width: '100%',
...styles
}}
/>
</Fragment>
);
};

Expand Down

0 comments on commit 61446d5

Please sign in to comment.