UNPKG

3.62 kBJavaScriptView Raw
1import React from 'react'
2import PropTypes from 'prop-types'
3
4/**
5 * The code for ConfettiParticle was taken from https://codepen.io/rainner/pen/BEOyJq
6 * thank you Rainner Lins :)
7 */
8class ConfettiParticle {
9 constructor({ context, width, height, color, speed }) {
10 this.context = context;
11 this.width = width;
12 this.height = height;
13 this.color = color;
14 this.diameter = 0;
15 this.tilt = 0;
16 this.tiltAngleIncrement = 0;
17 this.tiltAngle = 0;
18 this.particleSpeed = speed;
19 this.waveAngle = 0;
20 this.x = 0;
21 this.y = 0;
22 this.reset();
23 }
24
25 reset() {
26 this.x = Math.random() * this.width;
27 this.y = Math.random() * this.height - this.height;
28 this.diameter = Math.random() * 6 + 4;
29 this.tilt = 0;
30 this.tiltAngleIncrement = Math.random() * 0.1 + 0.04;
31 this.tiltAngle = 0;
32 }
33
34 update() {
35 this.waveAngle += this.tiltAngleIncrement;
36 this.tiltAngle += this.tiltAngleIncrement;
37 this.tilt = Math.sin(this.tiltAngle) * 12;
38 this.x += Math.sin(this.waveAngle);
39 this.y += (Math.cos(this.waveAngle ) + this.diameter + this.particleSpeed) * 0.4;
40 }
41
42 complete() {
43 return (this.y > this.height + 20);
44 }
45
46 draw() {
47 let x = this.x + this.tilt;
48 this.context.beginPath();
49 this.context.lineWidth = this.diameter;
50 this.context.strokeStyle = this.color;
51 this.context.moveTo(x + this.diameter / 2, this.y);
52 this.context.lineTo(x, this.y + this.tilt + this.diameter / 2);
53 this.context.stroke();
54 }
55}
56
57const colorOptions = [
58 '#44D7B6',
59 '#76C2F3',
60 '#F0FF02',
61 '#FEACBE',
62 '#FF511C',
63 '#6236FF',
64 '#0073D1'
65]
66
67class Confetti extends React.Component {
68 constructor(props) {
69 super(props)
70 this.setCanvasRef = this.setCanvasRef.bind(this)
71 this.animate = this.animate.bind(this)
72 this.setup = this.setup.bind(this)
73 }
74
75 componentDidMount() {
76 setTimeout(this.setup, 500)
77 }
78
79 setCanvasRef(canvas) {
80 this.canvas = canvas
81 }
82
83 componentWillUnmount() {
84 cancelAnimationFrame(this.animationId)
85 }
86
87 setup() {
88 this.createParticles()
89 this.animate()
90 }
91
92 // create confetti particles
93 createParticles() {
94 const context = this.getContext()
95 const { width, height, particleCount, particleSpeed } = this.props
96
97 this.particles = [];
98
99 for (let i = 0; i < particleCount; ++i) {
100 const index = Math.floor(Math.random() * colorOptions.length)
101 const color = colorOptions[index]
102 const particle = new ConfettiParticle({
103 context, width, height, color, speed: particleSpeed
104 })
105 this.particles.push(particle);
106 }
107 }
108
109 getContext() {
110 return this.canvas.getContext('2d')
111 }
112
113 animate() {
114 const { width, height, onComplete } = this.props
115 const context = this.getContext()
116 context.clearRect(0, 0, width, height)
117
118 let complete = true
119 for (let p of this.particles) {
120 p.width = width;
121 p.height = height;
122 p.update();
123 p.draw();
124 complete = complete && p.complete()
125 }
126
127 if (complete) {
128 onComplete()
129 } else {
130 this.animationId = requestAnimationFrame(this.animate)
131 }
132 }
133
134 render() {
135 const { width, height } = this.props
136 return (
137 <canvas ref={this.setCanvasRef} width={width} height={height}></canvas>
138 )
139 }
140}
141
142Confetti.propTypes = {
143 width: PropTypes.number.isRequired,
144 height: PropTypes.number.isRequired,
145 particleCount: PropTypes.number,
146 particleSpeed: PropTypes.number,
147 onComplete: PropTypes.func
148}
149
150Confetti.defaultProps = {
151 particleCount: 300,
152 particleSpeed: 1,
153 onComplete: () => null
154}
155
156/** @component */
157export default Confetti