Skip to content

Commit 829a560

Browse files
committed
feat: use easing function for adding confetti
1 parent 3c9e108 commit 829a560

File tree

7 files changed

+111
-74
lines changed

7 files changed

+111
-74
lines changed

README.md

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -43,17 +43,19 @@ export default () => {
4343

4444
## Props
4545

46-
| Property | Type | Default | Description |
47-
| ---------------- | --------------------- | --- | --- |
48-
| `width` | `Number` | `window.innerWidth \|\| 300` | Width of the `<canvas>` element. |
49-
| `height` | `Number` | `window.innerHeight \|\| 200` | Height of the `<canvas>` 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-
| `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. |
56-
| `opacity` | `Number` | 1.0 | |
57-
| `recycle` | `Bool` | true | Keep spawning confetti after `numberOfPieces` pieces have been shown. |
58-
| `run` | `Bool` | true | Run the animation loop |
46+
| Property | Type | Default | Description |
47+
| ---------------- | --------------------- | --- | --- |
48+
| `width` | `Number` | `window.innerWidth \|\| 300` | Width of the `<canvas>` element. |
49+
| `height` | `Number` | `window.innerHeight \|\| 200` | Height of the `<canvas>` 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+
| `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. |
56+
| `opacity` | `Number` | 1.0 | |
57+
| `recycle` | `Bool` | true | Keep spawning confetti after `numberOfPieces` pieces have been shown. |
58+
| `run` | `Bool` | true | Run the animation loop |
59+
| `tweenDuration` | `Number` | 5000 | How fast the confetti is added |
60+
| `tweenFunction` | `(currentTime: number, currentValue: number, targetValue: number, duration: number, s?: number) => number` | easeInOutQuad | See [tween-functions](https://github.com/chenglou/tween-functions) |
5961

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@
4242
"prop-types": "^15.6.0",
4343
"react": "0.14.x || ^15.0.1 || ^16.2.0"
4444
},
45+
"dependencies": {
46+
"tween-functions": "^1.2.0"
47+
},
4548
"devDependencies": {
4649
"@babel/core": "^7.1.6",
4750
"@babel/plugin-proposal-class-properties": "^7.3.4",

src/Confetti.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import tweens from 'tween-functions'
12
import { IRect } from './Rect'
23
import ParticleGenerator from './ParticleGenerator'
34

@@ -14,6 +15,8 @@ export interface IConfettiOptions {
1415
run: boolean
1516
debug: boolean
1617
confettiSource: IRect
18+
tweenFunction: (currentTime: number, currentValue: number, targetValue: number, duration: number, s?: number) => number
19+
tweenDuration: number
1720
}
1821

1922
export const confettiDefaults: Pick<IConfettiOptions, Exclude<keyof IConfettiOptions, 'confettiSource'>> = {
@@ -31,6 +34,8 @@ export const confettiDefaults: Pick<IConfettiOptions, Exclude<keyof IConfettiOpt
3134
],
3235
opacity: 1.0,
3336
debug: false,
37+
tweenFunction: tweens.easeInOutQuad,
38+
tweenDuration: 5000,
3439
recycle: true,
3540
run: true,
3641
}

src/ParticleGenerator.ts

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import { IRect } from './Rect'
33
import Particle from './Particle'
44
import { randomRange } from './utils'
55

6+
import { number } from 'prop-types';
7+
68
export interface IParticleGenerator extends IRect {
79
removeParticleAt: (index: number) => void
810
getParticle: () => void
@@ -28,6 +30,8 @@ export default class ParticleGenerator implements IParticleGenerator {
2830
y: number = 0
2931
w: number = 0
3032
h: number = 0
33+
lastNumberOfPieces: number = 0
34+
tweenInitTime: number = Date.now()
3135
particles: Particle[] = []
3236
particlesGenerated: number = 0
3337

@@ -46,35 +50,52 @@ export default class ParticleGenerator implements IParticleGenerator {
4650
canvas,
4751
context,
4852
particlesGenerated,
53+
lastNumberOfPieces,
4954
} = this
5055
const {
5156
run,
5257
recycle,
5358
numberOfPieces,
5459
debug,
60+
tweenFunction,
61+
tweenDuration,
5562
} = this.getOptions()
5663
if(!run) {
5764
return false
5865
}
5966

6067
const nP = this.particles.length
61-
const limit = recycle ? nP : particlesGenerated
68+
const activeCount = recycle ? nP : particlesGenerated
69+
70+
const now = Date.now()
71+
6272

6373
// Initial population
64-
if(limit < numberOfPieces) {
74+
if(activeCount < numberOfPieces) {
75+
// Use the numberOfPieces prop as a key to reset the easing timing
76+
if(lastNumberOfPieces !== numberOfPieces) {
77+
this.tweenInitTime = now
78+
this.lastNumberOfPieces = numberOfPieces
79+
}
80+
const { tweenInitTime } = this
6581
// Add more than one piece per loop, otherwise the number of pieces would
6682
// be limitted by the RAF framerate
67-
const numToAdd = Math.ceil((numberOfPieces - limit) / 20)
83+
const progressTime = now - tweenInitTime > tweenDuration
84+
? tweenDuration
85+
: Math.max(0, now - tweenInitTime)
86+
const tweenedVal = tweenFunction(progressTime, activeCount, numberOfPieces, tweenDuration)
87+
const numToAdd = Math.round(tweenedVal - activeCount)
6888
for(let i = 0; i < numToAdd; i++) {
6989
this.particles.push(this.getParticle())
7090
}
7191
this.particlesGenerated += numToAdd
7292
}
7393
if(debug) {
7494
// Draw debug text
75-
context.font = '12px serif'
95+
context.font = '12px sans-serif'
7696
context.fillStyle = '#333'
77-
context.fillText(`Particles: ${nP}`, 20, 20)
97+
context.textAlign = 'right'
98+
context.fillText(`Particles: ${nP}`, canvas.width - 10, canvas.height - 20)
7899
}
79100

80101
// Maintain the population
@@ -83,14 +104,14 @@ export default class ParticleGenerator implements IParticleGenerator {
83104
p.update()
84105
// Prune the off-canvas particles
85106
if(p.y > canvas.height || p.y < -100 || p.x > canvas.width + 100 || p.x < -100) {
86-
if(recycle && limit <= numberOfPieces) {
107+
if(recycle && activeCount <= numberOfPieces) {
87108
// Replace the particle with a brand new one
88109
this.particles[i] = this.getParticle()
89110
} else {
90111
this.removeParticleAt(i)
91112
}
92113
}
93114
})
94-
return nP > 0 || limit < numberOfPieces
115+
return nP > 0 || activeCount < numberOfPieces
95116
}
96117
}

tsconfig.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
{
22
"compilerOptions": {
3+
"baseUrl": ".",
4+
"paths": { "*": ["types/*"] },
35
"target": "esnext",
46
"module": "commonjs",
57
"moduleResolution": "node",

0 commit comments

Comments
 (0)