import { fixDPR } from './fixDPR' ; import { ConfettiShape } from './ConfettiShape' ; import { createCanvas } from './createCanvas' ; import { normalizeConfettiConfig } from './normalizeConfettiConfig' ; import { IPosition , IJSConfettiConfig , IAddConfettiConfig } from './types' ; class ConfettiBatch { private resolvePromise ? : ( ) => void ; private promise : Promise < void > ; private shapes : ConfettiShape [ ] ; constructor ( private canvasContext ) { this . shapes = [ ] ; this . promise = new Promise ( ( completionCallback ) => this . resolvePromise = completionCallback ) ; } getBatchCompletePromise ( ) { return this . promise ; } addShapes ( ... shapes ) { this . shapes . push ( ... shapes ) ; } complete ( ) { if ( this . shapes . length ) { return false ; } this . resolvePromise ?. ( ) ; return true ; } processShapes ( time , canvasHeight , cleanupInvisibleShapes ) { const { timeDelta , currentTime } = time ; this . shapes = this . shapes . filter ( ( shape ) => { shape . updatePosition ( timeDelta , currentTime ) ; shape . draw ( this . canvasContext ) ; if ( ! cleanupInvisibleShapes ) { return true ; } return shape . getIsVisibleOnCanvas ( canvasHeight ) ; } ) ; } } class JSConfetti { private readonly canvas : HTMLCanvasElement ; private readonly canvasContext : CanvasRenderingContext2D ; private activeConfettiBatches : ConfettiBatch [ ] ; private lastUpdated : number ; private iterationIndex : number ; private requestAnimationFrameRequested : boolean ; public constructor ( jsConfettiConfig = { } ) { this . activeConfettiBatches = [ ] ; this . canvas = jsConfettiConfig . canvas || createCanvas ( ) ; this . canvasContext = < CanvasRenderingContext2D > this . canvas . getContext ( '2d' ) ; this . requestAnimationFrameRequested = false ; this . lastUpdated = new Date ( ) . getTime ( ) ; this . iterationIndex = 0 ; this . loop = this . loop . bind ( this ) ; requestAnimationFrame ( this . loop ) ; } private loop ( ) { this . requestAnimationFrameRequested = false ; fixDPR ( this . canvas ) ; const currentTime = new Date ( ) . getTime ( ) ; const timeDelta = currentTime - this . lastUpdated ; const canvasHeight = this . canvas . offsetHeight ; const cleanupInvisibleShapes = ( this . iterationIndex % 10 === 0 ) ; this . activeConfettiBatches = this . activeConfettiBatches . filter ( ( batch ) => { batch . processShapes ( { timeDelta , currentTime } , canvasHeight , cleanupInvisibleShapes ) ; if ( ! cleanupInvisibleShapes ) { return true ; } return ! ( batch . complete ( ) ) ; } ) ; this . iterationIndex ++ ; this . queueAnimationFrameIfNeeded ( currentTime ) ; } private queueAnimationFrameIfNeeded ( currentTime ? ) { if ( this . requestAnimationFrameRequested ) { return ; } if ( this . activeConfettiBatches . length < 1 ) { return ; } this . requestAnimationFrameRequested = true ; this . lastUpdated = currentTime || new Date ( ) . getTime ( ) ; requestAnimationFrame ( this . loop ) ; } public addConfetti ( confettiConfig = { } ) { const { confettiRadius , confettiNumber , confettiColors , emojis , emojiSize , } = normalizeConfettiConfig ( confettiConfig ) ; const canvasRect = this . canvas . getBoundingClientRect ( ) ; const canvasWidth = canvasRect . width ; const canvasHeight = canvasRect . height ; const yPosition = canvasHeight * 5 / 7 ; const leftConfettiPosition = { x : 0 , y : yPosition , } ; const rightConfettiPosition = { x : canvasWidth , y : yPosition , } ; const confettiGroup = new ConfettiBatch ( this . canvasContext ) ; for ( let i = 0 ; i < confettiNumber / 2 ; i ++ ) { const confettiOnTheRight = new ConfettiShape ( { initialPosition : leftConfettiPosition , direction : 'right' , confettiRadius , confettiColors , confettiNumber , emojis , emojiSize , canvasWidth , } ) ; const confettiOnTheLeft = new ConfettiShape ( { initialPosition : rightConfettiPosition , direction : 'left' , confettiRadius , confettiColors , confettiNumber , emojis , emojiSize , canvasWidth , } ) ; confettiGroup . addShapes ( confettiOnTheRight , confettiOnTheLeft ) ; } this . activeConfettiBatches . push ( confettiGroup ) ; this . queueAnimationFrameIfNeeded ( ) ; return confettiGroup . getBatchCompletePromise ( ) ; } } export { JSConfetti } ;