From 8af89f28e68a0ae1796d66c6c9a4021e8e470108 Mon Sep 17 00:00:00 2001 From: Aaron Lampros Date: Wed, 17 Apr 2019 11:30:02 -0400 Subject: [PATCH] feat: Add onConfettiComplete param With stories. --- .storybook/addons.js | 1 + .storybook/config.js | 1 + README.md | 33 +++++++++++++++++---------------- src/Confetti.ts | 20 ++++++++++++++++++++ src/ReactConfetti.tsx | 2 +- stories/index.story.jsx | 2 ++ stories/party.story.jsx | 22 ++++++++++++++++------ 7 files changed, 58 insertions(+), 23 deletions(-) diff --git a/.storybook/addons.js b/.storybook/addons.js index d2b044e..13879ba 100644 --- a/.storybook/addons.js +++ b/.storybook/addons.js @@ -1,2 +1,3 @@ import '@storybook/addon-knobs/register' import '@storybook/addon-storysource/register' +import '@storybook/addon-actions/register' diff --git a/.storybook/config.js b/.storybook/config.js index d476c29..db46f84 100644 --- a/.storybook/config.js +++ b/.storybook/config.js @@ -2,6 +2,7 @@ import { addParameters, configure } from '@storybook/react' import { create } from '@storybook/theming' addParameters({ options: { + sortStoriesByKind: true, theme: create({ base: 'light', brandTitle: 'React Confetti', diff --git a/README.md b/README.md index 857a949..9bbf72c 100644 --- a/README.md +++ b/README.md @@ -43,22 +43,23 @@ export default () => { ## Props -| Property | Type | Default | Description | -| ---------------- | --------------------- | --- | --- | -| `width` | `Number` | `window.innerWidth \|\| 300` | Width of the `` element. | -| `height` | `Number` | `window.innerHeight \|\| 200` | Height of the `` element. | -| `numberOfPieces` | `Number` | 200 | Number of confetti pieces at one time. | -| `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. | -| `friction` | `Number` | 0.99 | | -| `wind` | `Number` | 0 | | -| `gravity` | `Number` | 0.1 | | -| `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. | -| `opacity` | `Number` | 1.0 | | -| `recycle` | `Bool` | true | Keep spawning confetti after `numberOfPieces` pieces have been shown. | -| `run` | `Bool` | true | Run the animation loop | -| `tweenDuration` | `Number` | 5000 | How fast the confetti is added | -| `tweenFunction` | `(currentTime: number, currentValue: number, targetValue: number, duration: number, s?: number) => number` | easeInOutQuad | See [tween-functions](https://github.com/chenglou/tween-functions) | -| `drawShape` | `(context: CanvasRenderingContext2D) => void` | `undefined` | See below +| Property | Type | Default | Description | +| ---------------- | --------------------- | --- | --- | +| `width` | `Number` | `window.innerWidth \|\| 300` | Width of the `` element. | +| `height` | `Number` | `window.innerHeight \|\| 200` | Height of the `` element. | +| `numberOfPieces` | `Number` | 200 | Number of confetti pieces at one time. | +| `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. | +| `friction` | `Number` | 0.99 | | +| `wind` | `Number` | 0 | | +| `gravity` | `Number` | 0.1 | | +| `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. | +| `opacity` | `Number` | 1.0 | | +| `recycle` | `Bool` | true | Keep spawning confetti after `numberOfPieces` pieces have been shown. | +| `run` | `Bool` | true | Run the animation loop | +| `tweenDuration` | `Number` | 5000 | How fast the confetti is added | +| `tweenFunction` | `(currentTime: number, currentValue: number, targetValue: number, duration: number, s?: number) => number` | easeInOutQuad | See [tween-functions](https://github.com/chenglou/tween-functions) | +| `drawShape` | `(context: CanvasRenderingContext2D) => void` | `undefined` | See below | +| `onConfettiComplete` | `(confetti: Confetti) => void` | `undefined` | Called when all confetti has fallen off-canvas. | diff --git a/src/Confetti.ts b/src/Confetti.ts index b47868f..1bf50c4 100644 --- a/src/Confetti.ts +++ b/src/Confetti.ts @@ -81,6 +81,10 @@ export interface IConfettiOptions { * Function to draw your own confetti shapes. */ drawShape?: (context: CanvasRenderingContext2D) => void + /** + * Function called when all confetti has fallen off-canvas. + */ + onConfettiComplete?: (confettiInstance?: Confetti) => void } export const confettiDefaults: Pick> = { @@ -128,9 +132,13 @@ export class Confetti { } set options(opts: Partial) { const lastRunState = this._options && this._options.run + const lastRecycleState = this._options && this._options.recycle this.setOptionsWithDefaults(opts) if(this.generator) { Object.assign(this.generator, this.options.confettiSource) + if(typeof opts.recycle === 'boolean' && opts.recycle && lastRecycleState === false) { + this.generator.lastNumberOfPieces = this.generator.particles.length + } } if(typeof opts.run === 'boolean' && opts.run && lastRunState === false) { this.update() @@ -154,6 +162,7 @@ export class Confetti { const { options: { run, + onConfettiComplete, }, canvas, context, @@ -165,10 +174,21 @@ export class Confetti { if(this.generator.animate()) { this.rafId = requestAnimationFrame(this.update) } else { + if(onConfettiComplete && typeof onConfettiComplete === 'function' && this.generator.particlesGenerated > 0) { + onConfettiComplete.call(this, this) + } this._options.run = false } } + reset = () => { + if(this.generator && this.generator.particlesGenerated > 0) { + this.generator.particlesGenerated = 0 + this.generator.particles = [] + this.generator.lastNumberOfPieces = 0 + } + } + stop = () => { this.options = { run: false } if(this.rafId) { diff --git a/src/ReactConfetti.tsx b/src/ReactConfetti.tsx index e393035..73c1235 100644 --- a/src/ReactConfetti.tsx +++ b/src/ReactConfetti.tsx @@ -56,7 +56,7 @@ export class ReactConfetti extends Component { function extractCanvasProps(props: Partial | any): [Partial, Partial>] { const confettiOptions: Partial = {} const rest: any = {} - const confettiOptionKeys = [...Object.keys(confettiDefaults), 'confettiSource', 'drawShape'] + const confettiOptionKeys = [...Object.keys(confettiDefaults), 'confettiSource', 'drawShape', 'onConfettiComplete'] for(const prop in props) { const val = props[prop as string] if(confettiOptionKeys.includes(prop)) { diff --git a/stories/index.story.jsx b/stories/index.story.jsx index 3c03474..c83db86 100644 --- a/stories/index.story.jsx +++ b/stories/index.story.jsx @@ -4,6 +4,7 @@ import SizedConfetti from './SizedConfetti' import { withKnobs, boolean, number } from '@storybook/addon-knobs' import { storiesOf } from '@storybook/react' import { withInfo } from '@storybook/addon-info' +import { action } from '@storybook/addon-actions' import ReactConfetti from '../src/ReactConfetti' @@ -54,6 +55,7 @@ storiesOf('Props|Demos', module) max: 100, step: 1, }) / 100} + onConfettiComplete={action('Confetti Complete')} /> )) .add('Custom Source', () => ( diff --git a/stories/party.story.jsx b/stories/party.story.jsx index 0ce510c..30dba1e 100644 --- a/stories/party.story.jsx +++ b/stories/party.story.jsx @@ -1,21 +1,31 @@ import React, { useState } from 'react' import { storiesOf } from '@storybook/react' +import { action } from '@storybook/addon-actions' import SizedConfetti from './SizedConfetti' import './party.css' -const PartyMode = ({ children }) => { +const confettiCompleteAction = action('Confetti Complete') + +const PartyMode = () => { const [party, setParty] = useState(false) - const handleClick = () => { - setParty(!party) - } return (
{ + confettiCompleteAction() + setParty(false) + confetti.reset() + }} />
-
@@ -23,7 +33,7 @@ const PartyMode = ({ children }) => { ) } -storiesOf('Props Demos', module) +storiesOf('Props|Demos', module) .add('Party', () => ( ))