Skip to content

Commit

Permalink
feat: use easing function for adding confetti
Browse files Browse the repository at this point in the history
  • Loading branch information
alampros committed Mar 8, 2019
1 parent 3c9e108 commit 829a560
Show file tree
Hide file tree
Showing 7 changed files with 111 additions and 74 deletions.
28 changes: 15 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,17 +43,19 @@ export default () => {

## Props

| Property | Type | Default | Description |
| ---------------- | --------------------- | --- | --- |
| `width` | `Number` | `window.innerWidth \|\| 300` | Width of the `<canvas>` element. |
| `height` | `Number` | `window.innerHeight \|\| 200` | Height of the `<canvas>` 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` | `Array` of `String` | `['#f44336'`</br>`'#e91e63'`</br>`'#9c27b0'`</br>`'#673ab7'`</br>`'#3f51b5'`</br>`'#2196f3'`</br>`'#03a9f4'`</br>`'#00bcd4'`</br>`'#009688'`</br>`'#4CAF50'`</br>`'#8BC34A'`</br>`'#CDDC39'`</br>`'#FFEB3B'`</br>`'#FFC107'`</br>`'#FF9800'`</br>`'#FF5722'`</br>`'#795548']`</br> | 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 |
| Property | Type | Default | Description |
| ---------------- | --------------------- | --- | --- |
| `width` | `Number` | `window.innerWidth \|\| 300` | Width of the `<canvas>` element. |
| `height` | `Number` | `window.innerHeight \|\| 200` | Height of the `<canvas>` 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'`</br>`'#e91e63'`</br>`'#9c27b0'`</br>`'#673ab7'`</br>`'#3f51b5'`</br>`'#2196f3'`</br>`'#03a9f4'`</br>`'#00bcd4'`</br>`'#009688'`</br>`'#4CAF50'`</br>`'#8BC34A'`</br>`'#CDDC39'`</br>`'#FFEB3B'`</br>`'#FFC107'`</br>`'#FF9800'`</br>`'#FF5722'`</br>`'#795548']`</br> | 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) |

3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@
"prop-types": "^15.6.0",
"react": "0.14.x || ^15.0.1 || ^16.2.0"
},
"dependencies": {
"tween-functions": "^1.2.0"
},
"devDependencies": {
"@babel/core": "^7.1.6",
"@babel/plugin-proposal-class-properties": "^7.3.4",
Expand Down
5 changes: 5 additions & 0 deletions src/Confetti.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import tweens from 'tween-functions'
import { IRect } from './Rect'
import ParticleGenerator from './ParticleGenerator'

Expand All @@ -14,6 +15,8 @@ export interface IConfettiOptions {
run: boolean
debug: boolean
confettiSource: IRect
tweenFunction: (currentTime: number, currentValue: number, targetValue: number, duration: number, s?: number) => number
tweenDuration: number
}

export const confettiDefaults: Pick<IConfettiOptions, Exclude<keyof IConfettiOptions, 'confettiSource'>> = {
Expand All @@ -31,6 +34,8 @@ export const confettiDefaults: Pick<IConfettiOptions, Exclude<keyof IConfettiOpt
],
opacity: 1.0,
debug: false,
tweenFunction: tweens.easeInOutQuad,
tweenDuration: 5000,
recycle: true,
run: true,
}
Expand Down
35 changes: 28 additions & 7 deletions src/ParticleGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { IRect } from './Rect'
import Particle from './Particle'
import { randomRange } from './utils'

import { number } from 'prop-types';

export interface IParticleGenerator extends IRect {
removeParticleAt: (index: number) => void
getParticle: () => void
Expand All @@ -28,6 +30,8 @@ export default class ParticleGenerator implements IParticleGenerator {
y: number = 0
w: number = 0
h: number = 0
lastNumberOfPieces: number = 0
tweenInitTime: number = Date.now()
particles: Particle[] = []
particlesGenerated: number = 0

Expand All @@ -46,35 +50,52 @@ export default class ParticleGenerator implements IParticleGenerator {
canvas,
context,
particlesGenerated,
lastNumberOfPieces,
} = this
const {
run,
recycle,
numberOfPieces,
debug,
tweenFunction,
tweenDuration,
} = this.getOptions()
if(!run) {
return false
}

const nP = this.particles.length
const limit = recycle ? nP : particlesGenerated
const activeCount = recycle ? nP : particlesGenerated

const now = Date.now()


// Initial population
if(limit < numberOfPieces) {
if(activeCount < numberOfPieces) {
// Use the numberOfPieces prop as a key to reset the easing timing
if(lastNumberOfPieces !== numberOfPieces) {
this.tweenInitTime = now
this.lastNumberOfPieces = numberOfPieces
}
const { tweenInitTime } = this
// Add more than one piece per loop, otherwise the number of pieces would
// be limitted by the RAF framerate
const numToAdd = Math.ceil((numberOfPieces - limit) / 20)
const progressTime = now - tweenInitTime > tweenDuration
? tweenDuration
: Math.max(0, now - tweenInitTime)
const tweenedVal = tweenFunction(progressTime, activeCount, numberOfPieces, tweenDuration)
const numToAdd = Math.round(tweenedVal - activeCount)
for(let i = 0; i < numToAdd; i++) {
this.particles.push(this.getParticle())
}
this.particlesGenerated += numToAdd
}
if(debug) {
// Draw debug text
context.font = '12px serif'
context.font = '12px sans-serif'
context.fillStyle = '#333'
context.fillText(`Particles: ${nP}`, 20, 20)
context.textAlign = 'right'
context.fillText(`Particles: ${nP}`, canvas.width - 10, canvas.height - 20)
}

// Maintain the population
Expand All @@ -83,14 +104,14 @@ export default class ParticleGenerator implements IParticleGenerator {
p.update()
// Prune the off-canvas particles
if(p.y > canvas.height || p.y < -100 || p.x > canvas.width + 100 || p.x < -100) {
if(recycle && limit <= numberOfPieces) {
if(recycle && activeCount <= numberOfPieces) {
// Replace the particle with a brand new one
this.particles[i] = this.getParticle()
} else {
this.removeParticleAt(i)
}
}
})
return nP > 0 || limit < numberOfPieces
return nP > 0 || activeCount < numberOfPieces
}
}
2 changes: 2 additions & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
{
"compilerOptions": {
"baseUrl": ".",
"paths": { "*": ["types/*"] },
"target": "esnext",
"module": "commonjs",
"moduleResolution": "node",
Expand Down
Loading

0 comments on commit 829a560

Please sign in to comment.