├── .eslintignore ├── .gitignore ├── global.d.ts ├── src ├── Rect.ts ├── Point.ts ├── CircleClass.ts ├── utils.ts ├── index.tsx ├── ParticleGeneratorClass.ts ├── Particle.ts └── Confetti.ts ├── dist ├── Rect.d.ts ├── Point.d.ts ├── index.js ├── CircleClass.d.ts ├── index.d.ts ├── Particle.d.ts ├── ParticleGeneratorClass.d.ts ├── utils.d.ts ├── Confetti.d.ts ├── confetti-react.cjs.production.min.js ├── confetti-react.esm.js ├── confetti-react.cjs.development.js ├── confetti-react.cjs.production.min.js.map ├── confetti-react.esm.js.map └── confetti-react.cjs.development.js.map ├── .editorconfig ├── tsconfig.json ├── LICENSE ├── package.json ├── types └── tween-functions.d.ts └── README.md /.eslintignore: -------------------------------------------------------------------------------- 1 | dist/* 2 | node_modules/ 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | .DS_Store 3 | .idea 4 | coverage 5 | node_modules 6 | -------------------------------------------------------------------------------- /global.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'tween-functions'; 2 | declare module 'csstype'; 3 | -------------------------------------------------------------------------------- /src/Rect.ts: -------------------------------------------------------------------------------- 1 | export interface Rect { 2 | h: number; 3 | w: number; 4 | x: number; 5 | y: number; 6 | } 7 | -------------------------------------------------------------------------------- /dist/Rect.d.ts: -------------------------------------------------------------------------------- 1 | export interface Rect { 2 | h: number; 3 | w: number; 4 | x: number; 5 | y: number; 6 | } 7 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | charset = utf-8 7 | end_of_line = lf 8 | indent_size = 2 9 | indent_style = space 10 | insert_final_newline = true 11 | trim_trailing_whitespace = true 12 | 13 | -------------------------------------------------------------------------------- /dist/Point.d.ts: -------------------------------------------------------------------------------- 1 | export interface Point { 2 | x: number; 3 | y: number; 4 | } 5 | export default class PointClass implements Point { 6 | constructor(init: PointClass); 7 | x: number; 8 | y: number; 9 | } 10 | -------------------------------------------------------------------------------- /dist/index.js: -------------------------------------------------------------------------------- 1 | 2 | 'use strict' 3 | 4 | if (process.env.NODE_ENV === 'production') { 5 | module.exports = require('./confetti-react.cjs.production.min.js') 6 | } else { 7 | module.exports = require('./confetti-react.cjs.development.js') 8 | } 9 | -------------------------------------------------------------------------------- /src/Point.ts: -------------------------------------------------------------------------------- 1 | export interface Point { 2 | x: number; 3 | y: number; 4 | } 5 | 6 | export default class PointClass implements Point { 7 | constructor(init: PointClass) { 8 | this.x = init.x; 9 | this.y = init.y; 10 | } 11 | 12 | x: number; 13 | 14 | y: number; 15 | } 16 | -------------------------------------------------------------------------------- /dist/CircleClass.d.ts: -------------------------------------------------------------------------------- 1 | import { Point } from './Point'; 2 | export interface Circle extends Point { 3 | radius: number; 4 | } 5 | export default class CircleClass implements Circle { 6 | constructor(init: Circle); 7 | x: number; 8 | y: number; 9 | radius: number; 10 | } 11 | -------------------------------------------------------------------------------- /src/CircleClass.ts: -------------------------------------------------------------------------------- 1 | import { Point } from './Point'; 2 | 3 | export interface Circle extends Point { 4 | radius: number; 5 | } 6 | 7 | export default class CircleClass implements Circle { 8 | constructor(init: Circle) { 9 | this.x = init.x; 10 | this.y = init.y; 11 | this.radius = init.radius; 12 | } 13 | 14 | x: number; 15 | 16 | y: number; 17 | 18 | radius: number; 19 | } 20 | -------------------------------------------------------------------------------- /dist/index.d.ts: -------------------------------------------------------------------------------- 1 | import React, { CanvasHTMLAttributes } from 'react'; 2 | import { ConfettiOptions } from './Confetti'; 3 | export declare type Props = Partial & CanvasHTMLAttributes & { 4 | canvasRef?: React.Ref; 5 | }; 6 | export declare const Index: React.ForwardRefExoticComponent & React.CanvasHTMLAttributes & { 7 | canvasRef?: React.Ref; 8 | } & React.RefAttributes>; 9 | export default Index; 10 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": "./", 4 | "declaration": true, 5 | "esModuleInterop": true, 6 | "experimentalDecorators": true, 7 | "importHelpers": true, 8 | "jsx": "react", 9 | "lib": ["dom", "esnext"], 10 | "module": "esnext", 11 | "moduleResolution": "node", 12 | "noFallthroughCasesInSwitch": true, 13 | "noImplicitReturns": true, 14 | "noUnusedLocals": true, 15 | "noUnusedParameters": true, 16 | "outDir": "./build", 17 | "paths": { "*": ["src/*", "node_modules/*"] }, 18 | "rootDir": "./src", 19 | "sourceMap": true 20 | }, 21 | "exclude": [ 22 | "test", 23 | "build", 24 | "coverage", 25 | "node_modules" 26 | ], 27 | "include": [ 28 | "src" 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /dist/Particle.d.ts: -------------------------------------------------------------------------------- 1 | import { ConfettiOptions } from './Confetti'; 2 | export declare enum ParticleShape { 3 | Circle = 0, 4 | Square = 1, 5 | Strip = 2 6 | } 7 | declare enum RotationDirection { 8 | Positive = 1, 9 | Negative = -1 10 | } 11 | export default class Particle { 12 | constructor(context: CanvasRenderingContext2D, getOptions: () => ConfettiOptions, x: number, y: number); 13 | context: CanvasRenderingContext2D; 14 | radius: number; 15 | x: number; 16 | y: number; 17 | w: number; 18 | h: number; 19 | vx: number; 20 | vy: number; 21 | shape: ParticleShape; 22 | angle: number; 23 | angularSpin: number; 24 | color: string; 25 | rotateY: number; 26 | rotationDirection: RotationDirection; 27 | getOptions: () => ConfettiOptions; 28 | update(): void; 29 | } 30 | export {}; 31 | -------------------------------------------------------------------------------- /dist/ParticleGeneratorClass.d.ts: -------------------------------------------------------------------------------- 1 | import { ConfettiOptions } from './Confetti'; 2 | import { Rect } from './Rect'; 3 | import Particle from './Particle'; 4 | export interface ParticleGenerator extends Rect { 5 | removeParticleAt: (index: number) => void; 6 | getParticle: () => void; 7 | animate: () => boolean; 8 | particles: Particle[]; 9 | particlesGenerated: number; 10 | } 11 | export default class ParticleGeneratorClass implements ParticleGenerator { 12 | constructor(canvas: HTMLCanvasElement, getOptions: () => ConfettiOptions); 13 | canvas: HTMLCanvasElement; 14 | context: CanvasRenderingContext2D; 15 | getOptions: () => ConfettiOptions; 16 | x: number; 17 | y: number; 18 | w: number; 19 | h: number; 20 | lastNumberOfPieces: number; 21 | tweenInitTime: number; 22 | particles: Particle[]; 23 | particlesGenerated: number; 24 | removeParticleAt: (i: number) => void; 25 | getParticle: () => Particle; 26 | animate: () => boolean; 27 | } 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Aaron Lampros 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /dist/utils.d.ts: -------------------------------------------------------------------------------- 1 | import { Point } from './Point'; 2 | import { Rect } from './Rect'; 3 | import { Circle } from './CircleClass'; 4 | export declare function norm(value: number, min: number, max: number): number; 5 | export declare function lerp(lnorm: number, min: number, max: number): number; 6 | export declare function map(value: number, sourceMin: number, sourceMax: number, destMin: number, destMax: number): number; 7 | export declare function clamp(value: number, min: number, max: number): number; 8 | export declare function distance(p0: Point, p1: Point): number; 9 | export declare function distanceXY(x0: number, y0: number, x1: number, y1: number): number; 10 | export declare function circleCollision(c0: Circle, c1: Circle): boolean; 11 | export declare function circlePointCollision(x: number, y: number, circle: Circle): boolean; 12 | export declare function inRange(value: number, min: number, max: number): boolean; 13 | export declare function pointInRect(p: Point, rect: Rect): boolean; 14 | export declare function rangeIntersect(min0: number, max0: number, min1: number, max1: number): boolean; 15 | export declare function rectIntersect(r0: Rect, r1: Rect): boolean; 16 | export declare function degreesToRads(degrees: number): number; 17 | export declare function radsToDegrees(radians: number): number; 18 | export declare function randomRange(min: number, max: number): number; 19 | export declare function randomInt(min: number, max: number): number; 20 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "confetti-react", 3 | "version": "2.6.0", 4 | "description": "React component to draw confetti for your party.", 5 | "main": "dist/index.js", 6 | "module": "dist/confetti-react.esm.js", 7 | "typings": "dist/index.d.ts", 8 | "repository": { 9 | "type": "git", 10 | "url": "https://github.com/rkuykendall/confetti-react.git" 11 | }, 12 | "keywords": [ 13 | "component", 14 | "confetti", 15 | "react", 16 | "react-component" 17 | ], 18 | "author": "Gthibaud, Aaron Lampros, Robert Kuykendall", 19 | "license": "MIT", 20 | "bugs": { 21 | "url": "https://github.com/rkuykendall/confetti-react/issues" 22 | }, 23 | "homepage": "https://github.com/rkuykendall/confetti-react#readme", 24 | "files": [ 25 | "README.md", 26 | "LICENSE.md", 27 | "dist" 28 | ], 29 | "scripts": { 30 | "build": "tsdx build", 31 | "deploy": "np", 32 | "format": "tsdx lint src --fix", 33 | "lint": "tsdx lint src", 34 | "prepublishOnly": "tsdx build", 35 | "preversion": "npm run lint", 36 | "start": "tsdx watch", 37 | "test": "npm run lint", 38 | "version": "npm run build && git add dist" 39 | }, 40 | "prettier": { 41 | "printWidth": 120, 42 | "proseWrap": "always", 43 | "semi": true, 44 | "singleQuote": true, 45 | "trailingComma": "all" 46 | }, 47 | "peerDependencies": { 48 | "react": "^16.3.0 || ^17.0.0 || ^18.3.1" 49 | }, 50 | "dependencies": { 51 | "tween-functions": "^1.2.0" 52 | }, 53 | "devDependencies": { 54 | "@types/react": "^16.9.23", 55 | "np": "^5.0.0", 56 | "prettier": "^1.19.1", 57 | "react": "^16.3.0", 58 | "react-dom": "^16.3.0", 59 | "tsdx": "^0.14.1", 60 | "typescript": "^3.3.3333" 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/utils.ts: -------------------------------------------------------------------------------- 1 | import { Point } from './Point'; 2 | import { Rect } from './Rect'; 3 | import { Circle } from './CircleClass'; 4 | 5 | export function norm(value: number, min: number, max: number) { 6 | return (value - min) / (max - min); 7 | } 8 | 9 | export function lerp(lnorm: number, min: number, max: number) { 10 | return (max - min) * lnorm + min; 11 | } 12 | 13 | export function map(value: number, sourceMin: number, sourceMax: number, destMin: number, destMax: number) { 14 | return lerp(norm(value, sourceMin, sourceMax), destMin, destMax); 15 | } 16 | 17 | export function clamp(value: number, min: number, max: number) { 18 | return Math.min(Math.max(value, Math.min(min, max)), Math.max(min, max)); 19 | } 20 | 21 | export function distance(p0: Point, p1: Point) { 22 | const dx = p1.x - p0.x; 23 | const dy = p1.y - p0.y; 24 | return Math.sqrt(dx * dx + dy * dy); 25 | } 26 | 27 | export function distanceXY(x0: number, y0: number, x1: number, y1: number) { 28 | const dx = x1 - x0; 29 | const dy = y1 - y0; 30 | return Math.sqrt(dx * dx + dy * dy); 31 | } 32 | 33 | export function circleCollision(c0: Circle, c1: Circle) { 34 | return distance(c0, c1) <= c0.radius + c1.radius; 35 | } 36 | 37 | export function circlePointCollision(x: number, y: number, circle: Circle) { 38 | return distanceXY(x, y, circle.x, circle.y) < circle.radius; 39 | } 40 | 41 | export function inRange(value: number, min: number, max: number) { 42 | return value >= Math.min(min, max) && value <= Math.max(min, max); 43 | } 44 | 45 | export function pointInRect(p: Point, rect: Rect) { 46 | return inRange(p.x, rect.x, rect.x + rect.w) && inRange(p.y, rect.y, rect.y + rect.h); 47 | } 48 | 49 | export function rangeIntersect(min0: number, max0: number, min1: number, max1: number) { 50 | return Math.max(min0, max0) >= Math.min(min1, max1) && Math.min(min0, max0) <= Math.max(min1, max1); 51 | } 52 | 53 | export function rectIntersect(r0: Rect, r1: Rect) { 54 | return rangeIntersect(r0.x, r0.x + r0.w, r1.x, r1.x + r1.w) && rangeIntersect(r0.y, r0.y + r0.h, r1.y, r1.y + r1.h); 55 | } 56 | 57 | export function degreesToRads(degrees: number) { 58 | return (degrees * Math.PI) / 180; 59 | } 60 | 61 | export function radsToDegrees(radians: number) { 62 | return (radians * 180) / Math.PI; 63 | } 64 | 65 | export function randomRange(min: number, max: number) { 66 | return min + Math.random() * (max - min); 67 | } 68 | 69 | export function randomInt(min: number, max: number) { 70 | return Math.floor(min + Math.random() * (max - min + 1)); 71 | } 72 | -------------------------------------------------------------------------------- /src/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { Component, CanvasHTMLAttributes } from 'react'; 2 | import Confetti, { ConfettiOptions, confettiDefaults } from './Confetti'; 3 | 4 | const ref = React.createRef(); 5 | 6 | interface Refs { 7 | [key: string]: React.Ref; 8 | } 9 | 10 | function extractCanvasProps( 11 | props: Partial | any, 12 | ): [Partial, Partial>, Refs] { 13 | const confettiOptions: Partial = {}; 14 | const refs: Refs = {}; 15 | const rest: any = {}; 16 | const confettiOptionKeys = [...Object.keys(confettiDefaults), 'confettiSource', 'drawShape', 'onConfettiComplete']; 17 | const refProps = ['canvasRef']; 18 | Object.keys(props).forEach(prop => { 19 | const val = props[prop as string]; 20 | if (confettiOptionKeys.includes(prop)) { 21 | confettiOptions[prop] = val; 22 | } else if (refProps.includes(prop)) { 23 | refProps[prop as any] = val; 24 | } else { 25 | rest[prop] = val; 26 | } 27 | }); 28 | return [confettiOptions, rest, refs]; 29 | } 30 | 31 | export type Props = Partial & 32 | CanvasHTMLAttributes & { 33 | canvasRef?: React.Ref; 34 | }; 35 | 36 | class ConfettiReactInternal extends Component { 37 | static readonly defaultProps = { 38 | ...confettiDefaults, 39 | }; 40 | 41 | static readonly displayName = 'ConfettiReact'; 42 | 43 | canvas: React.RefObject = React.createRef(); 44 | 45 | confetti?: Confetti; 46 | 47 | constructor(props: Props) { 48 | super(props); 49 | this.canvas = (props.canvasRef as React.RefObject) || ref; 50 | } 51 | 52 | componentDidMount() { 53 | if (this.canvas.current) { 54 | const opts = extractCanvasProps(this.props)[0]; 55 | this.confetti = new Confetti(this.canvas.current, opts); 56 | } 57 | } 58 | 59 | componentDidUpdate() { 60 | const confettiOptions = extractCanvasProps(this.props)[0]; 61 | if (this.confetti) { 62 | this.confetti.options = confettiOptions as ConfettiOptions; 63 | } 64 | } 65 | 66 | componentWillUnmount() { 67 | if (this.confetti) { 68 | this.confetti.stop(); 69 | } 70 | this.confetti = undefined; 71 | } 72 | 73 | render() { 74 | const [confettiOptions, passedProps] = extractCanvasProps(this.props); 75 | const canvasStyles = { 76 | zIndex: 2, 77 | position: 'absolute' as 'absolute', 78 | pointerEvents: 'none' as 'none', 79 | top: 0, 80 | left: 0, 81 | bottom: 0, 82 | right: 0, 83 | ...passedProps.style, 84 | }; 85 | return ( 86 | 93 | ); 94 | } 95 | } 96 | 97 | // eslint-disable-next-line react/display-name 98 | export const Index = React.forwardRef((props, _ref) => ( 99 | 100 | )); 101 | 102 | export default Index; 103 | -------------------------------------------------------------------------------- /dist/Confetti.d.ts: -------------------------------------------------------------------------------- 1 | import { Rect } from './Rect'; 2 | import ParticleGeneratorClass from './ParticleGeneratorClass'; 3 | export interface ConfettiOptions { 4 | /** 5 | * Width of the component 6 | * @default window.width 7 | */ 8 | width: number; 9 | /** 10 | * Height of the component 11 | * @default window.height 12 | */ 13 | height: number; 14 | /** 15 | * Max number of confetti pieces to render. 16 | * @default 200 17 | */ 18 | numberOfPieces: number; 19 | /** 20 | * Slows movement of pieces. (lower number = slower confetti) 21 | * @default 0.99 22 | */ 23 | friction: number; 24 | /** 25 | * Blows confetti along the X axis. 26 | * @default 0 27 | */ 28 | wind: number; 29 | /** 30 | * How fast it falls (pixels per frame) 31 | * @default 0.1 32 | */ 33 | gravity: number; 34 | /** 35 | * How fast the confetti is emitted horizontally 36 | * @default 4 37 | */ 38 | initialVelocityX: number; 39 | /** 40 | * How fast the confetti is emitted vertically 41 | * @default 10 42 | */ 43 | initialVelocityY: number; 44 | /** 45 | * Array of colors to choose from. 46 | */ 47 | colors: string[]; 48 | /** 49 | * Opacity of the confetti. 50 | * @default 1 51 | */ 52 | opacity: number; 53 | /** 54 | * If false, only numberOfPieces will be emitted and then stops. If true, when a confetto goes offscreen, a new one will be emitted. 55 | * @default true 56 | */ 57 | recycle: boolean; 58 | /** 59 | * If false, stops the requestAnimationFrame loop. 60 | * @default true 61 | */ 62 | run: boolean; 63 | /** 64 | * Renders some debug text on the canvas. 65 | * @default false 66 | */ 67 | debug: boolean; 68 | /** 69 | * A Rect defining the area where the confetti will spawn. 70 | * @default { 71 | * x: 0, 72 | * y: 0, 73 | * w: canvas.width, 74 | * h: 0 75 | * } 76 | */ 77 | confettiSource: Rect; 78 | /** 79 | * Controls the rate at which confetti is spawned. 80 | * @default easeInOutQuad 81 | */ 82 | tweenFunction: (currentTime: number, currentValue: number, targetValue: number, duration: number, s?: number) => number; 83 | /** 84 | * Number of milliseconds it should take to spawn numberOfPieces. 85 | * @default 5000 86 | */ 87 | tweenDuration: number; 88 | /** 89 | * Function to draw your own confetti shapes. 90 | */ 91 | drawShape?: (context: CanvasRenderingContext2D) => void; 92 | /** 93 | * Function called when all confetti has fallen off-canvas. 94 | */ 95 | onConfettiComplete?: (confettiInstance?: Confetti) => void; 96 | } 97 | export declare const confettiDefaults: Pick>; 98 | export declare class Confetti { 99 | constructor(canvas: HTMLCanvasElement, opts: Partial); 100 | canvas: HTMLCanvasElement; 101 | context: CanvasRenderingContext2D; 102 | _options: ConfettiOptions; 103 | generator: ParticleGeneratorClass; 104 | rafId?: number; 105 | get options(): Partial; 106 | set options(opts: Partial); 107 | setOptionsWithDefaults: (opts: Partial) => void; 108 | update: () => void; 109 | reset: () => void; 110 | stop: () => void; 111 | } 112 | export default Confetti; 113 | -------------------------------------------------------------------------------- /src/ParticleGeneratorClass.ts: -------------------------------------------------------------------------------- 1 | import { ConfettiOptions } from './Confetti'; 2 | import { Rect } from './Rect'; 3 | import Particle from './Particle'; 4 | import { randomRange } from './utils'; 5 | 6 | export interface ParticleGenerator extends Rect { 7 | removeParticleAt: (index: number) => void; 8 | getParticle: () => void; 9 | animate: () => boolean; 10 | particles: Particle[]; 11 | particlesGenerated: number; 12 | } 13 | 14 | export default class ParticleGeneratorClass implements ParticleGenerator { 15 | constructor(canvas: HTMLCanvasElement, getOptions: () => ConfettiOptions) { 16 | this.canvas = canvas; 17 | const ctx = this.canvas.getContext('2d'); 18 | if (!ctx) { 19 | throw new Error('Could not get canvas context'); 20 | } 21 | this.context = ctx; 22 | this.getOptions = getOptions; 23 | } 24 | 25 | canvas: HTMLCanvasElement; 26 | 27 | context: CanvasRenderingContext2D; 28 | 29 | getOptions: () => ConfettiOptions; 30 | 31 | x = 0; 32 | 33 | y = 0; 34 | 35 | w = 0; 36 | 37 | h = 0; 38 | 39 | lastNumberOfPieces = 0; 40 | 41 | tweenInitTime: number = Date.now(); 42 | 43 | particles: Particle[] = []; 44 | 45 | particlesGenerated = 0; 46 | 47 | removeParticleAt = (i: number) => { 48 | this.particles.splice(i, 1); 49 | }; 50 | 51 | getParticle = () => { 52 | const newParticleX = randomRange(this.x, this.w + this.x); 53 | const newParticleY = randomRange(this.y, this.h + this.y); 54 | return new Particle(this.context, this.getOptions, newParticleX, newParticleY); 55 | }; 56 | 57 | animate = (): boolean => { 58 | const { canvas, context, particlesGenerated, lastNumberOfPieces } = this; 59 | const { run, recycle, numberOfPieces, debug, tweenFunction, tweenDuration } = this.getOptions(); 60 | if (!run) { 61 | return false; 62 | } 63 | 64 | const nP = this.particles.length; 65 | const activeCount = recycle ? nP : particlesGenerated; 66 | 67 | const now = Date.now(); 68 | 69 | // Initial population 70 | if (activeCount < numberOfPieces) { 71 | // Use the numberOfPieces prop as a key to reset the easing timing 72 | if (lastNumberOfPieces !== numberOfPieces) { 73 | this.tweenInitTime = now; 74 | this.lastNumberOfPieces = numberOfPieces; 75 | } 76 | const { tweenInitTime } = this; 77 | // Add more than one piece per loop, otherwise the number of pieces would 78 | // be limitted by the RAF framerate 79 | const progressTime = now - tweenInitTime > tweenDuration ? tweenDuration : Math.max(0, now - tweenInitTime); 80 | const tweenedVal = tweenFunction(progressTime, activeCount, numberOfPieces, tweenDuration); 81 | const numToAdd = Math.round(tweenedVal - activeCount); 82 | for (let i = 0; i < numToAdd; i += 1) { 83 | this.particles.push(this.getParticle()); 84 | } 85 | this.particlesGenerated += numToAdd; 86 | } 87 | if (debug) { 88 | // Draw debug text 89 | context.font = '12px sans-serif'; 90 | context.fillStyle = '#333'; 91 | context.textAlign = 'right'; 92 | context.fillText(`Particles: ${nP}`, canvas.width - 10, canvas.height - 20); 93 | } 94 | 95 | // Maintain the population 96 | this.particles.forEach((p, i) => { 97 | // Update each particle's position 98 | p.update(); 99 | // Prune the off-canvas particles 100 | if (p.y > canvas.height || p.y < -100 || p.x > canvas.width + 100 || p.x < -100) { 101 | if (recycle && activeCount <= numberOfPieces) { 102 | // Replace the particle with a brand new one 103 | this.particles[i] = this.getParticle(); 104 | } else { 105 | this.removeParticleAt(i); 106 | } 107 | } 108 | }); 109 | return nP > 0 || activeCount < numberOfPieces; 110 | }; 111 | } 112 | -------------------------------------------------------------------------------- /src/Particle.ts: -------------------------------------------------------------------------------- 1 | import { randomRange, randomInt, degreesToRads } from './utils'; 2 | import { ConfettiOptions } from './Confetti'; 3 | 4 | export enum ParticleShape { 5 | Circle = 0, 6 | Square, 7 | Strip, 8 | } 9 | 10 | enum RotationDirection { 11 | Positive = 1, 12 | Negative = -1, 13 | } 14 | 15 | export default class Particle { 16 | constructor(context: CanvasRenderingContext2D, getOptions: () => ConfettiOptions, x: number, y: number) { 17 | this.getOptions = getOptions; 18 | const { colors, initialVelocityX, initialVelocityY } = this.getOptions(); 19 | this.context = context; 20 | this.x = x; 21 | this.y = y; 22 | this.w = randomRange(5, 20); 23 | this.h = randomRange(5, 20); 24 | this.radius = randomRange(5, 10); 25 | this.vx = randomRange(-initialVelocityX, initialVelocityX); 26 | this.vy = randomRange(-initialVelocityY, 0); 27 | this.shape = randomInt(0, 2); 28 | this.angle = degreesToRads(randomRange(0, 360)); 29 | this.angularSpin = randomRange(-0.2, 0.2); 30 | this.color = colors[Math.floor(Math.random() * colors.length)]; 31 | this.rotateY = randomRange(0, 1); 32 | this.rotationDirection = randomRange(0, 1) ? RotationDirection.Positive : RotationDirection.Negative; 33 | } 34 | 35 | context: CanvasRenderingContext2D; 36 | 37 | radius: number; 38 | 39 | x: number; 40 | 41 | y: number; 42 | 43 | w: number; 44 | 45 | h: number; 46 | 47 | vx: number; 48 | 49 | vy: number; 50 | 51 | shape: ParticleShape; 52 | 53 | angle: number; 54 | 55 | angularSpin: number; 56 | 57 | color: string; 58 | 59 | // Actually used as scaleY to simulate rotation cheaply 60 | rotateY: number; 61 | 62 | rotationDirection: RotationDirection; 63 | 64 | getOptions: () => ConfettiOptions; 65 | 66 | update() { 67 | const { gravity, wind, friction, opacity, drawShape } = this.getOptions(); 68 | this.x += this.vx; 69 | this.y += this.vy; 70 | this.vy += gravity; 71 | this.vx += wind; 72 | this.vx *= friction; 73 | this.vy *= friction; 74 | if (this.rotateY >= 1 && this.rotationDirection === RotationDirection.Positive) { 75 | this.rotationDirection = RotationDirection.Negative; 76 | } else if (this.rotateY <= -1 && this.rotationDirection === RotationDirection.Negative) { 77 | this.rotationDirection = RotationDirection.Positive; 78 | } 79 | 80 | const rotateDelta = 0.1 * this.rotationDirection; 81 | 82 | this.rotateY += rotateDelta; 83 | this.angle += this.angularSpin; 84 | this.context.save(); 85 | this.context.translate(this.x, this.y); 86 | this.context.rotate(this.angle); 87 | this.context.scale(1, this.rotateY); 88 | this.context.rotate(this.angle); 89 | this.context.beginPath(); 90 | this.context.fillStyle = this.color; 91 | this.context.strokeStyle = this.color; 92 | this.context.globalAlpha = opacity; 93 | this.context.lineCap = 'round'; 94 | this.context.lineWidth = 2; 95 | if (drawShape && typeof drawShape === 'function') { 96 | drawShape.call(this, this.context); 97 | } else { 98 | switch (this.shape) { 99 | case ParticleShape.Circle: { 100 | this.context.beginPath(); 101 | this.context.arc(0, 0, this.radius, 0, 2 * Math.PI); 102 | this.context.fill(); 103 | break; 104 | } 105 | case ParticleShape.Square: { 106 | this.context.fillRect(-this.w / 2, -this.h / 2, this.w, this.h); 107 | break; 108 | } 109 | case ParticleShape.Strip: { 110 | this.context.fillRect(-this.w / 6, -this.h / 2, this.w / 3, this.h); 111 | break; 112 | } 113 | default: { 114 | throw new Error('Unknown type in Particle.ts'); 115 | } 116 | } 117 | } 118 | this.context.closePath(); 119 | this.context.restore(); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /types/tween-functions.d.ts: -------------------------------------------------------------------------------- 1 | export function easeInBack( 2 | currentTime: number, 3 | currentValue: number, 4 | targetValue: number, 5 | duration: number, 6 | s: any, 7 | ): number; 8 | export function easeInBounce(currentTime: number, currentValue: number, targetValue: number, duration: number): number; 9 | export function easeInCirc(currentTime: number, currentValue: number, targetValue: number, duration: number): number; 10 | export function easeInCubic(currentTime: number, currentValue: number, targetValue: number, duration: number): number; 11 | export function easeInElastic(currentTime: number, currentValue: number, targetValue: number, duration: number): number; 12 | export function easeInExpo(currentTime: number, currentValue: number, targetValue: number, duration: number): number; 13 | export function easeInOutBack( 14 | currentTime: number, 15 | currentValue: number, 16 | targetValue: number, 17 | duration: number, 18 | s: any, 19 | ): number; 20 | export function easeInOutBounce( 21 | currentTime: number, 22 | currentValue: number, 23 | targetValue: number, 24 | duration: number, 25 | ): number; 26 | export function easeInOutCirc(currentTime: number, currentValue: number, targetValue: number, duration: number): number; 27 | export function easeInOutCubic( 28 | currentTime: number, 29 | currentValue: number, 30 | targetValue: number, 31 | duration: number, 32 | ): number; 33 | export function easeInOutElastic( 34 | currentTime: number, 35 | currentValue: number, 36 | targetValue: number, 37 | duration: number, 38 | ): number; 39 | export function easeInOutExpo(currentTime: number, currentValue: number, targetValue: number, duration: number): number; 40 | export function easeInOutQuad(currentTime: number, currentValue: number, targetValue: number, duration: number): number; 41 | export function easeInOutQuart( 42 | currentTime: number, 43 | currentValue: number, 44 | targetValue: number, 45 | duration: number, 46 | ): number; 47 | export function easeInOutQuint( 48 | currentTime: number, 49 | currentValue: number, 50 | targetValue: number, 51 | duration: number, 52 | ): number; 53 | export function easeInOutSine(currentTime: number, currentValue: number, targetValue: number, duration: number): number; 54 | export function easeInQuad(currentTime: number, currentValue: number, targetValue: number, duration: number): number; 55 | export function easeInQuart(currentTime: number, currentValue: number, targetValue: number, duration: number): number; 56 | export function easeInQuint(currentTime: number, currentValue: number, targetValue: number, duration: number): number; 57 | export function easeInSine(currentTime: number, currentValue: number, targetValue: number, duration: number): number; 58 | export function easeOutBack( 59 | currentTime: number, 60 | currentValue: number, 61 | targetValue: number, 62 | duration: number, 63 | s: any, 64 | ): number; 65 | export function easeOutBounce(currentTime: number, currentValue: number, targetValue: number, duration: number): number; 66 | export function easeOutCirc(currentTime: number, currentValue: number, targetValue: number, duration: number): number; 67 | export function easeOutCubic(currentTime: number, currentValue: number, targetValue: number, duration: number): number; 68 | export function easeOutElastic( 69 | currentTime: number, 70 | currentValue: number, 71 | targetValue: number, 72 | duration: number, 73 | ): number; 74 | export function easeOutExpo(currentTime: number, currentValue: number, targetValue: number, duration: number): number; 75 | export function easeOutQuad(currentTime: number, currentValue: number, targetValue: number, duration: number): number; 76 | export function easeOutQuart(currentTime: number, currentValue: number, targetValue: number, duration: number): number; 77 | export function easeOutQuint(currentTime: number, currentValue: number, targetValue: number, duration: number): number; 78 | export function easeOutSine(currentTime: number, currentValue: number, targetValue: number, duration: number): number; 79 | export function linear(currentTime: number, currentValue: number, targetValue: number, duration: number): number; 80 | -------------------------------------------------------------------------------- /dist/confetti-react.cjs.production.min.js: -------------------------------------------------------------------------------- 1 | "use strict";function t(t){return t&&"object"==typeof t&&"default"in t?t.default:t}Object.defineProperty(exports,"__esModule",{value:!0});var e,i,n=require("react"),o=t(n),r=t(require("tween-functions"));function s(){return(s=Object.assign||function(t){for(var e=1;e=1&&this.rotationDirection===i.Positive?this.rotationDirection=i.Negative:this.rotateY<=-1&&this.rotationDirection===i.Negative&&(this.rotationDirection=i.Positive),this.rotateY+=.1*this.rotationDirection,this.angle+=this.angularSpin,this.context.save(),this.context.translate(this.x,this.y),this.context.rotate(this.angle),this.context.scale(1,this.rotateY),this.context.rotate(this.angle),this.context.beginPath(),this.context.fillStyle=this.color,this.context.strokeStyle=this.color,this.context.globalAlpha=s,this.context.lineCap="round",this.context.lineWidth=2,a&&"function"==typeof a)a.call(this,this.context);else switch(this.shape){case e.Circle:this.context.beginPath(),this.context.arc(0,0,this.radius,0,2*Math.PI),this.context.fill();break;case e.Square:this.context.fillRect(-this.w/2,-this.h/2,this.w,this.h);break;case e.Strip:this.context.fillRect(-this.w/6,-this.h/2,this.w/3,this.h);break;default:throw new Error("Unknown type in Particle.ts")}this.context.closePath(),this.context.restore()},t}(),l=function(t,e){var i=this;this.x=0,this.y=0,this.w=0,this.h=0,this.lastNumberOfPieces=0,this.tweenInitTime=Date.now(),this.particles=[],this.particlesGenerated=0,this.removeParticleAt=function(t){i.particles.splice(t,1)},this.getParticle=function(){var t=c(i.x,i.w+i.x),e=c(i.y,i.h+i.y);return new h(i.context,i.getOptions,t,e)},this.animate=function(){var t=i.canvas,e=i.context,n=i.particlesGenerated,o=i.lastNumberOfPieces,r=i.getOptions(),s=r.recycle,a=r.numberOfPieces,c=r.debug,h=r.tweenFunction,l=r.tweenDuration;if(!r.run)return!1;var u=i.particles.length,f=s?u:n,p=Date.now();if(fl?l:Math.max(0,p-v),f,a,l),g=Math.round(d-f),y=0;yt.height||e.y<-100||e.x>t.width+100||e.x<-100)&&(s&&f<=a?i.particles[n]=i.getParticle():i.removeParticleAt(n))})),u>0||f0&&e.call(i,i),i._options.run=!1)},this.reset=function(){i.generator&&i.generator.particlesGenerated>0&&(i.generator.particlesGenerated=0,i.generator.particles=[],i.generator.lastNumberOfPieces=0)},this.stop=function(){i.options={run:!1},i.rafId&&(cancelAnimationFrame(i.rafId),i.rafId=void 0)},this.canvas=t;var n=this.canvas.getContext("2d");if(!n)throw new Error("Could not get canvas context");this.context=n,this.generator=new l(this.canvas,(function(){return i.options})),this.options=e,this.update()}var e;return(e=[{key:"options",get:function(){return this._options},set:function(t){var e=this._options&&this._options.run,i=this._options&&this._options.recycle;this.setOptionsWithDefaults(t),this.generator&&(Object.assign(this.generator,this.options.confettiSource),"boolean"==typeof t.recycle&&t.recycle&&!1===i&&(this.generator.lastNumberOfPieces=this.generator.particles.length)),"boolean"==typeof t.run&&t.run&&!1===e&&this.update()}}])&&function(t,e){for(var i=0;i number; 91 | /** 92 | * Number of milliseconds it should take to spawn numberOfPieces. 93 | * @default 5000 94 | */ 95 | tweenDuration: number; 96 | /** 97 | * Function to draw your own confetti shapes. 98 | */ 99 | drawShape?: (context: CanvasRenderingContext2D) => void; 100 | /** 101 | * Function called when all confetti has fallen off-canvas. 102 | */ 103 | onConfettiComplete?: (confettiInstance?: Confetti) => void; 104 | } 105 | 106 | export const confettiDefaults: Pick> = { 107 | width: typeof window !== 'undefined' ? window.innerWidth : 300, 108 | height: typeof window !== 'undefined' ? window.innerHeight : 200, 109 | numberOfPieces: 200, 110 | friction: 0.99, 111 | wind: 0, 112 | gravity: 0.1, 113 | initialVelocityX: 4, 114 | initialVelocityY: 10, 115 | colors: [ 116 | '#f44336', 117 | '#e91e63', 118 | '#9c27b0', 119 | '#673ab7', 120 | '#3f51b5', 121 | '#2196f3', 122 | '#03a9f4', 123 | '#00bcd4', 124 | '#009688', 125 | '#4CAF50', 126 | '#8BC34A', 127 | '#CDDC39', 128 | '#FFEB3B', 129 | '#FFC107', 130 | '#FF9800', 131 | '#FF5722', 132 | '#795548', 133 | ], 134 | opacity: 1.0, 135 | debug: false, 136 | tweenFunction: tweens.easeInOutQuad, 137 | tweenDuration: 5000, 138 | recycle: true, 139 | run: true, 140 | }; 141 | 142 | export class Confetti { 143 | constructor(canvas: HTMLCanvasElement, opts: Partial) { 144 | this.canvas = canvas; 145 | const ctx = this.canvas.getContext('2d'); 146 | if (!ctx) { 147 | throw new Error('Could not get canvas context'); 148 | } 149 | this.context = ctx; 150 | 151 | this.generator = new ParticleGeneratorClass(this.canvas, () => this.options as ConfettiOptions); 152 | this.options = opts; 153 | this.update(); 154 | } 155 | 156 | canvas: HTMLCanvasElement; 157 | 158 | context: CanvasRenderingContext2D; 159 | 160 | _options!: ConfettiOptions; 161 | 162 | generator: ParticleGeneratorClass; 163 | 164 | rafId?: number; 165 | 166 | get options(): Partial { 167 | return this._options; 168 | } 169 | 170 | set options(opts: Partial) { 171 | const lastRunState = this._options && this._options.run; 172 | const lastRecycleState = this._options && this._options.recycle; 173 | this.setOptionsWithDefaults(opts); 174 | if (this.generator) { 175 | Object.assign(this.generator, this.options.confettiSource); 176 | if (typeof opts.recycle === 'boolean' && opts.recycle && lastRecycleState === false) { 177 | this.generator.lastNumberOfPieces = this.generator.particles.length; 178 | } 179 | } 180 | if (typeof opts.run === 'boolean' && opts.run && lastRunState === false) { 181 | this.update(); 182 | } 183 | } 184 | 185 | setOptionsWithDefaults = (opts: Partial) => { 186 | const computedConfettiDefaults = { 187 | confettiSource: { 188 | x: 0, 189 | y: 0, 190 | w: this.canvas.width, 191 | h: 0, 192 | }, 193 | }; 194 | this._options = { 195 | ...computedConfettiDefaults, 196 | ...confettiDefaults, 197 | ...opts, 198 | }; 199 | Object.assign(this, opts.confettiSource); 200 | }; 201 | 202 | update = () => { 203 | const { 204 | options: { run, onConfettiComplete }, 205 | canvas, 206 | context, 207 | } = this; 208 | if (run) { 209 | context.fillStyle = 'white'; 210 | context.clearRect(0, 0, canvas.width, canvas.height); 211 | } 212 | if (this.generator.animate()) { 213 | this.rafId = requestAnimationFrame(this.update); 214 | } else { 215 | if (onConfettiComplete && typeof onConfettiComplete === 'function' && this.generator.particlesGenerated > 0) { 216 | onConfettiComplete.call(this, this); 217 | } 218 | this._options.run = false; 219 | } 220 | }; 221 | 222 | reset = () => { 223 | if (this.generator && this.generator.particlesGenerated > 0) { 224 | this.generator.particlesGenerated = 0; 225 | this.generator.particles = []; 226 | this.generator.lastNumberOfPieces = 0; 227 | } 228 | }; 229 | 230 | stop = () => { 231 | this.options = { run: false }; 232 | if (this.rafId) { 233 | cancelAnimationFrame(this.rafId); 234 | this.rafId = undefined; 235 | } 236 | }; 237 | } 238 | 239 | export default Confetti; 240 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This is a fork of [react-confetti](https://github.com/alampros/react-confetti) which was created when that library was unsupported and the latest release was completely broken. It is my understanding that the original library is once again actively maintained. This fork has cleaner builds, but is not as active. 2 | 3 | For live examples and demo's of this code, you can reference the [react-confetti live documentation](https://alampros.github.io/react-confetti/). 4 | 5 | # confetti-react 6 | 7 | Confetti without the cleanup. 8 | 9 | [![Build Status](https://travis-ci.org/rkuykendall/confetti-react.svg?branch=master)](https://travis-ci.org/rkuykendall/confetti-react) 10 | [![npm](https://img.shields.io/npm/v/confetti-react.svg)](https://www.npmjs.com/package/confetti-react) 11 | [![npm bundle size](https://img.shields.io/bundlephobia/min/confetti-react.svg)](https://bundlephobia.com/result?p=confetti-react) 12 | ![npm type definitions](https://img.shields.io/npm/types/confetti-react.svg) 13 | 14 | Based on a pen by @Gthibaud: https://codepen.io/Gthibaud/pen/ENzXbp 15 | 16 | [![demogif][2]][1] 17 | 18 | [1]: http://rkuykendall.github.com/confetti-react 19 | [2]: https://user-images.githubusercontent.com/796717/113315634-7d24f800-92db-11eb-9a01-4c83413a5756.png 'demo gif' 20 | 21 | ## Install 22 | 23 | ```sh 24 | npm install confetti-react 25 | ``` 26 | 27 | ## Use 28 | 29 | `width` and `height` props are recommended. They will default to the initial window dimensions, but will not respond to 30 | resize events. It is recommended to provide the dimensions yourself. Here is an example using 31 | [a hook](https://github.com/streamich/react-use/blob/master/docs/useWindowSize.md): 32 | 33 | ```jsx 34 | import React from 'react'; 35 | import useWindowSize from 'react-use/lib/useWindowSize'; 36 | import Confetti from 'confetti-react'; 37 | 38 | export default () => { 39 | const { width, height } = useWindowSize(); 40 | return ; 41 | }; 42 | ``` 43 | 44 | ## Props 45 | 46 | | Property | Type | Default | Description | 47 | | -------------------- | ---------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------- | 48 | | `width` | `Number` | `window.innerWidth \|\| 300` | Width of the `` element. | 49 | | `height` | `Number` | `window.innerHeight \|\| 200` | Height of the `` element. | 50 | | `numberOfPieces` | `Number` | 200 | Number of confetti pieces at one time. | 51 | | `confettiSource` | `{ x: Number, y: Number, w: Number, h: Number }` | `{x: 0, y: 0, w: canvas.width, h:0}` | Rectangle where the confetti should spawn. Default is across the top. | 52 | | `friction` | `Number` | 0.99 | | 53 | | `wind` | `Number` | 0 | | 54 | | `gravity` | `Number` | 0.1 | | 55 | | `initialVelocityX` | `Number` | 4 | How fast confetti is emitted horizontally | 56 | | `initialVelocityY` | `Number` | 10 | How fast confetti is emitted vertically | 57 | | `colors` | `String[]` | `['#f44336'`
`'#e91e63'`
`'#9c27b0'`
`'#673ab7'`
`'#3f51b5'`
`'#2196f3'`
`'#03a9f4'`
`'#00bcd4'`
`'#009688'`
`'#4CAF50'`
`'#8BC34A'`
`'#CDDC39'`
`'#FFEB3B'`
`'#FFC107'`
`'#FF9800'`
`'#FF5722'`
`'#795548']`
| All available Colors for the confetti pieces. | 58 | | `opacity` | `Number` | 1.0 | | 59 | | `recycle` | `Bool` | true | Keep spawning confetti after `numberOfPieces` pieces have been shown. | 60 | | `run` | `Bool` | true | Run the animation loop | 61 | | `tweenDuration` | `Number` | 5000 | How fast the confetti is added | 62 | | `tweenFunction` | `(currentTime: number, currentValue: number, targetValue: number, duration: number, s?: number) => number` | easeInOutQuad | See [tween-functions](https://github.com/chenglou/tween-functions) | 63 | | `drawShape` | `(context: CanvasRenderingContext2D) => void` | `undefined` | See below | 64 | | `onConfettiComplete` | `(confetti: Confetti) => void` | `undefined` | Called when all confetti has fallen off-canvas. | 65 | 66 | # `drawShape()` 67 | 68 | Draw a custom shape for a particle. If not provided, defaults to a random selection of a square, circle or strip 69 | confetto. The function is called with the canvas context as a parameter and the [Particle](src/Particle.ts) as the 70 | `this` context. 71 | 72 | For example, to draw all spirals: 73 | 74 | ```jsx 75 | { 77 | ctx.beginPath(); 78 | for (let i = 0; i < 22; i++) { 79 | const angle = 0.35 * i; 80 | const x = (0.2 + 1.5 * angle) * Math.cos(angle); 81 | const y = (0.2 + 1.5 * angle) * Math.sin(angle); 82 | ctx.lineTo(x, y); 83 | } 84 | ctx.stroke(); 85 | ctx.closePath(); 86 | }} 87 | /> 88 | ``` 89 | -------------------------------------------------------------------------------- /dist/confetti-react.esm.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import tweens from 'tween-functions'; 3 | 4 | function _defineProperties(target, props) { 5 | for (var i = 0; i < props.length; i++) { 6 | var descriptor = props[i]; 7 | descriptor.enumerable = descriptor.enumerable || false; 8 | descriptor.configurable = true; 9 | if ("value" in descriptor) descriptor.writable = true; 10 | Object.defineProperty(target, descriptor.key, descriptor); 11 | } 12 | } 13 | 14 | function _createClass(Constructor, protoProps, staticProps) { 15 | if (protoProps) _defineProperties(Constructor.prototype, protoProps); 16 | if (staticProps) _defineProperties(Constructor, staticProps); 17 | return Constructor; 18 | } 19 | 20 | function _extends() { 21 | _extends = Object.assign || function (target) { 22 | for (var i = 1; i < arguments.length; i++) { 23 | var source = arguments[i]; 24 | 25 | for (var key in source) { 26 | if (Object.prototype.hasOwnProperty.call(source, key)) { 27 | target[key] = source[key]; 28 | } 29 | } 30 | } 31 | 32 | return target; 33 | }; 34 | 35 | return _extends.apply(this, arguments); 36 | } 37 | 38 | function _inheritsLoose(subClass, superClass) { 39 | subClass.prototype = Object.create(superClass.prototype); 40 | subClass.prototype.constructor = subClass; 41 | 42 | _setPrototypeOf(subClass, superClass); 43 | } 44 | 45 | function _setPrototypeOf(o, p) { 46 | _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { 47 | o.__proto__ = p; 48 | return o; 49 | }; 50 | 51 | return _setPrototypeOf(o, p); 52 | } 53 | 54 | function degreesToRads(degrees) { 55 | return degrees * Math.PI / 180; 56 | } 57 | function randomRange(min, max) { 58 | return min + Math.random() * (max - min); 59 | } 60 | function randomInt(min, max) { 61 | return Math.floor(min + Math.random() * (max - min + 1)); 62 | } 63 | 64 | var ParticleShape; 65 | 66 | (function (ParticleShape) { 67 | ParticleShape[ParticleShape["Circle"] = 0] = "Circle"; 68 | ParticleShape[ParticleShape["Square"] = 1] = "Square"; 69 | ParticleShape[ParticleShape["Strip"] = 2] = "Strip"; 70 | })(ParticleShape || (ParticleShape = {})); 71 | 72 | var RotationDirection; 73 | 74 | (function (RotationDirection) { 75 | RotationDirection[RotationDirection["Positive"] = 1] = "Positive"; 76 | RotationDirection[RotationDirection["Negative"] = -1] = "Negative"; 77 | })(RotationDirection || (RotationDirection = {})); 78 | 79 | var Particle = /*#__PURE__*/function () { 80 | function Particle(context, getOptions, x, y) { 81 | this.getOptions = getOptions; 82 | 83 | var _this$getOptions = this.getOptions(), 84 | colors = _this$getOptions.colors, 85 | initialVelocityX = _this$getOptions.initialVelocityX, 86 | initialVelocityY = _this$getOptions.initialVelocityY; 87 | 88 | this.context = context; 89 | this.x = x; 90 | this.y = y; 91 | this.w = randomRange(5, 20); 92 | this.h = randomRange(5, 20); 93 | this.radius = randomRange(5, 10); 94 | this.vx = randomRange(-initialVelocityX, initialVelocityX); 95 | this.vy = randomRange(-initialVelocityY, 0); 96 | this.shape = randomInt(0, 2); 97 | this.angle = degreesToRads(randomRange(0, 360)); 98 | this.angularSpin = randomRange(-0.2, 0.2); 99 | this.color = colors[Math.floor(Math.random() * colors.length)]; 100 | this.rotateY = randomRange(0, 1); 101 | this.rotationDirection = randomRange(0, 1) ? RotationDirection.Positive : RotationDirection.Negative; 102 | } 103 | 104 | var _proto = Particle.prototype; 105 | 106 | _proto.update = function update() { 107 | var _this$getOptions2 = this.getOptions(), 108 | gravity = _this$getOptions2.gravity, 109 | wind = _this$getOptions2.wind, 110 | friction = _this$getOptions2.friction, 111 | opacity = _this$getOptions2.opacity, 112 | drawShape = _this$getOptions2.drawShape; 113 | 114 | this.x += this.vx; 115 | this.y += this.vy; 116 | this.vy += gravity; 117 | this.vx += wind; 118 | this.vx *= friction; 119 | this.vy *= friction; 120 | 121 | if (this.rotateY >= 1 && this.rotationDirection === RotationDirection.Positive) { 122 | this.rotationDirection = RotationDirection.Negative; 123 | } else if (this.rotateY <= -1 && this.rotationDirection === RotationDirection.Negative) { 124 | this.rotationDirection = RotationDirection.Positive; 125 | } 126 | 127 | var rotateDelta = 0.1 * this.rotationDirection; 128 | this.rotateY += rotateDelta; 129 | this.angle += this.angularSpin; 130 | this.context.save(); 131 | this.context.translate(this.x, this.y); 132 | this.context.rotate(this.angle); 133 | this.context.scale(1, this.rotateY); 134 | this.context.rotate(this.angle); 135 | this.context.beginPath(); 136 | this.context.fillStyle = this.color; 137 | this.context.strokeStyle = this.color; 138 | this.context.globalAlpha = opacity; 139 | this.context.lineCap = 'round'; 140 | this.context.lineWidth = 2; 141 | 142 | if (drawShape && typeof drawShape === 'function') { 143 | drawShape.call(this, this.context); 144 | } else { 145 | switch (this.shape) { 146 | case ParticleShape.Circle: 147 | { 148 | this.context.beginPath(); 149 | this.context.arc(0, 0, this.radius, 0, 2 * Math.PI); 150 | this.context.fill(); 151 | break; 152 | } 153 | 154 | case ParticleShape.Square: 155 | { 156 | this.context.fillRect(-this.w / 2, -this.h / 2, this.w, this.h); 157 | break; 158 | } 159 | 160 | case ParticleShape.Strip: 161 | { 162 | this.context.fillRect(-this.w / 6, -this.h / 2, this.w / 3, this.h); 163 | break; 164 | } 165 | 166 | default: 167 | { 168 | throw new Error('Unknown type in Particle.ts'); 169 | } 170 | } 171 | } 172 | 173 | this.context.closePath(); 174 | this.context.restore(); 175 | }; 176 | 177 | return Particle; 178 | }(); 179 | 180 | var ParticleGeneratorClass = function ParticleGeneratorClass(canvas, getOptions) { 181 | var _this = this; 182 | 183 | this.x = 0; 184 | this.y = 0; 185 | this.w = 0; 186 | this.h = 0; 187 | this.lastNumberOfPieces = 0; 188 | this.tweenInitTime = Date.now(); 189 | this.particles = []; 190 | this.particlesGenerated = 0; 191 | 192 | this.removeParticleAt = function (i) { 193 | _this.particles.splice(i, 1); 194 | }; 195 | 196 | this.getParticle = function () { 197 | var newParticleX = randomRange(_this.x, _this.w + _this.x); 198 | var newParticleY = randomRange(_this.y, _this.h + _this.y); 199 | return new Particle(_this.context, _this.getOptions, newParticleX, newParticleY); 200 | }; 201 | 202 | this.animate = function () { 203 | var canvas = _this.canvas, 204 | context = _this.context, 205 | particlesGenerated = _this.particlesGenerated, 206 | lastNumberOfPieces = _this.lastNumberOfPieces; 207 | 208 | var _this$getOptions = _this.getOptions(), 209 | run = _this$getOptions.run, 210 | recycle = _this$getOptions.recycle, 211 | numberOfPieces = _this$getOptions.numberOfPieces, 212 | debug = _this$getOptions.debug, 213 | tweenFunction = _this$getOptions.tweenFunction, 214 | tweenDuration = _this$getOptions.tweenDuration; 215 | 216 | if (!run) { 217 | return false; 218 | } 219 | 220 | var nP = _this.particles.length; 221 | var activeCount = recycle ? nP : particlesGenerated; 222 | var now = Date.now(); // Initial population 223 | 224 | if (activeCount < numberOfPieces) { 225 | // Use the numberOfPieces prop as a key to reset the easing timing 226 | if (lastNumberOfPieces !== numberOfPieces) { 227 | _this.tweenInitTime = now; 228 | _this.lastNumberOfPieces = numberOfPieces; 229 | } 230 | 231 | var tweenInitTime = _this.tweenInitTime; // Add more than one piece per loop, otherwise the number of pieces would 232 | // be limitted by the RAF framerate 233 | 234 | var progressTime = now - tweenInitTime > tweenDuration ? tweenDuration : Math.max(0, now - tweenInitTime); 235 | var tweenedVal = tweenFunction(progressTime, activeCount, numberOfPieces, tweenDuration); 236 | var numToAdd = Math.round(tweenedVal - activeCount); 237 | 238 | for (var i = 0; i < numToAdd; i += 1) { 239 | _this.particles.push(_this.getParticle()); 240 | } 241 | 242 | _this.particlesGenerated += numToAdd; 243 | } 244 | 245 | if (debug) { 246 | // Draw debug text 247 | context.font = '12px sans-serif'; 248 | context.fillStyle = '#333'; 249 | context.textAlign = 'right'; 250 | context.fillText("Particles: " + nP, canvas.width - 10, canvas.height - 20); 251 | } // Maintain the population 252 | 253 | 254 | _this.particles.forEach(function (p, i) { 255 | // Update each particle's position 256 | p.update(); // Prune the off-canvas particles 257 | 258 | if (p.y > canvas.height || p.y < -100 || p.x > canvas.width + 100 || p.x < -100) { 259 | if (recycle && activeCount <= numberOfPieces) { 260 | // Replace the particle with a brand new one 261 | _this.particles[i] = _this.getParticle(); 262 | } else { 263 | _this.removeParticleAt(i); 264 | } 265 | } 266 | }); 267 | 268 | return nP > 0 || activeCount < numberOfPieces; 269 | }; 270 | 271 | this.canvas = canvas; 272 | var ctx = this.canvas.getContext('2d'); 273 | 274 | if (!ctx) { 275 | throw new Error('Could not get canvas context'); 276 | } 277 | 278 | this.context = ctx; 279 | this.getOptions = getOptions; 280 | }; 281 | 282 | var confettiDefaults = { 283 | width: typeof window !== 'undefined' ? window.innerWidth : 300, 284 | height: typeof window !== 'undefined' ? window.innerHeight : 200, 285 | numberOfPieces: 200, 286 | friction: 0.99, 287 | wind: 0, 288 | gravity: 0.1, 289 | initialVelocityX: 4, 290 | initialVelocityY: 10, 291 | colors: ['#f44336', '#e91e63', '#9c27b0', '#673ab7', '#3f51b5', '#2196f3', '#03a9f4', '#00bcd4', '#009688', '#4CAF50', '#8BC34A', '#CDDC39', '#FFEB3B', '#FFC107', '#FF9800', '#FF5722', '#795548'], 292 | opacity: 1.0, 293 | debug: false, 294 | tweenFunction: tweens.easeInOutQuad, 295 | tweenDuration: 5000, 296 | recycle: true, 297 | run: true 298 | }; 299 | var Confetti = /*#__PURE__*/function () { 300 | function Confetti(canvas, opts) { 301 | var _this = this; 302 | 303 | this.setOptionsWithDefaults = function (opts) { 304 | var computedConfettiDefaults = { 305 | confettiSource: { 306 | x: 0, 307 | y: 0, 308 | w: _this.canvas.width, 309 | h: 0 310 | } 311 | }; 312 | _this._options = _extends({}, computedConfettiDefaults, confettiDefaults, opts); 313 | Object.assign(_this, opts.confettiSource); 314 | }; 315 | 316 | this.update = function () { 317 | var _this$options = _this.options, 318 | run = _this$options.run, 319 | onConfettiComplete = _this$options.onConfettiComplete, 320 | canvas = _this.canvas, 321 | context = _this.context; 322 | 323 | if (run) { 324 | context.fillStyle = 'white'; 325 | context.clearRect(0, 0, canvas.width, canvas.height); 326 | } 327 | 328 | if (_this.generator.animate()) { 329 | _this.rafId = requestAnimationFrame(_this.update); 330 | } else { 331 | if (onConfettiComplete && typeof onConfettiComplete === 'function' && _this.generator.particlesGenerated > 0) { 332 | onConfettiComplete.call(_this, _this); 333 | } 334 | 335 | _this._options.run = false; 336 | } 337 | }; 338 | 339 | this.reset = function () { 340 | if (_this.generator && _this.generator.particlesGenerated > 0) { 341 | _this.generator.particlesGenerated = 0; 342 | _this.generator.particles = []; 343 | _this.generator.lastNumberOfPieces = 0; 344 | } 345 | }; 346 | 347 | this.stop = function () { 348 | _this.options = { 349 | run: false 350 | }; 351 | 352 | if (_this.rafId) { 353 | cancelAnimationFrame(_this.rafId); 354 | _this.rafId = undefined; 355 | } 356 | }; 357 | 358 | this.canvas = canvas; 359 | var ctx = this.canvas.getContext('2d'); 360 | 361 | if (!ctx) { 362 | throw new Error('Could not get canvas context'); 363 | } 364 | 365 | this.context = ctx; 366 | this.generator = new ParticleGeneratorClass(this.canvas, function () { 367 | return _this.options; 368 | }); 369 | this.options = opts; 370 | this.update(); 371 | } 372 | 373 | _createClass(Confetti, [{ 374 | key: "options", 375 | get: function get() { 376 | return this._options; 377 | }, 378 | set: function set(opts) { 379 | var lastRunState = this._options && this._options.run; 380 | var lastRecycleState = this._options && this._options.recycle; 381 | this.setOptionsWithDefaults(opts); 382 | 383 | if (this.generator) { 384 | Object.assign(this.generator, this.options.confettiSource); 385 | 386 | if (typeof opts.recycle === 'boolean' && opts.recycle && lastRecycleState === false) { 387 | this.generator.lastNumberOfPieces = this.generator.particles.length; 388 | } 389 | } 390 | 391 | if (typeof opts.run === 'boolean' && opts.run && lastRunState === false) { 392 | this.update(); 393 | } 394 | } 395 | }]); 396 | 397 | return Confetti; 398 | }(); 399 | 400 | var ref = /*#__PURE__*/React.createRef(); 401 | 402 | function extractCanvasProps(props) { 403 | var confettiOptions = {}; 404 | var refs = {}; 405 | var rest = {}; 406 | var confettiOptionKeys = [].concat(Object.keys(confettiDefaults), ['confettiSource', 'drawShape', 'onConfettiComplete']); 407 | var refProps = ['canvasRef']; 408 | Object.keys(props).forEach(function (prop) { 409 | var val = props[prop]; 410 | 411 | if (confettiOptionKeys.includes(prop)) { 412 | confettiOptions[prop] = val; 413 | } else if (refProps.includes(prop)) { 414 | refProps[prop] = val; 415 | } else { 416 | rest[prop] = val; 417 | } 418 | }); 419 | return [confettiOptions, rest, refs]; 420 | } 421 | 422 | var ConfettiReactInternal = /*#__PURE__*/function (_Component) { 423 | _inheritsLoose(ConfettiReactInternal, _Component); 424 | 425 | function ConfettiReactInternal(props) { 426 | var _this; 427 | 428 | _this = _Component.call(this, props) || this; 429 | _this.canvas = React.createRef(); 430 | _this.canvas = props.canvasRef || ref; 431 | return _this; 432 | } 433 | 434 | var _proto = ConfettiReactInternal.prototype; 435 | 436 | _proto.componentDidMount = function componentDidMount() { 437 | if (this.canvas.current) { 438 | var opts = extractCanvasProps(this.props)[0]; 439 | this.confetti = new Confetti(this.canvas.current, opts); 440 | } 441 | }; 442 | 443 | _proto.componentDidUpdate = function componentDidUpdate() { 444 | var confettiOptions = extractCanvasProps(this.props)[0]; 445 | 446 | if (this.confetti) { 447 | this.confetti.options = confettiOptions; 448 | } 449 | }; 450 | 451 | _proto.componentWillUnmount = function componentWillUnmount() { 452 | if (this.confetti) { 453 | this.confetti.stop(); 454 | } 455 | 456 | this.confetti = undefined; 457 | }; 458 | 459 | _proto.render = function render() { 460 | var _extractCanvasProps = extractCanvasProps(this.props), 461 | confettiOptions = _extractCanvasProps[0], 462 | passedProps = _extractCanvasProps[1]; 463 | 464 | var canvasStyles = _extends({ 465 | zIndex: 2, 466 | position: 'absolute', 467 | pointerEvents: 'none', 468 | top: 0, 469 | left: 0, 470 | bottom: 0, 471 | right: 0 472 | }, passedProps.style); 473 | 474 | return React.createElement("canvas", Object.assign({ 475 | width: confettiOptions.width, 476 | height: confettiOptions.height, 477 | ref: this.canvas 478 | }, passedProps, { 479 | style: canvasStyles 480 | })); 481 | }; 482 | 483 | return ConfettiReactInternal; 484 | }(Component); 485 | 486 | ConfettiReactInternal.defaultProps = /*#__PURE__*/_extends({}, confettiDefaults); 487 | ConfettiReactInternal.displayName = 'ConfettiReact'; // eslint-disable-next-line react/display-name 488 | 489 | var Index = /*#__PURE__*/React.forwardRef(function (props, _ref) { 490 | return React.createElement(ConfettiReactInternal, Object.assign({ 491 | canvasRef: ref 492 | }, props)); 493 | }); 494 | 495 | export default Index; 496 | export { Index }; 497 | //# sourceMappingURL=confetti-react.esm.js.map 498 | -------------------------------------------------------------------------------- /dist/confetti-react.cjs.development.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, '__esModule', { value: true }); 4 | 5 | function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } 6 | 7 | var React = require('react'); 8 | var React__default = _interopDefault(React); 9 | var tweens = _interopDefault(require('tween-functions')); 10 | 11 | function _defineProperties(target, props) { 12 | for (var i = 0; i < props.length; i++) { 13 | var descriptor = props[i]; 14 | descriptor.enumerable = descriptor.enumerable || false; 15 | descriptor.configurable = true; 16 | if ("value" in descriptor) descriptor.writable = true; 17 | Object.defineProperty(target, descriptor.key, descriptor); 18 | } 19 | } 20 | 21 | function _createClass(Constructor, protoProps, staticProps) { 22 | if (protoProps) _defineProperties(Constructor.prototype, protoProps); 23 | if (staticProps) _defineProperties(Constructor, staticProps); 24 | return Constructor; 25 | } 26 | 27 | function _extends() { 28 | _extends = Object.assign || function (target) { 29 | for (var i = 1; i < arguments.length; i++) { 30 | var source = arguments[i]; 31 | 32 | for (var key in source) { 33 | if (Object.prototype.hasOwnProperty.call(source, key)) { 34 | target[key] = source[key]; 35 | } 36 | } 37 | } 38 | 39 | return target; 40 | }; 41 | 42 | return _extends.apply(this, arguments); 43 | } 44 | 45 | function _inheritsLoose(subClass, superClass) { 46 | subClass.prototype = Object.create(superClass.prototype); 47 | subClass.prototype.constructor = subClass; 48 | 49 | _setPrototypeOf(subClass, superClass); 50 | } 51 | 52 | function _setPrototypeOf(o, p) { 53 | _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { 54 | o.__proto__ = p; 55 | return o; 56 | }; 57 | 58 | return _setPrototypeOf(o, p); 59 | } 60 | 61 | function degreesToRads(degrees) { 62 | return degrees * Math.PI / 180; 63 | } 64 | function randomRange(min, max) { 65 | return min + Math.random() * (max - min); 66 | } 67 | function randomInt(min, max) { 68 | return Math.floor(min + Math.random() * (max - min + 1)); 69 | } 70 | 71 | var ParticleShape; 72 | 73 | (function (ParticleShape) { 74 | ParticleShape[ParticleShape["Circle"] = 0] = "Circle"; 75 | ParticleShape[ParticleShape["Square"] = 1] = "Square"; 76 | ParticleShape[ParticleShape["Strip"] = 2] = "Strip"; 77 | })(ParticleShape || (ParticleShape = {})); 78 | 79 | var RotationDirection; 80 | 81 | (function (RotationDirection) { 82 | RotationDirection[RotationDirection["Positive"] = 1] = "Positive"; 83 | RotationDirection[RotationDirection["Negative"] = -1] = "Negative"; 84 | })(RotationDirection || (RotationDirection = {})); 85 | 86 | var Particle = /*#__PURE__*/function () { 87 | function Particle(context, getOptions, x, y) { 88 | this.getOptions = getOptions; 89 | 90 | var _this$getOptions = this.getOptions(), 91 | colors = _this$getOptions.colors, 92 | initialVelocityX = _this$getOptions.initialVelocityX, 93 | initialVelocityY = _this$getOptions.initialVelocityY; 94 | 95 | this.context = context; 96 | this.x = x; 97 | this.y = y; 98 | this.w = randomRange(5, 20); 99 | this.h = randomRange(5, 20); 100 | this.radius = randomRange(5, 10); 101 | this.vx = randomRange(-initialVelocityX, initialVelocityX); 102 | this.vy = randomRange(-initialVelocityY, 0); 103 | this.shape = randomInt(0, 2); 104 | this.angle = degreesToRads(randomRange(0, 360)); 105 | this.angularSpin = randomRange(-0.2, 0.2); 106 | this.color = colors[Math.floor(Math.random() * colors.length)]; 107 | this.rotateY = randomRange(0, 1); 108 | this.rotationDirection = randomRange(0, 1) ? RotationDirection.Positive : RotationDirection.Negative; 109 | } 110 | 111 | var _proto = Particle.prototype; 112 | 113 | _proto.update = function update() { 114 | var _this$getOptions2 = this.getOptions(), 115 | gravity = _this$getOptions2.gravity, 116 | wind = _this$getOptions2.wind, 117 | friction = _this$getOptions2.friction, 118 | opacity = _this$getOptions2.opacity, 119 | drawShape = _this$getOptions2.drawShape; 120 | 121 | this.x += this.vx; 122 | this.y += this.vy; 123 | this.vy += gravity; 124 | this.vx += wind; 125 | this.vx *= friction; 126 | this.vy *= friction; 127 | 128 | if (this.rotateY >= 1 && this.rotationDirection === RotationDirection.Positive) { 129 | this.rotationDirection = RotationDirection.Negative; 130 | } else if (this.rotateY <= -1 && this.rotationDirection === RotationDirection.Negative) { 131 | this.rotationDirection = RotationDirection.Positive; 132 | } 133 | 134 | var rotateDelta = 0.1 * this.rotationDirection; 135 | this.rotateY += rotateDelta; 136 | this.angle += this.angularSpin; 137 | this.context.save(); 138 | this.context.translate(this.x, this.y); 139 | this.context.rotate(this.angle); 140 | this.context.scale(1, this.rotateY); 141 | this.context.rotate(this.angle); 142 | this.context.beginPath(); 143 | this.context.fillStyle = this.color; 144 | this.context.strokeStyle = this.color; 145 | this.context.globalAlpha = opacity; 146 | this.context.lineCap = 'round'; 147 | this.context.lineWidth = 2; 148 | 149 | if (drawShape && typeof drawShape === 'function') { 150 | drawShape.call(this, this.context); 151 | } else { 152 | switch (this.shape) { 153 | case ParticleShape.Circle: 154 | { 155 | this.context.beginPath(); 156 | this.context.arc(0, 0, this.radius, 0, 2 * Math.PI); 157 | this.context.fill(); 158 | break; 159 | } 160 | 161 | case ParticleShape.Square: 162 | { 163 | this.context.fillRect(-this.w / 2, -this.h / 2, this.w, this.h); 164 | break; 165 | } 166 | 167 | case ParticleShape.Strip: 168 | { 169 | this.context.fillRect(-this.w / 6, -this.h / 2, this.w / 3, this.h); 170 | break; 171 | } 172 | 173 | default: 174 | { 175 | throw new Error('Unknown type in Particle.ts'); 176 | } 177 | } 178 | } 179 | 180 | this.context.closePath(); 181 | this.context.restore(); 182 | }; 183 | 184 | return Particle; 185 | }(); 186 | 187 | var ParticleGeneratorClass = function ParticleGeneratorClass(canvas, getOptions) { 188 | var _this = this; 189 | 190 | this.x = 0; 191 | this.y = 0; 192 | this.w = 0; 193 | this.h = 0; 194 | this.lastNumberOfPieces = 0; 195 | this.tweenInitTime = Date.now(); 196 | this.particles = []; 197 | this.particlesGenerated = 0; 198 | 199 | this.removeParticleAt = function (i) { 200 | _this.particles.splice(i, 1); 201 | }; 202 | 203 | this.getParticle = function () { 204 | var newParticleX = randomRange(_this.x, _this.w + _this.x); 205 | var newParticleY = randomRange(_this.y, _this.h + _this.y); 206 | return new Particle(_this.context, _this.getOptions, newParticleX, newParticleY); 207 | }; 208 | 209 | this.animate = function () { 210 | var canvas = _this.canvas, 211 | context = _this.context, 212 | particlesGenerated = _this.particlesGenerated, 213 | lastNumberOfPieces = _this.lastNumberOfPieces; 214 | 215 | var _this$getOptions = _this.getOptions(), 216 | run = _this$getOptions.run, 217 | recycle = _this$getOptions.recycle, 218 | numberOfPieces = _this$getOptions.numberOfPieces, 219 | debug = _this$getOptions.debug, 220 | tweenFunction = _this$getOptions.tweenFunction, 221 | tweenDuration = _this$getOptions.tweenDuration; 222 | 223 | if (!run) { 224 | return false; 225 | } 226 | 227 | var nP = _this.particles.length; 228 | var activeCount = recycle ? nP : particlesGenerated; 229 | var now = Date.now(); // Initial population 230 | 231 | if (activeCount < numberOfPieces) { 232 | // Use the numberOfPieces prop as a key to reset the easing timing 233 | if (lastNumberOfPieces !== numberOfPieces) { 234 | _this.tweenInitTime = now; 235 | _this.lastNumberOfPieces = numberOfPieces; 236 | } 237 | 238 | var tweenInitTime = _this.tweenInitTime; // Add more than one piece per loop, otherwise the number of pieces would 239 | // be limitted by the RAF framerate 240 | 241 | var progressTime = now - tweenInitTime > tweenDuration ? tweenDuration : Math.max(0, now - tweenInitTime); 242 | var tweenedVal = tweenFunction(progressTime, activeCount, numberOfPieces, tweenDuration); 243 | var numToAdd = Math.round(tweenedVal - activeCount); 244 | 245 | for (var i = 0; i < numToAdd; i += 1) { 246 | _this.particles.push(_this.getParticle()); 247 | } 248 | 249 | _this.particlesGenerated += numToAdd; 250 | } 251 | 252 | if (debug) { 253 | // Draw debug text 254 | context.font = '12px sans-serif'; 255 | context.fillStyle = '#333'; 256 | context.textAlign = 'right'; 257 | context.fillText("Particles: " + nP, canvas.width - 10, canvas.height - 20); 258 | } // Maintain the population 259 | 260 | 261 | _this.particles.forEach(function (p, i) { 262 | // Update each particle's position 263 | p.update(); // Prune the off-canvas particles 264 | 265 | if (p.y > canvas.height || p.y < -100 || p.x > canvas.width + 100 || p.x < -100) { 266 | if (recycle && activeCount <= numberOfPieces) { 267 | // Replace the particle with a brand new one 268 | _this.particles[i] = _this.getParticle(); 269 | } else { 270 | _this.removeParticleAt(i); 271 | } 272 | } 273 | }); 274 | 275 | return nP > 0 || activeCount < numberOfPieces; 276 | }; 277 | 278 | this.canvas = canvas; 279 | var ctx = this.canvas.getContext('2d'); 280 | 281 | if (!ctx) { 282 | throw new Error('Could not get canvas context'); 283 | } 284 | 285 | this.context = ctx; 286 | this.getOptions = getOptions; 287 | }; 288 | 289 | var confettiDefaults = { 290 | width: typeof window !== 'undefined' ? window.innerWidth : 300, 291 | height: typeof window !== 'undefined' ? window.innerHeight : 200, 292 | numberOfPieces: 200, 293 | friction: 0.99, 294 | wind: 0, 295 | gravity: 0.1, 296 | initialVelocityX: 4, 297 | initialVelocityY: 10, 298 | colors: ['#f44336', '#e91e63', '#9c27b0', '#673ab7', '#3f51b5', '#2196f3', '#03a9f4', '#00bcd4', '#009688', '#4CAF50', '#8BC34A', '#CDDC39', '#FFEB3B', '#FFC107', '#FF9800', '#FF5722', '#795548'], 299 | opacity: 1.0, 300 | debug: false, 301 | tweenFunction: tweens.easeInOutQuad, 302 | tweenDuration: 5000, 303 | recycle: true, 304 | run: true 305 | }; 306 | var Confetti = /*#__PURE__*/function () { 307 | function Confetti(canvas, opts) { 308 | var _this = this; 309 | 310 | this.setOptionsWithDefaults = function (opts) { 311 | var computedConfettiDefaults = { 312 | confettiSource: { 313 | x: 0, 314 | y: 0, 315 | w: _this.canvas.width, 316 | h: 0 317 | } 318 | }; 319 | _this._options = _extends({}, computedConfettiDefaults, confettiDefaults, opts); 320 | Object.assign(_this, opts.confettiSource); 321 | }; 322 | 323 | this.update = function () { 324 | var _this$options = _this.options, 325 | run = _this$options.run, 326 | onConfettiComplete = _this$options.onConfettiComplete, 327 | canvas = _this.canvas, 328 | context = _this.context; 329 | 330 | if (run) { 331 | context.fillStyle = 'white'; 332 | context.clearRect(0, 0, canvas.width, canvas.height); 333 | } 334 | 335 | if (_this.generator.animate()) { 336 | _this.rafId = requestAnimationFrame(_this.update); 337 | } else { 338 | if (onConfettiComplete && typeof onConfettiComplete === 'function' && _this.generator.particlesGenerated > 0) { 339 | onConfettiComplete.call(_this, _this); 340 | } 341 | 342 | _this._options.run = false; 343 | } 344 | }; 345 | 346 | this.reset = function () { 347 | if (_this.generator && _this.generator.particlesGenerated > 0) { 348 | _this.generator.particlesGenerated = 0; 349 | _this.generator.particles = []; 350 | _this.generator.lastNumberOfPieces = 0; 351 | } 352 | }; 353 | 354 | this.stop = function () { 355 | _this.options = { 356 | run: false 357 | }; 358 | 359 | if (_this.rafId) { 360 | cancelAnimationFrame(_this.rafId); 361 | _this.rafId = undefined; 362 | } 363 | }; 364 | 365 | this.canvas = canvas; 366 | var ctx = this.canvas.getContext('2d'); 367 | 368 | if (!ctx) { 369 | throw new Error('Could not get canvas context'); 370 | } 371 | 372 | this.context = ctx; 373 | this.generator = new ParticleGeneratorClass(this.canvas, function () { 374 | return _this.options; 375 | }); 376 | this.options = opts; 377 | this.update(); 378 | } 379 | 380 | _createClass(Confetti, [{ 381 | key: "options", 382 | get: function get() { 383 | return this._options; 384 | }, 385 | set: function set(opts) { 386 | var lastRunState = this._options && this._options.run; 387 | var lastRecycleState = this._options && this._options.recycle; 388 | this.setOptionsWithDefaults(opts); 389 | 390 | if (this.generator) { 391 | Object.assign(this.generator, this.options.confettiSource); 392 | 393 | if (typeof opts.recycle === 'boolean' && opts.recycle && lastRecycleState === false) { 394 | this.generator.lastNumberOfPieces = this.generator.particles.length; 395 | } 396 | } 397 | 398 | if (typeof opts.run === 'boolean' && opts.run && lastRunState === false) { 399 | this.update(); 400 | } 401 | } 402 | }]); 403 | 404 | return Confetti; 405 | }(); 406 | 407 | var ref = /*#__PURE__*/React__default.createRef(); 408 | 409 | function extractCanvasProps(props) { 410 | var confettiOptions = {}; 411 | var refs = {}; 412 | var rest = {}; 413 | var confettiOptionKeys = [].concat(Object.keys(confettiDefaults), ['confettiSource', 'drawShape', 'onConfettiComplete']); 414 | var refProps = ['canvasRef']; 415 | Object.keys(props).forEach(function (prop) { 416 | var val = props[prop]; 417 | 418 | if (confettiOptionKeys.includes(prop)) { 419 | confettiOptions[prop] = val; 420 | } else if (refProps.includes(prop)) { 421 | refProps[prop] = val; 422 | } else { 423 | rest[prop] = val; 424 | } 425 | }); 426 | return [confettiOptions, rest, refs]; 427 | } 428 | 429 | var ConfettiReactInternal = /*#__PURE__*/function (_Component) { 430 | _inheritsLoose(ConfettiReactInternal, _Component); 431 | 432 | function ConfettiReactInternal(props) { 433 | var _this; 434 | 435 | _this = _Component.call(this, props) || this; 436 | _this.canvas = React__default.createRef(); 437 | _this.canvas = props.canvasRef || ref; 438 | return _this; 439 | } 440 | 441 | var _proto = ConfettiReactInternal.prototype; 442 | 443 | _proto.componentDidMount = function componentDidMount() { 444 | if (this.canvas.current) { 445 | var opts = extractCanvasProps(this.props)[0]; 446 | this.confetti = new Confetti(this.canvas.current, opts); 447 | } 448 | }; 449 | 450 | _proto.componentDidUpdate = function componentDidUpdate() { 451 | var confettiOptions = extractCanvasProps(this.props)[0]; 452 | 453 | if (this.confetti) { 454 | this.confetti.options = confettiOptions; 455 | } 456 | }; 457 | 458 | _proto.componentWillUnmount = function componentWillUnmount() { 459 | if (this.confetti) { 460 | this.confetti.stop(); 461 | } 462 | 463 | this.confetti = undefined; 464 | }; 465 | 466 | _proto.render = function render() { 467 | var _extractCanvasProps = extractCanvasProps(this.props), 468 | confettiOptions = _extractCanvasProps[0], 469 | passedProps = _extractCanvasProps[1]; 470 | 471 | var canvasStyles = _extends({ 472 | zIndex: 2, 473 | position: 'absolute', 474 | pointerEvents: 'none', 475 | top: 0, 476 | left: 0, 477 | bottom: 0, 478 | right: 0 479 | }, passedProps.style); 480 | 481 | return React__default.createElement("canvas", Object.assign({ 482 | width: confettiOptions.width, 483 | height: confettiOptions.height, 484 | ref: this.canvas 485 | }, passedProps, { 486 | style: canvasStyles 487 | })); 488 | }; 489 | 490 | return ConfettiReactInternal; 491 | }(React.Component); 492 | 493 | ConfettiReactInternal.defaultProps = /*#__PURE__*/_extends({}, confettiDefaults); 494 | ConfettiReactInternal.displayName = 'ConfettiReact'; // eslint-disable-next-line react/display-name 495 | 496 | var Index = /*#__PURE__*/React__default.forwardRef(function (props, _ref) { 497 | return React__default.createElement(ConfettiReactInternal, Object.assign({ 498 | canvasRef: ref 499 | }, props)); 500 | }); 501 | 502 | exports.Index = Index; 503 | exports.default = Index; 504 | //# sourceMappingURL=confetti-react.cjs.development.js.map 505 | -------------------------------------------------------------------------------- /dist/confetti-react.cjs.production.min.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"confetti-react.cjs.production.min.js","sources":["../src/Particle.ts","../src/utils.ts","../src/ParticleGeneratorClass.ts","../src/Confetti.ts","../src/index.tsx"],"sourcesContent":["import { randomRange, randomInt, degreesToRads } from './utils';\nimport { ConfettiOptions } from './Confetti';\n\nexport enum ParticleShape {\n Circle = 0,\n Square,\n Strip,\n}\n\nenum RotationDirection {\n Positive = 1,\n Negative = -1,\n}\n\nexport default class Particle {\n constructor(context: CanvasRenderingContext2D, getOptions: () => ConfettiOptions, x: number, y: number) {\n this.getOptions = getOptions;\n const { colors, initialVelocityX, initialVelocityY } = this.getOptions();\n this.context = context;\n this.x = x;\n this.y = y;\n this.w = randomRange(5, 20);\n this.h = randomRange(5, 20);\n this.radius = randomRange(5, 10);\n this.vx = randomRange(-initialVelocityX, initialVelocityX);\n this.vy = randomRange(-initialVelocityY, 0);\n this.shape = randomInt(0, 2);\n this.angle = degreesToRads(randomRange(0, 360));\n this.angularSpin = randomRange(-0.2, 0.2);\n this.color = colors[Math.floor(Math.random() * colors.length)];\n this.rotateY = randomRange(0, 1);\n this.rotationDirection = randomRange(0, 1) ? RotationDirection.Positive : RotationDirection.Negative;\n }\n\n context: CanvasRenderingContext2D;\n\n radius: number;\n\n x: number;\n\n y: number;\n\n w: number;\n\n h: number;\n\n vx: number;\n\n vy: number;\n\n shape: ParticleShape;\n\n angle: number;\n\n angularSpin: number;\n\n color: string;\n\n // Actually used as scaleY to simulate rotation cheaply\n rotateY: number;\n\n rotationDirection: RotationDirection;\n\n getOptions: () => ConfettiOptions;\n\n update() {\n const { gravity, wind, friction, opacity, drawShape } = this.getOptions();\n this.x += this.vx;\n this.y += this.vy;\n this.vy += gravity;\n this.vx += wind;\n this.vx *= friction;\n this.vy *= friction;\n if (this.rotateY >= 1 && this.rotationDirection === RotationDirection.Positive) {\n this.rotationDirection = RotationDirection.Negative;\n } else if (this.rotateY <= -1 && this.rotationDirection === RotationDirection.Negative) {\n this.rotationDirection = RotationDirection.Positive;\n }\n\n const rotateDelta = 0.1 * this.rotationDirection;\n\n this.rotateY += rotateDelta;\n this.angle += this.angularSpin;\n this.context.save();\n this.context.translate(this.x, this.y);\n this.context.rotate(this.angle);\n this.context.scale(1, this.rotateY);\n this.context.rotate(this.angle);\n this.context.beginPath();\n this.context.fillStyle = this.color;\n this.context.strokeStyle = this.color;\n this.context.globalAlpha = opacity;\n this.context.lineCap = 'round';\n this.context.lineWidth = 2;\n if (drawShape && typeof drawShape === 'function') {\n drawShape.call(this, this.context);\n } else {\n switch (this.shape) {\n case ParticleShape.Circle: {\n this.context.beginPath();\n this.context.arc(0, 0, this.radius, 0, 2 * Math.PI);\n this.context.fill();\n break;\n }\n case ParticleShape.Square: {\n this.context.fillRect(-this.w / 2, -this.h / 2, this.w, this.h);\n break;\n }\n case ParticleShape.Strip: {\n this.context.fillRect(-this.w / 6, -this.h / 2, this.w / 3, this.h);\n break;\n }\n default: {\n throw new Error('Unknown type in Particle.ts');\n }\n }\n }\n this.context.closePath();\n this.context.restore();\n }\n}\n","import { Point } from './Point';\nimport { Rect } from './Rect';\nimport { Circle } from './CircleClass';\n\nexport function norm(value: number, min: number, max: number) {\n return (value - min) / (max - min);\n}\n\nexport function lerp(lnorm: number, min: number, max: number) {\n return (max - min) * lnorm + min;\n}\n\nexport function map(value: number, sourceMin: number, sourceMax: number, destMin: number, destMax: number) {\n return lerp(norm(value, sourceMin, sourceMax), destMin, destMax);\n}\n\nexport function clamp(value: number, min: number, max: number) {\n return Math.min(Math.max(value, Math.min(min, max)), Math.max(min, max));\n}\n\nexport function distance(p0: Point, p1: Point) {\n const dx = p1.x - p0.x;\n const dy = p1.y - p0.y;\n return Math.sqrt(dx * dx + dy * dy);\n}\n\nexport function distanceXY(x0: number, y0: number, x1: number, y1: number) {\n const dx = x1 - x0;\n const dy = y1 - y0;\n return Math.sqrt(dx * dx + dy * dy);\n}\n\nexport function circleCollision(c0: Circle, c1: Circle) {\n return distance(c0, c1) <= c0.radius + c1.radius;\n}\n\nexport function circlePointCollision(x: number, y: number, circle: Circle) {\n return distanceXY(x, y, circle.x, circle.y) < circle.radius;\n}\n\nexport function inRange(value: number, min: number, max: number) {\n return value >= Math.min(min, max) && value <= Math.max(min, max);\n}\n\nexport function pointInRect(p: Point, rect: Rect) {\n return inRange(p.x, rect.x, rect.x + rect.w) && inRange(p.y, rect.y, rect.y + rect.h);\n}\n\nexport function rangeIntersect(min0: number, max0: number, min1: number, max1: number) {\n return Math.max(min0, max0) >= Math.min(min1, max1) && Math.min(min0, max0) <= Math.max(min1, max1);\n}\n\nexport function rectIntersect(r0: Rect, r1: Rect) {\n return rangeIntersect(r0.x, r0.x + r0.w, r1.x, r1.x + r1.w) && rangeIntersect(r0.y, r0.y + r0.h, r1.y, r1.y + r1.h);\n}\n\nexport function degreesToRads(degrees: number) {\n return (degrees * Math.PI) / 180;\n}\n\nexport function radsToDegrees(radians: number) {\n return (radians * 180) / Math.PI;\n}\n\nexport function randomRange(min: number, max: number) {\n return min + Math.random() * (max - min);\n}\n\nexport function randomInt(min: number, max: number) {\n return Math.floor(min + Math.random() * (max - min + 1));\n}\n","import { ConfettiOptions } from './Confetti';\nimport { Rect } from './Rect';\nimport Particle from './Particle';\nimport { randomRange } from './utils';\n\nexport interface ParticleGenerator extends Rect {\n removeParticleAt: (index: number) => void;\n getParticle: () => void;\n animate: () => boolean;\n particles: Particle[];\n particlesGenerated: number;\n}\n\nexport default class ParticleGeneratorClass implements ParticleGenerator {\n constructor(canvas: HTMLCanvasElement, getOptions: () => ConfettiOptions) {\n this.canvas = canvas;\n const ctx = this.canvas.getContext('2d');\n if (!ctx) {\n throw new Error('Could not get canvas context');\n }\n this.context = ctx;\n this.getOptions = getOptions;\n }\n\n canvas: HTMLCanvasElement;\n\n context: CanvasRenderingContext2D;\n\n getOptions: () => ConfettiOptions;\n\n x = 0;\n\n y = 0;\n\n w = 0;\n\n h = 0;\n\n lastNumberOfPieces = 0;\n\n tweenInitTime: number = Date.now();\n\n particles: Particle[] = [];\n\n particlesGenerated = 0;\n\n removeParticleAt = (i: number) => {\n this.particles.splice(i, 1);\n };\n\n getParticle = () => {\n const newParticleX = randomRange(this.x, this.w + this.x);\n const newParticleY = randomRange(this.y, this.h + this.y);\n return new Particle(this.context, this.getOptions, newParticleX, newParticleY);\n };\n\n animate = (): boolean => {\n const { canvas, context, particlesGenerated, lastNumberOfPieces } = this;\n const { run, recycle, numberOfPieces, debug, tweenFunction, tweenDuration } = this.getOptions();\n if (!run) {\n return false;\n }\n\n const nP = this.particles.length;\n const activeCount = recycle ? nP : particlesGenerated;\n\n const now = Date.now();\n\n // Initial population\n if (activeCount < numberOfPieces) {\n // Use the numberOfPieces prop as a key to reset the easing timing\n if (lastNumberOfPieces !== numberOfPieces) {\n this.tweenInitTime = now;\n this.lastNumberOfPieces = numberOfPieces;\n }\n const { tweenInitTime } = this;\n // Add more than one piece per loop, otherwise the number of pieces would\n // be limitted by the RAF framerate\n const progressTime = now - tweenInitTime > tweenDuration ? tweenDuration : Math.max(0, now - tweenInitTime);\n const tweenedVal = tweenFunction(progressTime, activeCount, numberOfPieces, tweenDuration);\n const numToAdd = Math.round(tweenedVal - activeCount);\n for (let i = 0; i < numToAdd; i += 1) {\n this.particles.push(this.getParticle());\n }\n this.particlesGenerated += numToAdd;\n }\n if (debug) {\n // Draw debug text\n context.font = '12px sans-serif';\n context.fillStyle = '#333';\n context.textAlign = 'right';\n context.fillText(`Particles: ${nP}`, canvas.width - 10, canvas.height - 20);\n }\n\n // Maintain the population\n this.particles.forEach((p, i) => {\n // Update each particle's position\n p.update();\n // Prune the off-canvas particles\n if (p.y > canvas.height || p.y < -100 || p.x > canvas.width + 100 || p.x < -100) {\n if (recycle && activeCount <= numberOfPieces) {\n // Replace the particle with a brand new one\n this.particles[i] = this.getParticle();\n } else {\n this.removeParticleAt(i);\n }\n }\n });\n return nP > 0 || activeCount < numberOfPieces;\n };\n}\n","import tweens from 'tween-functions';\nimport { Rect } from './Rect';\nimport ParticleGeneratorClass from './ParticleGeneratorClass';\n\nexport interface ConfettiOptions {\n /**\n * Width of the component\n * @default window.width\n */\n width: number;\n /**\n * Height of the component\n * @default window.height\n */\n height: number;\n /**\n * Max number of confetti pieces to render.\n * @default 200\n */\n numberOfPieces: number;\n /**\n * Slows movement of pieces. (lower number = slower confetti)\n * @default 0.99\n */\n friction: number;\n /**\n * Blows confetti along the X axis.\n * @default 0\n */\n wind: number;\n /**\n * How fast it falls (pixels per frame)\n * @default 0.1\n */\n gravity: number;\n /**\n * How fast the confetti is emitted horizontally\n * @default 4\n */\n initialVelocityX: number;\n /**\n * How fast the confetti is emitted vertically\n * @default 10\n */\n initialVelocityY: number;\n /**\n * Array of colors to choose from.\n */\n colors: string[];\n /**\n * Opacity of the confetti.\n * @default 1\n */\n opacity: number;\n /**\n * If false, only numberOfPieces will be emitted and then stops. If true, when a confetto goes offscreen, a new one will be emitted.\n * @default true\n */\n recycle: boolean;\n /**\n * If false, stops the requestAnimationFrame loop.\n * @default true\n */\n run: boolean;\n /**\n * Renders some debug text on the canvas.\n * @default false\n */\n debug: boolean;\n /**\n * A Rect defining the area where the confetti will spawn.\n * @default {\n * x: 0,\n * y: 0,\n * w: canvas.width,\n * h: 0\n * }\n */\n confettiSource: Rect;\n /**\n * Controls the rate at which confetti is spawned.\n * @default easeInOutQuad\n */\n tweenFunction: (\n currentTime: number,\n currentValue: number,\n targetValue: number,\n duration: number,\n s?: number,\n ) => number;\n /**\n * Number of milliseconds it should take to spawn numberOfPieces.\n * @default 5000\n */\n tweenDuration: number;\n /**\n * Function to draw your own confetti shapes.\n */\n drawShape?: (context: CanvasRenderingContext2D) => void;\n /**\n * Function called when all confetti has fallen off-canvas.\n */\n onConfettiComplete?: (confettiInstance?: Confetti) => void;\n}\n\nexport const confettiDefaults: Pick> = {\n width: typeof window !== 'undefined' ? window.innerWidth : 300,\n height: typeof window !== 'undefined' ? window.innerHeight : 200,\n numberOfPieces: 200,\n friction: 0.99,\n wind: 0,\n gravity: 0.1,\n initialVelocityX: 4,\n initialVelocityY: 10,\n colors: [\n '#f44336',\n '#e91e63',\n '#9c27b0',\n '#673ab7',\n '#3f51b5',\n '#2196f3',\n '#03a9f4',\n '#00bcd4',\n '#009688',\n '#4CAF50',\n '#8BC34A',\n '#CDDC39',\n '#FFEB3B',\n '#FFC107',\n '#FF9800',\n '#FF5722',\n '#795548',\n ],\n opacity: 1.0,\n debug: false,\n tweenFunction: tweens.easeInOutQuad,\n tweenDuration: 5000,\n recycle: true,\n run: true,\n};\n\nexport class Confetti {\n constructor(canvas: HTMLCanvasElement, opts: Partial) {\n this.canvas = canvas;\n const ctx = this.canvas.getContext('2d');\n if (!ctx) {\n throw new Error('Could not get canvas context');\n }\n this.context = ctx;\n\n this.generator = new ParticleGeneratorClass(this.canvas, () => this.options as ConfettiOptions);\n this.options = opts;\n this.update();\n }\n\n canvas: HTMLCanvasElement;\n\n context: CanvasRenderingContext2D;\n\n _options!: ConfettiOptions;\n\n generator: ParticleGeneratorClass;\n\n rafId?: number;\n\n get options(): Partial {\n return this._options;\n }\n\n set options(opts: Partial) {\n const lastRunState = this._options && this._options.run;\n const lastRecycleState = this._options && this._options.recycle;\n this.setOptionsWithDefaults(opts);\n if (this.generator) {\n Object.assign(this.generator, this.options.confettiSource);\n if (typeof opts.recycle === 'boolean' && opts.recycle && lastRecycleState === false) {\n this.generator.lastNumberOfPieces = this.generator.particles.length;\n }\n }\n if (typeof opts.run === 'boolean' && opts.run && lastRunState === false) {\n this.update();\n }\n }\n\n setOptionsWithDefaults = (opts: Partial) => {\n const computedConfettiDefaults = {\n confettiSource: {\n x: 0,\n y: 0,\n w: this.canvas.width,\n h: 0,\n },\n };\n this._options = {\n ...computedConfettiDefaults,\n ...confettiDefaults,\n ...opts,\n };\n Object.assign(this, opts.confettiSource);\n };\n\n update = () => {\n const {\n options: { run, onConfettiComplete },\n canvas,\n context,\n } = this;\n if (run) {\n context.fillStyle = 'white';\n context.clearRect(0, 0, canvas.width, canvas.height);\n }\n if (this.generator.animate()) {\n this.rafId = requestAnimationFrame(this.update);\n } else {\n if (onConfettiComplete && typeof onConfettiComplete === 'function' && this.generator.particlesGenerated > 0) {\n onConfettiComplete.call(this, this);\n }\n this._options.run = false;\n }\n };\n\n reset = () => {\n if (this.generator && this.generator.particlesGenerated > 0) {\n this.generator.particlesGenerated = 0;\n this.generator.particles = [];\n this.generator.lastNumberOfPieces = 0;\n }\n };\n\n stop = () => {\n this.options = { run: false };\n if (this.rafId) {\n cancelAnimationFrame(this.rafId);\n this.rafId = undefined;\n }\n };\n}\n\nexport default Confetti;\n","import React, { Component, CanvasHTMLAttributes } from 'react';\nimport Confetti, { ConfettiOptions, confettiDefaults } from './Confetti';\n\nconst ref = React.createRef();\n\ninterface Refs {\n [key: string]: React.Ref;\n}\n\nfunction extractCanvasProps(\n props: Partial | any,\n): [Partial, Partial>, Refs] {\n const confettiOptions: Partial = {};\n const refs: Refs = {};\n const rest: any = {};\n const confettiOptionKeys = [...Object.keys(confettiDefaults), 'confettiSource', 'drawShape', 'onConfettiComplete'];\n const refProps = ['canvasRef'];\n Object.keys(props).forEach(prop => {\n const val = props[prop as string];\n if (confettiOptionKeys.includes(prop)) {\n confettiOptions[prop] = val;\n } else if (refProps.includes(prop)) {\n refProps[prop as any] = val;\n } else {\n rest[prop] = val;\n }\n });\n return [confettiOptions, rest, refs];\n}\n\nexport type Props = Partial &\n CanvasHTMLAttributes & {\n canvasRef?: React.Ref;\n };\n\nclass ConfettiReactInternal extends Component {\n static readonly defaultProps = {\n ...confettiDefaults,\n };\n\n static readonly displayName = 'ConfettiReact';\n\n canvas: React.RefObject = React.createRef();\n\n confetti?: Confetti;\n\n constructor(props: Props) {\n super(props);\n this.canvas = (props.canvasRef as React.RefObject) || ref;\n }\n\n componentDidMount() {\n if (this.canvas.current) {\n const opts = extractCanvasProps(this.props)[0];\n this.confetti = new Confetti(this.canvas.current, opts);\n }\n }\n\n componentDidUpdate() {\n const confettiOptions = extractCanvasProps(this.props)[0];\n if (this.confetti) {\n this.confetti.options = confettiOptions as ConfettiOptions;\n }\n }\n\n componentWillUnmount() {\n if (this.confetti) {\n this.confetti.stop();\n }\n this.confetti = undefined;\n }\n\n render() {\n const [confettiOptions, passedProps] = extractCanvasProps(this.props);\n const canvasStyles = {\n zIndex: 2,\n position: 'absolute' as 'absolute',\n pointerEvents: 'none' as 'none',\n top: 0,\n left: 0,\n bottom: 0,\n right: 0,\n ...passedProps.style,\n };\n return (\n \n );\n }\n}\n\n// eslint-disable-next-line react/display-name\nexport const Index = React.forwardRef((props, _ref) => (\n \n));\n\nexport default Index;\n"],"names":["ParticleShape","RotationDirection","randomRange","min","max","Math","random","Particle","context","getOptions","x","y","this","colors","initialVelocityX","initialVelocityY","w","h","radius","vx","vy","shape","floor","angle","PI","angularSpin","color","length","rotateY","rotationDirection","Positive","Negative","update","gravity","wind","friction","opacity","drawShape","save","translate","rotate","scale","beginPath","fillStyle","strokeStyle","globalAlpha","lineCap","lineWidth","call","Circle","arc","fill","Square","fillRect","Strip","Error","closePath","restore","ParticleGeneratorClass","canvas","Date","now","i","_this","particles","splice","newParticleX","newParticleY","particlesGenerated","lastNumberOfPieces","recycle","numberOfPieces","debug","tweenFunction","tweenDuration","run","nP","activeCount","tweenInitTime","tweenedVal","numToAdd","round","push","getParticle","font","textAlign","fillText","width","height","forEach","p","removeParticleAt","ctx","getContext","confettiDefaults","window","innerWidth","innerHeight","tweens","easeInOutQuad","Confetti","opts","_options","confettiSource","Object","assign","options","onConfettiComplete","clearRect","generator","animate","rafId","requestAnimationFrame","cancelAnimationFrame","undefined","lastRunState","lastRecycleState","setOptionsWithDefaults","ref","React","createRef","extractCanvasProps","props","confettiOptions","rest","confettiOptionKeys","keys","refProps","prop","val","includes","ConfettiReactInternal","canvasRef","componentDidMount","current","confetti","componentDidUpdate","componentWillUnmount","stop","render","passedProps","canvasStyles","zIndex","position","pointerEvents","top","left","bottom","right","style","Component","Index","forwardRef","_ref"],"mappings":"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"} -------------------------------------------------------------------------------- /dist/confetti-react.esm.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"confetti-react.esm.js","sources":["../src/utils.ts","../src/Particle.ts","../src/ParticleGeneratorClass.ts","../src/Confetti.ts","../src/index.tsx"],"sourcesContent":["import { Point } from './Point';\nimport { Rect } from './Rect';\nimport { Circle } from './CircleClass';\n\nexport function norm(value: number, min: number, max: number) {\n return (value - min) / (max - min);\n}\n\nexport function lerp(lnorm: number, min: number, max: number) {\n return (max - min) * lnorm + min;\n}\n\nexport function map(value: number, sourceMin: number, sourceMax: number, destMin: number, destMax: number) {\n return lerp(norm(value, sourceMin, sourceMax), destMin, destMax);\n}\n\nexport function clamp(value: number, min: number, max: number) {\n return Math.min(Math.max(value, Math.min(min, max)), Math.max(min, max));\n}\n\nexport function distance(p0: Point, p1: Point) {\n const dx = p1.x - p0.x;\n const dy = p1.y - p0.y;\n return Math.sqrt(dx * dx + dy * dy);\n}\n\nexport function distanceXY(x0: number, y0: number, x1: number, y1: number) {\n const dx = x1 - x0;\n const dy = y1 - y0;\n return Math.sqrt(dx * dx + dy * dy);\n}\n\nexport function circleCollision(c0: Circle, c1: Circle) {\n return distance(c0, c1) <= c0.radius + c1.radius;\n}\n\nexport function circlePointCollision(x: number, y: number, circle: Circle) {\n return distanceXY(x, y, circle.x, circle.y) < circle.radius;\n}\n\nexport function inRange(value: number, min: number, max: number) {\n return value >= Math.min(min, max) && value <= Math.max(min, max);\n}\n\nexport function pointInRect(p: Point, rect: Rect) {\n return inRange(p.x, rect.x, rect.x + rect.w) && inRange(p.y, rect.y, rect.y + rect.h);\n}\n\nexport function rangeIntersect(min0: number, max0: number, min1: number, max1: number) {\n return Math.max(min0, max0) >= Math.min(min1, max1) && Math.min(min0, max0) <= Math.max(min1, max1);\n}\n\nexport function rectIntersect(r0: Rect, r1: Rect) {\n return rangeIntersect(r0.x, r0.x + r0.w, r1.x, r1.x + r1.w) && rangeIntersect(r0.y, r0.y + r0.h, r1.y, r1.y + r1.h);\n}\n\nexport function degreesToRads(degrees: number) {\n return (degrees * Math.PI) / 180;\n}\n\nexport function radsToDegrees(radians: number) {\n return (radians * 180) / Math.PI;\n}\n\nexport function randomRange(min: number, max: number) {\n return min + Math.random() * (max - min);\n}\n\nexport function randomInt(min: number, max: number) {\n return Math.floor(min + Math.random() * (max - min + 1));\n}\n","import { randomRange, randomInt, degreesToRads } from './utils';\nimport { ConfettiOptions } from './Confetti';\n\nexport enum ParticleShape {\n Circle = 0,\n Square,\n Strip,\n}\n\nenum RotationDirection {\n Positive = 1,\n Negative = -1,\n}\n\nexport default class Particle {\n constructor(context: CanvasRenderingContext2D, getOptions: () => ConfettiOptions, x: number, y: number) {\n this.getOptions = getOptions;\n const { colors, initialVelocityX, initialVelocityY } = this.getOptions();\n this.context = context;\n this.x = x;\n this.y = y;\n this.w = randomRange(5, 20);\n this.h = randomRange(5, 20);\n this.radius = randomRange(5, 10);\n this.vx = randomRange(-initialVelocityX, initialVelocityX);\n this.vy = randomRange(-initialVelocityY, 0);\n this.shape = randomInt(0, 2);\n this.angle = degreesToRads(randomRange(0, 360));\n this.angularSpin = randomRange(-0.2, 0.2);\n this.color = colors[Math.floor(Math.random() * colors.length)];\n this.rotateY = randomRange(0, 1);\n this.rotationDirection = randomRange(0, 1) ? RotationDirection.Positive : RotationDirection.Negative;\n }\n\n context: CanvasRenderingContext2D;\n\n radius: number;\n\n x: number;\n\n y: number;\n\n w: number;\n\n h: number;\n\n vx: number;\n\n vy: number;\n\n shape: ParticleShape;\n\n angle: number;\n\n angularSpin: number;\n\n color: string;\n\n // Actually used as scaleY to simulate rotation cheaply\n rotateY: number;\n\n rotationDirection: RotationDirection;\n\n getOptions: () => ConfettiOptions;\n\n update() {\n const { gravity, wind, friction, opacity, drawShape } = this.getOptions();\n this.x += this.vx;\n this.y += this.vy;\n this.vy += gravity;\n this.vx += wind;\n this.vx *= friction;\n this.vy *= friction;\n if (this.rotateY >= 1 && this.rotationDirection === RotationDirection.Positive) {\n this.rotationDirection = RotationDirection.Negative;\n } else if (this.rotateY <= -1 && this.rotationDirection === RotationDirection.Negative) {\n this.rotationDirection = RotationDirection.Positive;\n }\n\n const rotateDelta = 0.1 * this.rotationDirection;\n\n this.rotateY += rotateDelta;\n this.angle += this.angularSpin;\n this.context.save();\n this.context.translate(this.x, this.y);\n this.context.rotate(this.angle);\n this.context.scale(1, this.rotateY);\n this.context.rotate(this.angle);\n this.context.beginPath();\n this.context.fillStyle = this.color;\n this.context.strokeStyle = this.color;\n this.context.globalAlpha = opacity;\n this.context.lineCap = 'round';\n this.context.lineWidth = 2;\n if (drawShape && typeof drawShape === 'function') {\n drawShape.call(this, this.context);\n } else {\n switch (this.shape) {\n case ParticleShape.Circle: {\n this.context.beginPath();\n this.context.arc(0, 0, this.radius, 0, 2 * Math.PI);\n this.context.fill();\n break;\n }\n case ParticleShape.Square: {\n this.context.fillRect(-this.w / 2, -this.h / 2, this.w, this.h);\n break;\n }\n case ParticleShape.Strip: {\n this.context.fillRect(-this.w / 6, -this.h / 2, this.w / 3, this.h);\n break;\n }\n default: {\n throw new Error('Unknown type in Particle.ts');\n }\n }\n }\n this.context.closePath();\n this.context.restore();\n }\n}\n","import { ConfettiOptions } from './Confetti';\nimport { Rect } from './Rect';\nimport Particle from './Particle';\nimport { randomRange } from './utils';\n\nexport interface ParticleGenerator extends Rect {\n removeParticleAt: (index: number) => void;\n getParticle: () => void;\n animate: () => boolean;\n particles: Particle[];\n particlesGenerated: number;\n}\n\nexport default class ParticleGeneratorClass implements ParticleGenerator {\n constructor(canvas: HTMLCanvasElement, getOptions: () => ConfettiOptions) {\n this.canvas = canvas;\n const ctx = this.canvas.getContext('2d');\n if (!ctx) {\n throw new Error('Could not get canvas context');\n }\n this.context = ctx;\n this.getOptions = getOptions;\n }\n\n canvas: HTMLCanvasElement;\n\n context: CanvasRenderingContext2D;\n\n getOptions: () => ConfettiOptions;\n\n x = 0;\n\n y = 0;\n\n w = 0;\n\n h = 0;\n\n lastNumberOfPieces = 0;\n\n tweenInitTime: number = Date.now();\n\n particles: Particle[] = [];\n\n particlesGenerated = 0;\n\n removeParticleAt = (i: number) => {\n this.particles.splice(i, 1);\n };\n\n getParticle = () => {\n const newParticleX = randomRange(this.x, this.w + this.x);\n const newParticleY = randomRange(this.y, this.h + this.y);\n return new Particle(this.context, this.getOptions, newParticleX, newParticleY);\n };\n\n animate = (): boolean => {\n const { canvas, context, particlesGenerated, lastNumberOfPieces } = this;\n const { run, recycle, numberOfPieces, debug, tweenFunction, tweenDuration } = this.getOptions();\n if (!run) {\n return false;\n }\n\n const nP = this.particles.length;\n const activeCount = recycle ? nP : particlesGenerated;\n\n const now = Date.now();\n\n // Initial population\n if (activeCount < numberOfPieces) {\n // Use the numberOfPieces prop as a key to reset the easing timing\n if (lastNumberOfPieces !== numberOfPieces) {\n this.tweenInitTime = now;\n this.lastNumberOfPieces = numberOfPieces;\n }\n const { tweenInitTime } = this;\n // Add more than one piece per loop, otherwise the number of pieces would\n // be limitted by the RAF framerate\n const progressTime = now - tweenInitTime > tweenDuration ? tweenDuration : Math.max(0, now - tweenInitTime);\n const tweenedVal = tweenFunction(progressTime, activeCount, numberOfPieces, tweenDuration);\n const numToAdd = Math.round(tweenedVal - activeCount);\n for (let i = 0; i < numToAdd; i += 1) {\n this.particles.push(this.getParticle());\n }\n this.particlesGenerated += numToAdd;\n }\n if (debug) {\n // Draw debug text\n context.font = '12px sans-serif';\n context.fillStyle = '#333';\n context.textAlign = 'right';\n context.fillText(`Particles: ${nP}`, canvas.width - 10, canvas.height - 20);\n }\n\n // Maintain the population\n this.particles.forEach((p, i) => {\n // Update each particle's position\n p.update();\n // Prune the off-canvas particles\n if (p.y > canvas.height || p.y < -100 || p.x > canvas.width + 100 || p.x < -100) {\n if (recycle && activeCount <= numberOfPieces) {\n // Replace the particle with a brand new one\n this.particles[i] = this.getParticle();\n } else {\n this.removeParticleAt(i);\n }\n }\n });\n return nP > 0 || activeCount < numberOfPieces;\n };\n}\n","import tweens from 'tween-functions';\nimport { Rect } from './Rect';\nimport ParticleGeneratorClass from './ParticleGeneratorClass';\n\nexport interface ConfettiOptions {\n /**\n * Width of the component\n * @default window.width\n */\n width: number;\n /**\n * Height of the component\n * @default window.height\n */\n height: number;\n /**\n * Max number of confetti pieces to render.\n * @default 200\n */\n numberOfPieces: number;\n /**\n * Slows movement of pieces. (lower number = slower confetti)\n * @default 0.99\n */\n friction: number;\n /**\n * Blows confetti along the X axis.\n * @default 0\n */\n wind: number;\n /**\n * How fast it falls (pixels per frame)\n * @default 0.1\n */\n gravity: number;\n /**\n * How fast the confetti is emitted horizontally\n * @default 4\n */\n initialVelocityX: number;\n /**\n * How fast the confetti is emitted vertically\n * @default 10\n */\n initialVelocityY: number;\n /**\n * Array of colors to choose from.\n */\n colors: string[];\n /**\n * Opacity of the confetti.\n * @default 1\n */\n opacity: number;\n /**\n * If false, only numberOfPieces will be emitted and then stops. If true, when a confetto goes offscreen, a new one will be emitted.\n * @default true\n */\n recycle: boolean;\n /**\n * If false, stops the requestAnimationFrame loop.\n * @default true\n */\n run: boolean;\n /**\n * Renders some debug text on the canvas.\n * @default false\n */\n debug: boolean;\n /**\n * A Rect defining the area where the confetti will spawn.\n * @default {\n * x: 0,\n * y: 0,\n * w: canvas.width,\n * h: 0\n * }\n */\n confettiSource: Rect;\n /**\n * Controls the rate at which confetti is spawned.\n * @default easeInOutQuad\n */\n tweenFunction: (\n currentTime: number,\n currentValue: number,\n targetValue: number,\n duration: number,\n s?: number,\n ) => number;\n /**\n * Number of milliseconds it should take to spawn numberOfPieces.\n * @default 5000\n */\n tweenDuration: number;\n /**\n * Function to draw your own confetti shapes.\n */\n drawShape?: (context: CanvasRenderingContext2D) => void;\n /**\n * Function called when all confetti has fallen off-canvas.\n */\n onConfettiComplete?: (confettiInstance?: Confetti) => void;\n}\n\nexport const confettiDefaults: Pick> = {\n width: typeof window !== 'undefined' ? window.innerWidth : 300,\n height: typeof window !== 'undefined' ? window.innerHeight : 200,\n numberOfPieces: 200,\n friction: 0.99,\n wind: 0,\n gravity: 0.1,\n initialVelocityX: 4,\n initialVelocityY: 10,\n colors: [\n '#f44336',\n '#e91e63',\n '#9c27b0',\n '#673ab7',\n '#3f51b5',\n '#2196f3',\n '#03a9f4',\n '#00bcd4',\n '#009688',\n '#4CAF50',\n '#8BC34A',\n '#CDDC39',\n '#FFEB3B',\n '#FFC107',\n '#FF9800',\n '#FF5722',\n '#795548',\n ],\n opacity: 1.0,\n debug: false,\n tweenFunction: tweens.easeInOutQuad,\n tweenDuration: 5000,\n recycle: true,\n run: true,\n};\n\nexport class Confetti {\n constructor(canvas: HTMLCanvasElement, opts: Partial) {\n this.canvas = canvas;\n const ctx = this.canvas.getContext('2d');\n if (!ctx) {\n throw new Error('Could not get canvas context');\n }\n this.context = ctx;\n\n this.generator = new ParticleGeneratorClass(this.canvas, () => this.options as ConfettiOptions);\n this.options = opts;\n this.update();\n }\n\n canvas: HTMLCanvasElement;\n\n context: CanvasRenderingContext2D;\n\n _options!: ConfettiOptions;\n\n generator: ParticleGeneratorClass;\n\n rafId?: number;\n\n get options(): Partial {\n return this._options;\n }\n\n set options(opts: Partial) {\n const lastRunState = this._options && this._options.run;\n const lastRecycleState = this._options && this._options.recycle;\n this.setOptionsWithDefaults(opts);\n if (this.generator) {\n Object.assign(this.generator, this.options.confettiSource);\n if (typeof opts.recycle === 'boolean' && opts.recycle && lastRecycleState === false) {\n this.generator.lastNumberOfPieces = this.generator.particles.length;\n }\n }\n if (typeof opts.run === 'boolean' && opts.run && lastRunState === false) {\n this.update();\n }\n }\n\n setOptionsWithDefaults = (opts: Partial) => {\n const computedConfettiDefaults = {\n confettiSource: {\n x: 0,\n y: 0,\n w: this.canvas.width,\n h: 0,\n },\n };\n this._options = {\n ...computedConfettiDefaults,\n ...confettiDefaults,\n ...opts,\n };\n Object.assign(this, opts.confettiSource);\n };\n\n update = () => {\n const {\n options: { run, onConfettiComplete },\n canvas,\n context,\n } = this;\n if (run) {\n context.fillStyle = 'white';\n context.clearRect(0, 0, canvas.width, canvas.height);\n }\n if (this.generator.animate()) {\n this.rafId = requestAnimationFrame(this.update);\n } else {\n if (onConfettiComplete && typeof onConfettiComplete === 'function' && this.generator.particlesGenerated > 0) {\n onConfettiComplete.call(this, this);\n }\n this._options.run = false;\n }\n };\n\n reset = () => {\n if (this.generator && this.generator.particlesGenerated > 0) {\n this.generator.particlesGenerated = 0;\n this.generator.particles = [];\n this.generator.lastNumberOfPieces = 0;\n }\n };\n\n stop = () => {\n this.options = { run: false };\n if (this.rafId) {\n cancelAnimationFrame(this.rafId);\n this.rafId = undefined;\n }\n };\n}\n\nexport default Confetti;\n","import React, { Component, CanvasHTMLAttributes } from 'react';\nimport Confetti, { ConfettiOptions, confettiDefaults } from './Confetti';\n\nconst ref = React.createRef();\n\ninterface Refs {\n [key: string]: React.Ref;\n}\n\nfunction extractCanvasProps(\n props: Partial | any,\n): [Partial, Partial>, Refs] {\n const confettiOptions: Partial = {};\n const refs: Refs = {};\n const rest: any = {};\n const confettiOptionKeys = [...Object.keys(confettiDefaults), 'confettiSource', 'drawShape', 'onConfettiComplete'];\n const refProps = ['canvasRef'];\n Object.keys(props).forEach(prop => {\n const val = props[prop as string];\n if (confettiOptionKeys.includes(prop)) {\n confettiOptions[prop] = val;\n } else if (refProps.includes(prop)) {\n refProps[prop as any] = val;\n } else {\n rest[prop] = val;\n }\n });\n return [confettiOptions, rest, refs];\n}\n\nexport type Props = Partial &\n CanvasHTMLAttributes & {\n canvasRef?: React.Ref;\n };\n\nclass ConfettiReactInternal extends Component {\n static readonly defaultProps = {\n ...confettiDefaults,\n };\n\n static readonly displayName = 'ConfettiReact';\n\n canvas: React.RefObject = React.createRef();\n\n confetti?: Confetti;\n\n constructor(props: Props) {\n super(props);\n this.canvas = (props.canvasRef as React.RefObject) || ref;\n }\n\n componentDidMount() {\n if (this.canvas.current) {\n const opts = extractCanvasProps(this.props)[0];\n this.confetti = new Confetti(this.canvas.current, opts);\n }\n }\n\n componentDidUpdate() {\n const confettiOptions = extractCanvasProps(this.props)[0];\n if (this.confetti) {\n this.confetti.options = confettiOptions as ConfettiOptions;\n }\n }\n\n componentWillUnmount() {\n if (this.confetti) {\n this.confetti.stop();\n }\n this.confetti = undefined;\n }\n\n render() {\n const [confettiOptions, passedProps] = extractCanvasProps(this.props);\n const canvasStyles = {\n zIndex: 2,\n position: 'absolute' as 'absolute',\n pointerEvents: 'none' as 'none',\n top: 0,\n left: 0,\n bottom: 0,\n right: 0,\n ...passedProps.style,\n };\n return (\n \n );\n }\n}\n\n// eslint-disable-next-line react/display-name\nexport const Index = React.forwardRef((props, _ref) => (\n \n));\n\nexport default Index;\n"],"names":["degreesToRads","degrees","Math","PI","randomRange","min","max","random","randomInt","floor","ParticleShape","RotationDirection","Particle","context","getOptions","x","y","colors","initialVelocityX","initialVelocityY","w","h","radius","vx","vy","shape","angle","angularSpin","color","length","rotateY","rotationDirection","Positive","Negative","update","gravity","wind","friction","opacity","drawShape","rotateDelta","save","translate","rotate","scale","beginPath","fillStyle","strokeStyle","globalAlpha","lineCap","lineWidth","call","Circle","arc","fill","Square","fillRect","Strip","Error","closePath","restore","ParticleGeneratorClass","canvas","Date","now","i","particles","splice","newParticleX","newParticleY","particlesGenerated","lastNumberOfPieces","run","recycle","numberOfPieces","debug","tweenFunction","tweenDuration","nP","activeCount","tweenInitTime","progressTime","tweenedVal","numToAdd","round","push","getParticle","font","textAlign","fillText","width","height","forEach","p","removeParticleAt","ctx","getContext","confettiDefaults","window","innerWidth","innerHeight","tweens","easeInOutQuad","Confetti","opts","computedConfettiDefaults","confettiSource","_options","Object","assign","options","onConfettiComplete","clearRect","generator","animate","rafId","requestAnimationFrame","cancelAnimationFrame","undefined","lastRunState","lastRecycleState","setOptionsWithDefaults","ref","React","createRef","extractCanvasProps","props","confettiOptions","refs","rest","confettiOptionKeys","keys","refProps","prop","val","includes","ConfettiReactInternal","canvasRef","componentDidMount","current","confetti","componentDidUpdate","componentWillUnmount","stop","render","passedProps","canvasStyles","zIndex","position","pointerEvents","top","left","bottom","right","style","Component","Index","forwardRef","_ref"],"mappings":"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"} -------------------------------------------------------------------------------- /dist/confetti-react.cjs.development.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"confetti-react.cjs.development.js","sources":["../src/utils.ts","../src/Particle.ts","../src/ParticleGeneratorClass.ts","../src/Confetti.ts","../src/index.tsx"],"sourcesContent":["import { Point } from './Point';\nimport { Rect } from './Rect';\nimport { Circle } from './CircleClass';\n\nexport function norm(value: number, min: number, max: number) {\n return (value - min) / (max - min);\n}\n\nexport function lerp(lnorm: number, min: number, max: number) {\n return (max - min) * lnorm + min;\n}\n\nexport function map(value: number, sourceMin: number, sourceMax: number, destMin: number, destMax: number) {\n return lerp(norm(value, sourceMin, sourceMax), destMin, destMax);\n}\n\nexport function clamp(value: number, min: number, max: number) {\n return Math.min(Math.max(value, Math.min(min, max)), Math.max(min, max));\n}\n\nexport function distance(p0: Point, p1: Point) {\n const dx = p1.x - p0.x;\n const dy = p1.y - p0.y;\n return Math.sqrt(dx * dx + dy * dy);\n}\n\nexport function distanceXY(x0: number, y0: number, x1: number, y1: number) {\n const dx = x1 - x0;\n const dy = y1 - y0;\n return Math.sqrt(dx * dx + dy * dy);\n}\n\nexport function circleCollision(c0: Circle, c1: Circle) {\n return distance(c0, c1) <= c0.radius + c1.radius;\n}\n\nexport function circlePointCollision(x: number, y: number, circle: Circle) {\n return distanceXY(x, y, circle.x, circle.y) < circle.radius;\n}\n\nexport function inRange(value: number, min: number, max: number) {\n return value >= Math.min(min, max) && value <= Math.max(min, max);\n}\n\nexport function pointInRect(p: Point, rect: Rect) {\n return inRange(p.x, rect.x, rect.x + rect.w) && inRange(p.y, rect.y, rect.y + rect.h);\n}\n\nexport function rangeIntersect(min0: number, max0: number, min1: number, max1: number) {\n return Math.max(min0, max0) >= Math.min(min1, max1) && Math.min(min0, max0) <= Math.max(min1, max1);\n}\n\nexport function rectIntersect(r0: Rect, r1: Rect) {\n return rangeIntersect(r0.x, r0.x + r0.w, r1.x, r1.x + r1.w) && rangeIntersect(r0.y, r0.y + r0.h, r1.y, r1.y + r1.h);\n}\n\nexport function degreesToRads(degrees: number) {\n return (degrees * Math.PI) / 180;\n}\n\nexport function radsToDegrees(radians: number) {\n return (radians * 180) / Math.PI;\n}\n\nexport function randomRange(min: number, max: number) {\n return min + Math.random() * (max - min);\n}\n\nexport function randomInt(min: number, max: number) {\n return Math.floor(min + Math.random() * (max - min + 1));\n}\n","import { randomRange, randomInt, degreesToRads } from './utils';\nimport { ConfettiOptions } from './Confetti';\n\nexport enum ParticleShape {\n Circle = 0,\n Square,\n Strip,\n}\n\nenum RotationDirection {\n Positive = 1,\n Negative = -1,\n}\n\nexport default class Particle {\n constructor(context: CanvasRenderingContext2D, getOptions: () => ConfettiOptions, x: number, y: number) {\n this.getOptions = getOptions;\n const { colors, initialVelocityX, initialVelocityY } = this.getOptions();\n this.context = context;\n this.x = x;\n this.y = y;\n this.w = randomRange(5, 20);\n this.h = randomRange(5, 20);\n this.radius = randomRange(5, 10);\n this.vx = randomRange(-initialVelocityX, initialVelocityX);\n this.vy = randomRange(-initialVelocityY, 0);\n this.shape = randomInt(0, 2);\n this.angle = degreesToRads(randomRange(0, 360));\n this.angularSpin = randomRange(-0.2, 0.2);\n this.color = colors[Math.floor(Math.random() * colors.length)];\n this.rotateY = randomRange(0, 1);\n this.rotationDirection = randomRange(0, 1) ? RotationDirection.Positive : RotationDirection.Negative;\n }\n\n context: CanvasRenderingContext2D;\n\n radius: number;\n\n x: number;\n\n y: number;\n\n w: number;\n\n h: number;\n\n vx: number;\n\n vy: number;\n\n shape: ParticleShape;\n\n angle: number;\n\n angularSpin: number;\n\n color: string;\n\n // Actually used as scaleY to simulate rotation cheaply\n rotateY: number;\n\n rotationDirection: RotationDirection;\n\n getOptions: () => ConfettiOptions;\n\n update() {\n const { gravity, wind, friction, opacity, drawShape } = this.getOptions();\n this.x += this.vx;\n this.y += this.vy;\n this.vy += gravity;\n this.vx += wind;\n this.vx *= friction;\n this.vy *= friction;\n if (this.rotateY >= 1 && this.rotationDirection === RotationDirection.Positive) {\n this.rotationDirection = RotationDirection.Negative;\n } else if (this.rotateY <= -1 && this.rotationDirection === RotationDirection.Negative) {\n this.rotationDirection = RotationDirection.Positive;\n }\n\n const rotateDelta = 0.1 * this.rotationDirection;\n\n this.rotateY += rotateDelta;\n this.angle += this.angularSpin;\n this.context.save();\n this.context.translate(this.x, this.y);\n this.context.rotate(this.angle);\n this.context.scale(1, this.rotateY);\n this.context.rotate(this.angle);\n this.context.beginPath();\n this.context.fillStyle = this.color;\n this.context.strokeStyle = this.color;\n this.context.globalAlpha = opacity;\n this.context.lineCap = 'round';\n this.context.lineWidth = 2;\n if (drawShape && typeof drawShape === 'function') {\n drawShape.call(this, this.context);\n } else {\n switch (this.shape) {\n case ParticleShape.Circle: {\n this.context.beginPath();\n this.context.arc(0, 0, this.radius, 0, 2 * Math.PI);\n this.context.fill();\n break;\n }\n case ParticleShape.Square: {\n this.context.fillRect(-this.w / 2, -this.h / 2, this.w, this.h);\n break;\n }\n case ParticleShape.Strip: {\n this.context.fillRect(-this.w / 6, -this.h / 2, this.w / 3, this.h);\n break;\n }\n default: {\n throw new Error('Unknown type in Particle.ts');\n }\n }\n }\n this.context.closePath();\n this.context.restore();\n }\n}\n","import { ConfettiOptions } from './Confetti';\nimport { Rect } from './Rect';\nimport Particle from './Particle';\nimport { randomRange } from './utils';\n\nexport interface ParticleGenerator extends Rect {\n removeParticleAt: (index: number) => void;\n getParticle: () => void;\n animate: () => boolean;\n particles: Particle[];\n particlesGenerated: number;\n}\n\nexport default class ParticleGeneratorClass implements ParticleGenerator {\n constructor(canvas: HTMLCanvasElement, getOptions: () => ConfettiOptions) {\n this.canvas = canvas;\n const ctx = this.canvas.getContext('2d');\n if (!ctx) {\n throw new Error('Could not get canvas context');\n }\n this.context = ctx;\n this.getOptions = getOptions;\n }\n\n canvas: HTMLCanvasElement;\n\n context: CanvasRenderingContext2D;\n\n getOptions: () => ConfettiOptions;\n\n x = 0;\n\n y = 0;\n\n w = 0;\n\n h = 0;\n\n lastNumberOfPieces = 0;\n\n tweenInitTime: number = Date.now();\n\n particles: Particle[] = [];\n\n particlesGenerated = 0;\n\n removeParticleAt = (i: number) => {\n this.particles.splice(i, 1);\n };\n\n getParticle = () => {\n const newParticleX = randomRange(this.x, this.w + this.x);\n const newParticleY = randomRange(this.y, this.h + this.y);\n return new Particle(this.context, this.getOptions, newParticleX, newParticleY);\n };\n\n animate = (): boolean => {\n const { canvas, context, particlesGenerated, lastNumberOfPieces } = this;\n const { run, recycle, numberOfPieces, debug, tweenFunction, tweenDuration } = this.getOptions();\n if (!run) {\n return false;\n }\n\n const nP = this.particles.length;\n const activeCount = recycle ? nP : particlesGenerated;\n\n const now = Date.now();\n\n // Initial population\n if (activeCount < numberOfPieces) {\n // Use the numberOfPieces prop as a key to reset the easing timing\n if (lastNumberOfPieces !== numberOfPieces) {\n this.tweenInitTime = now;\n this.lastNumberOfPieces = numberOfPieces;\n }\n const { tweenInitTime } = this;\n // Add more than one piece per loop, otherwise the number of pieces would\n // be limitted by the RAF framerate\n const progressTime = now - tweenInitTime > tweenDuration ? tweenDuration : Math.max(0, now - tweenInitTime);\n const tweenedVal = tweenFunction(progressTime, activeCount, numberOfPieces, tweenDuration);\n const numToAdd = Math.round(tweenedVal - activeCount);\n for (let i = 0; i < numToAdd; i += 1) {\n this.particles.push(this.getParticle());\n }\n this.particlesGenerated += numToAdd;\n }\n if (debug) {\n // Draw debug text\n context.font = '12px sans-serif';\n context.fillStyle = '#333';\n context.textAlign = 'right';\n context.fillText(`Particles: ${nP}`, canvas.width - 10, canvas.height - 20);\n }\n\n // Maintain the population\n this.particles.forEach((p, i) => {\n // Update each particle's position\n p.update();\n // Prune the off-canvas particles\n if (p.y > canvas.height || p.y < -100 || p.x > canvas.width + 100 || p.x < -100) {\n if (recycle && activeCount <= numberOfPieces) {\n // Replace the particle with a brand new one\n this.particles[i] = this.getParticle();\n } else {\n this.removeParticleAt(i);\n }\n }\n });\n return nP > 0 || activeCount < numberOfPieces;\n };\n}\n","import tweens from 'tween-functions';\nimport { Rect } from './Rect';\nimport ParticleGeneratorClass from './ParticleGeneratorClass';\n\nexport interface ConfettiOptions {\n /**\n * Width of the component\n * @default window.width\n */\n width: number;\n /**\n * Height of the component\n * @default window.height\n */\n height: number;\n /**\n * Max number of confetti pieces to render.\n * @default 200\n */\n numberOfPieces: number;\n /**\n * Slows movement of pieces. (lower number = slower confetti)\n * @default 0.99\n */\n friction: number;\n /**\n * Blows confetti along the X axis.\n * @default 0\n */\n wind: number;\n /**\n * How fast it falls (pixels per frame)\n * @default 0.1\n */\n gravity: number;\n /**\n * How fast the confetti is emitted horizontally\n * @default 4\n */\n initialVelocityX: number;\n /**\n * How fast the confetti is emitted vertically\n * @default 10\n */\n initialVelocityY: number;\n /**\n * Array of colors to choose from.\n */\n colors: string[];\n /**\n * Opacity of the confetti.\n * @default 1\n */\n opacity: number;\n /**\n * If false, only numberOfPieces will be emitted and then stops. If true, when a confetto goes offscreen, a new one will be emitted.\n * @default true\n */\n recycle: boolean;\n /**\n * If false, stops the requestAnimationFrame loop.\n * @default true\n */\n run: boolean;\n /**\n * Renders some debug text on the canvas.\n * @default false\n */\n debug: boolean;\n /**\n * A Rect defining the area where the confetti will spawn.\n * @default {\n * x: 0,\n * y: 0,\n * w: canvas.width,\n * h: 0\n * }\n */\n confettiSource: Rect;\n /**\n * Controls the rate at which confetti is spawned.\n * @default easeInOutQuad\n */\n tweenFunction: (\n currentTime: number,\n currentValue: number,\n targetValue: number,\n duration: number,\n s?: number,\n ) => number;\n /**\n * Number of milliseconds it should take to spawn numberOfPieces.\n * @default 5000\n */\n tweenDuration: number;\n /**\n * Function to draw your own confetti shapes.\n */\n drawShape?: (context: CanvasRenderingContext2D) => void;\n /**\n * Function called when all confetti has fallen off-canvas.\n */\n onConfettiComplete?: (confettiInstance?: Confetti) => void;\n}\n\nexport const confettiDefaults: Pick> = {\n width: typeof window !== 'undefined' ? window.innerWidth : 300,\n height: typeof window !== 'undefined' ? window.innerHeight : 200,\n numberOfPieces: 200,\n friction: 0.99,\n wind: 0,\n gravity: 0.1,\n initialVelocityX: 4,\n initialVelocityY: 10,\n colors: [\n '#f44336',\n '#e91e63',\n '#9c27b0',\n '#673ab7',\n '#3f51b5',\n '#2196f3',\n '#03a9f4',\n '#00bcd4',\n '#009688',\n '#4CAF50',\n '#8BC34A',\n '#CDDC39',\n '#FFEB3B',\n '#FFC107',\n '#FF9800',\n '#FF5722',\n '#795548',\n ],\n opacity: 1.0,\n debug: false,\n tweenFunction: tweens.easeInOutQuad,\n tweenDuration: 5000,\n recycle: true,\n run: true,\n};\n\nexport class Confetti {\n constructor(canvas: HTMLCanvasElement, opts: Partial) {\n this.canvas = canvas;\n const ctx = this.canvas.getContext('2d');\n if (!ctx) {\n throw new Error('Could not get canvas context');\n }\n this.context = ctx;\n\n this.generator = new ParticleGeneratorClass(this.canvas, () => this.options as ConfettiOptions);\n this.options = opts;\n this.update();\n }\n\n canvas: HTMLCanvasElement;\n\n context: CanvasRenderingContext2D;\n\n _options!: ConfettiOptions;\n\n generator: ParticleGeneratorClass;\n\n rafId?: number;\n\n get options(): Partial {\n return this._options;\n }\n\n set options(opts: Partial) {\n const lastRunState = this._options && this._options.run;\n const lastRecycleState = this._options && this._options.recycle;\n this.setOptionsWithDefaults(opts);\n if (this.generator) {\n Object.assign(this.generator, this.options.confettiSource);\n if (typeof opts.recycle === 'boolean' && opts.recycle && lastRecycleState === false) {\n this.generator.lastNumberOfPieces = this.generator.particles.length;\n }\n }\n if (typeof opts.run === 'boolean' && opts.run && lastRunState === false) {\n this.update();\n }\n }\n\n setOptionsWithDefaults = (opts: Partial) => {\n const computedConfettiDefaults = {\n confettiSource: {\n x: 0,\n y: 0,\n w: this.canvas.width,\n h: 0,\n },\n };\n this._options = {\n ...computedConfettiDefaults,\n ...confettiDefaults,\n ...opts,\n };\n Object.assign(this, opts.confettiSource);\n };\n\n update = () => {\n const {\n options: { run, onConfettiComplete },\n canvas,\n context,\n } = this;\n if (run) {\n context.fillStyle = 'white';\n context.clearRect(0, 0, canvas.width, canvas.height);\n }\n if (this.generator.animate()) {\n this.rafId = requestAnimationFrame(this.update);\n } else {\n if (onConfettiComplete && typeof onConfettiComplete === 'function' && this.generator.particlesGenerated > 0) {\n onConfettiComplete.call(this, this);\n }\n this._options.run = false;\n }\n };\n\n reset = () => {\n if (this.generator && this.generator.particlesGenerated > 0) {\n this.generator.particlesGenerated = 0;\n this.generator.particles = [];\n this.generator.lastNumberOfPieces = 0;\n }\n };\n\n stop = () => {\n this.options = { run: false };\n if (this.rafId) {\n cancelAnimationFrame(this.rafId);\n this.rafId = undefined;\n }\n };\n}\n\nexport default Confetti;\n","import React, { Component, CanvasHTMLAttributes } from 'react';\nimport Confetti, { ConfettiOptions, confettiDefaults } from './Confetti';\n\nconst ref = React.createRef();\n\ninterface Refs {\n [key: string]: React.Ref;\n}\n\nfunction extractCanvasProps(\n props: Partial | any,\n): [Partial, Partial>, Refs] {\n const confettiOptions: Partial = {};\n const refs: Refs = {};\n const rest: any = {};\n const confettiOptionKeys = [...Object.keys(confettiDefaults), 'confettiSource', 'drawShape', 'onConfettiComplete'];\n const refProps = ['canvasRef'];\n Object.keys(props).forEach(prop => {\n const val = props[prop as string];\n if (confettiOptionKeys.includes(prop)) {\n confettiOptions[prop] = val;\n } else if (refProps.includes(prop)) {\n refProps[prop as any] = val;\n } else {\n rest[prop] = val;\n }\n });\n return [confettiOptions, rest, refs];\n}\n\nexport type Props = Partial &\n CanvasHTMLAttributes & {\n canvasRef?: React.Ref;\n };\n\nclass ConfettiReactInternal extends Component {\n static readonly defaultProps = {\n ...confettiDefaults,\n };\n\n static readonly displayName = 'ConfettiReact';\n\n canvas: React.RefObject = React.createRef();\n\n confetti?: Confetti;\n\n constructor(props: Props) {\n super(props);\n this.canvas = (props.canvasRef as React.RefObject) || ref;\n }\n\n componentDidMount() {\n if (this.canvas.current) {\n const opts = extractCanvasProps(this.props)[0];\n this.confetti = new Confetti(this.canvas.current, opts);\n }\n }\n\n componentDidUpdate() {\n const confettiOptions = extractCanvasProps(this.props)[0];\n if (this.confetti) {\n this.confetti.options = confettiOptions as ConfettiOptions;\n }\n }\n\n componentWillUnmount() {\n if (this.confetti) {\n this.confetti.stop();\n }\n this.confetti = undefined;\n }\n\n render() {\n const [confettiOptions, passedProps] = extractCanvasProps(this.props);\n const canvasStyles = {\n zIndex: 2,\n position: 'absolute' as 'absolute',\n pointerEvents: 'none' as 'none',\n top: 0,\n left: 0,\n bottom: 0,\n right: 0,\n ...passedProps.style,\n };\n return (\n \n );\n }\n}\n\n// eslint-disable-next-line react/display-name\nexport const Index = React.forwardRef((props, _ref) => (\n \n));\n\nexport default Index;\n"],"names":["degreesToRads","degrees","Math","PI","randomRange","min","max","random","randomInt","floor","ParticleShape","RotationDirection","Particle","context","getOptions","x","y","colors","initialVelocityX","initialVelocityY","w","h","radius","vx","vy","shape","angle","angularSpin","color","length","rotateY","rotationDirection","Positive","Negative","update","gravity","wind","friction","opacity","drawShape","rotateDelta","save","translate","rotate","scale","beginPath","fillStyle","strokeStyle","globalAlpha","lineCap","lineWidth","call","Circle","arc","fill","Square","fillRect","Strip","Error","closePath","restore","ParticleGeneratorClass","canvas","Date","now","i","particles","splice","newParticleX","newParticleY","particlesGenerated","lastNumberOfPieces","run","recycle","numberOfPieces","debug","tweenFunction","tweenDuration","nP","activeCount","tweenInitTime","progressTime","tweenedVal","numToAdd","round","push","getParticle","font","textAlign","fillText","width","height","forEach","p","removeParticleAt","ctx","getContext","confettiDefaults","window","innerWidth","innerHeight","tweens","easeInOutQuad","Confetti","opts","computedConfettiDefaults","confettiSource","_options","Object","assign","options","onConfettiComplete","clearRect","generator","animate","rafId","requestAnimationFrame","cancelAnimationFrame","undefined","lastRunState","lastRecycleState","setOptionsWithDefaults","ref","React","createRef","extractCanvasProps","props","confettiOptions","refs","rest","confettiOptionKeys","keys","refProps","prop","val","includes","ConfettiReactInternal","canvasRef","componentDidMount","current","confetti","componentDidUpdate","componentWillUnmount","stop","render","passedProps","canvasStyles","zIndex","position","pointerEvents","top","left","bottom","right","style","Component","Index","forwardRef","_ref"],"mappings":"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"} --------------------------------------------------------------------------------