@input-kit/confetti 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +13 -0
- package/README.md +141 -0
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +25 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/package.json +57 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Input Kit
|
|
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.
|
package/README.md
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# @input-kit/confetti
|
|
2
|
+
|
|
3
|
+
Lightweight, zero-dependency confetti effect for the browser. Built with the Canvas API, fully typed, SSR-safe, and cancellable.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- Canvas-based particle renderer — no external dependencies
|
|
8
|
+
- Configurable launch angle, spread, gravity, drift, and particle count
|
|
9
|
+
- Built-in `celebrate()` multi-burst preset
|
|
10
|
+
- Returns a cancellation function to stop an in-flight animation
|
|
11
|
+
- Automatically cancels when the tab is hidden (via `visibilitychange`)
|
|
12
|
+
- SSR-safe — no-ops when `document` is unavailable
|
|
13
|
+
- Zero React required — works with any framework or plain HTML
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install @input-kit/confetti
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Quick Start
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
import { confetti, celebrate } from '@input-kit/confetti';
|
|
25
|
+
|
|
26
|
+
// Basic burst from center
|
|
27
|
+
confetti();
|
|
28
|
+
|
|
29
|
+
// Custom burst
|
|
30
|
+
confetti({
|
|
31
|
+
particleCount: 150,
|
|
32
|
+
angle: 90,
|
|
33
|
+
spread: 60,
|
|
34
|
+
origin: { x: 0.5, y: 0.8 },
|
|
35
|
+
colors: ['#ff6b6b', '#feca57', '#48dbfb'],
|
|
36
|
+
gravity: 0.6,
|
|
37
|
+
ticks: 250,
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
// Built-in multi-burst celebration
|
|
41
|
+
celebrate();
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Cancel an animation
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
const stop = confetti({ particleCount: 500, ticks: 600 });
|
|
48
|
+
// Later:
|
|
49
|
+
stop?.();
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## API
|
|
53
|
+
|
|
54
|
+
### `confetti(options?)`
|
|
55
|
+
|
|
56
|
+
Fires a confetti burst. Returns a cancellation function, or `undefined` in SSR environments.
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
function confetti(options?: ConfettiOptions): (() => void) | undefined
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### `fireConfetti(options?)`
|
|
63
|
+
|
|
64
|
+
Alias for `confetti`.
|
|
65
|
+
|
|
66
|
+
### `celebrate()`
|
|
67
|
+
|
|
68
|
+
Fires three sequential bursts — left cannon, right cannon, center explosion — for a full-screen celebration effect.
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
function celebrate(): void
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## ConfettiOptions
|
|
75
|
+
|
|
76
|
+
| Option | Type | Default | Description |
|
|
77
|
+
|---|---|---|---|
|
|
78
|
+
| `particleCount` | `number` | `100` | Number of particles to spawn |
|
|
79
|
+
| `angle` | `number` | `90` | Launch direction in degrees. `0` = right, `90` = up, `180` = left, `270` = down |
|
|
80
|
+
| `spread` | `number` | `360` | Cone width in degrees centered on `angle`. `360` = all directions, `60` = narrow cone |
|
|
81
|
+
| `origin` | `{ x: number; y: number }` | `{ x: 0.5, y: 0.5 }` | Launch point as fraction of viewport. `(0, 0)` = top-left, `(1, 1)` = bottom-right |
|
|
82
|
+
| `colors` | `string[]` | 6-color rainbow | Array of CSS color strings for particle fill |
|
|
83
|
+
| `gravity` | `number` | `0.5` | Downward acceleration per frame. Higher = particles fall faster |
|
|
84
|
+
| `drift` | `number` | `0` | Horizontal drift per frame. Positive = right, negative = left |
|
|
85
|
+
| `ticks` | `number` | `200` | Lifetime of each particle in frames. Roughly `ticks / 60` seconds |
|
|
86
|
+
|
|
87
|
+
## Examples
|
|
88
|
+
|
|
89
|
+
### Fireworks effect
|
|
90
|
+
|
|
91
|
+
```ts
|
|
92
|
+
const end = Date.now() + 3000;
|
|
93
|
+
|
|
94
|
+
function frame() {
|
|
95
|
+
confetti({ particleCount: 2, angle: 60, spread: 55, origin: { x: 0, y: 0.6 } });
|
|
96
|
+
confetti({ particleCount: 2, angle: 120, spread: 55, origin: { x: 1, y: 0.6 } });
|
|
97
|
+
if (Date.now() < end) requestAnimationFrame(frame);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
frame();
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Custom color palette
|
|
104
|
+
|
|
105
|
+
```ts
|
|
106
|
+
confetti({
|
|
107
|
+
particleCount: 200,
|
|
108
|
+
colors: ['#ff0000', '#ff7f00', '#ffff00', '#00ff00', '#0000ff', '#4b0082', '#9400d3'],
|
|
109
|
+
spread: 180,
|
|
110
|
+
});
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### React usage
|
|
114
|
+
|
|
115
|
+
```tsx
|
|
116
|
+
import { confetti, celebrate } from '@input-kit/confetti';
|
|
117
|
+
|
|
118
|
+
function SubmitButton() {
|
|
119
|
+
const handleClick = () => {
|
|
120
|
+
celebrate();
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
return <button onClick={handleClick}>Submit</button>;
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## TypeScript
|
|
128
|
+
|
|
129
|
+
All types are exported:
|
|
130
|
+
|
|
131
|
+
```ts
|
|
132
|
+
import type { ConfettiOptions } from '@input-kit/confetti';
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Browser Support
|
|
136
|
+
|
|
137
|
+
Requires Canvas 2D API — supported in all modern browsers. Not supported in Node.js / SSR (calls are silently no-ops).
|
|
138
|
+
|
|
139
|
+
## License
|
|
140
|
+
|
|
141
|
+
MIT © Harshit
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
'use strict';var M=["#ff0000","#00ff00","#0000ff","#ffff00","#ff00ff","#00ffff"];function o(r={}){if(typeof document>"u"||!document.body)return;let{particleCount:g=100,angle:p=90,spread:y=360,origin:l={x:.5,y:.5},colors:c=M,gravity:b=.5,drift:x=0,ticks:v=200}=r,e=document.createElement("canvas");e.style.cssText="position:fixed;top:0;left:0;width:100%;height:100%;pointer-events:none;z-index:9999",document.body.appendChild(e);let d=e.getContext("2d");if(!d){document.body.removeChild(e);return}let n=d;e.width=window.innerWidth,e.height=window.innerHeight;let s=[],C=p*(Math.PI/180),A=y/2*(Math.PI/180);for(let i=0;i<g;i++){let t=C+(Math.random()*2-1)*A,h=Math.random()*10+5;s.push({x:l.x*e.width,y:l.y*e.height,color:c[Math.floor(Math.random()*c.length)],velocity:{x:Math.cos(t)*h,y:-Math.sin(t)*h},gravity:b,drift:x,ticks:v,tilt:Math.random()*10,tiltAngle:0,tiltAngleIncrement:Math.random()*.1+.05});}let f,u=new AbortController;function a(){cancelAnimationFrame(f),u.abort(),e.parentNode&&e.parentNode.removeChild(e);}function m(){n.clearRect(0,0,e.width,e.height);let i=0;for(let t of s)t.ticks>0&&(t.ticks--,t.x+=t.velocity.x+t.drift,t.y+=t.velocity.y,t.velocity.y+=t.gravity,t.tiltAngle+=t.tiltAngleIncrement,t.tilt=Math.sin(t.tiltAngle)*15,n.beginPath(),n.lineWidth=8,n.strokeStyle=t.color,n.moveTo(t.x+t.tilt,t.y),n.lineTo(t.x,t.y+10+t.tilt),n.stroke(),i++);i>0?f=requestAnimationFrame(m):a();}return document.addEventListener("visibilitychange",()=>{document.hidden&&a();},{signal:u.signal}),m(),a}function w(r={}){return o(r)}function k(){setTimeout(()=>{o({origin:{x:.1,y:.8},particleCount:50,spread:60,angle:60});},0),setTimeout(()=>{o({origin:{x:.9,y:.8},particleCount:50,spread:60,angle:120});},100),setTimeout(()=>{o({origin:{x:.5,y:.5},particleCount:100,spread:360});},200);}exports.celebrate=k;exports.confetti=o;exports.fireConfetti=w;//# sourceMappingURL=index.cjs.map
|
|
2
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.tsx"],"names":["defaultColors","confetti","options","particleCount","angle","spread","origin","colors","gravity","drift","ticks","canvas","rawCtx","ctx","particles","launchRad","halfSpreadRad","particleAngle","speed","animationId","controller","cleanup","update","activeParticles","p","fireConfetti","celebrate"],"mappings":"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","file":"index.cjs","sourcesContent":["// @input-kit/confetti - Lightweight confetti effects\r\n\r\nexport interface ConfettiOptions {\r\n particleCount?: number;\r\n /** Launch angle in degrees. 0 = right, 90 = up, 180 = left, 270 = down. Default: 90 */\r\n angle?: number;\r\n spread?: number;\r\n origin?: { x: number; y: number };\r\n colors?: string[];\r\n gravity?: number;\r\n drift?: number;\r\n ticks?: number;\r\n}\r\n\r\ninterface Particle {\r\n x: number;\r\n y: number;\r\n color: string;\r\n velocity: { x: number; y: number };\r\n gravity: number;\r\n drift: number;\r\n ticks: number;\r\n tilt: number;\r\n tiltAngle: number;\r\n tiltAngleIncrement: number;\r\n}\r\n\r\nconst defaultColors = ['#ff0000', '#00ff00', '#0000ff', '#ffff00', '#ff00ff', '#00ffff'];\r\n\r\n/**\r\n * Fire a confetti burst. Returns a cancellation function.\r\n * Safe to call in SSR environments (no-ops when `document` is unavailable).\r\n */\r\nexport function confetti(options: ConfettiOptions = {}): (() => void) | undefined {\r\n if (typeof document === 'undefined' || !document.body) return undefined;\r\n\r\n const {\r\n particleCount = 100,\r\n angle = 90,\r\n spread = 360,\r\n origin = { x: 0.5, y: 0.5 },\r\n colors = defaultColors,\r\n gravity = 0.5,\r\n drift = 0,\r\n ticks = 200,\r\n } = options;\r\n\r\n const canvas = document.createElement('canvas');\r\n canvas.style.cssText =\r\n 'position:fixed;top:0;left:0;width:100%;height:100%;pointer-events:none;z-index:9999';\r\n document.body.appendChild(canvas);\r\n\r\n const rawCtx = canvas.getContext('2d');\r\n if (!rawCtx) {\r\n document.body.removeChild(canvas);\r\n return undefined;\r\n }\r\n // Rebind with explicit non-null type so TypeScript keeps the narrowing inside closures.\r\n const ctx: CanvasRenderingContext2D = rawCtx;\r\n\r\n canvas.width = window.innerWidth;\r\n canvas.height = window.innerHeight;\r\n\r\n const particles: Particle[] = [];\r\n const launchRad = angle * (Math.PI / 180);\r\n const halfSpreadRad = (spread / 2) * (Math.PI / 180);\r\n\r\n for (let i = 0; i < particleCount; i++) {\r\n const particleAngle = launchRad + (Math.random() * 2 - 1) * halfSpreadRad;\r\n const speed = Math.random() * 10 + 5;\r\n\r\n particles.push({\r\n x: origin.x * canvas.width,\r\n y: origin.y * canvas.height,\r\n color: colors[Math.floor(Math.random() * colors.length)],\r\n velocity: {\r\n x: Math.cos(particleAngle) * speed,\r\n y: -Math.sin(particleAngle) * speed,\r\n },\r\n gravity,\r\n drift,\r\n ticks,\r\n tilt: Math.random() * 10,\r\n tiltAngle: 0,\r\n tiltAngleIncrement: Math.random() * 0.1 + 0.05,\r\n });\r\n }\r\n\r\n let animationId: number;\r\n const controller = new AbortController();\r\n\r\n function cleanup() {\r\n cancelAnimationFrame(animationId);\r\n controller.abort();\r\n if (canvas.parentNode) {\r\n canvas.parentNode.removeChild(canvas);\r\n }\r\n }\r\n\r\n function update() {\r\n ctx.clearRect(0, 0, canvas.width, canvas.height);\r\n\r\n let activeParticles = 0;\r\n\r\n for (const p of particles) {\r\n if (p.ticks > 0) {\r\n p.ticks--;\r\n p.x += p.velocity.x + p.drift;\r\n p.y += p.velocity.y;\r\n p.velocity.y += p.gravity;\r\n p.tiltAngle += p.tiltAngleIncrement;\r\n p.tilt = Math.sin(p.tiltAngle) * 15;\r\n\r\n ctx.beginPath();\r\n ctx.lineWidth = 8;\r\n ctx.strokeStyle = p.color;\r\n ctx.moveTo(p.x + p.tilt, p.y);\r\n ctx.lineTo(p.x, p.y + 10 + p.tilt);\r\n ctx.stroke();\r\n\r\n activeParticles++;\r\n }\r\n }\r\n\r\n if (activeParticles > 0) {\r\n animationId = requestAnimationFrame(update);\r\n } else {\r\n cleanup();\r\n }\r\n }\r\n\r\n // Auto-cancel when the tab is hidden; listener is removed via AbortController\r\n document.addEventListener(\r\n 'visibilitychange',\r\n () => {\r\n if (document.hidden) cleanup();\r\n },\r\n { signal: controller.signal },\r\n );\r\n\r\n update();\r\n\r\n return cleanup;\r\n}\r\n\r\n/** Alias for `confetti`. Returns a cancellation function. */\r\nexport function fireConfetti(options: ConfettiOptions = {}): (() => void) | undefined {\r\n return confetti(options);\r\n}\r\n\r\n/** Multi-burst celebration: left cannon + right cannon + center burst. */\r\nexport function celebrate(): void {\r\n setTimeout(() => {\r\n confetti({ origin: { x: 0.1, y: 0.8 }, particleCount: 50, spread: 60, angle: 60 });\r\n }, 0);\r\n\r\n setTimeout(() => {\r\n confetti({ origin: { x: 0.9, y: 0.8 }, particleCount: 50, spread: 60, angle: 120 });\r\n }, 100);\r\n\r\n setTimeout(() => {\r\n confetti({ origin: { x: 0.5, y: 0.5 }, particleCount: 100, spread: 360 });\r\n }, 200);\r\n}\r\n"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
interface ConfettiOptions {
|
|
2
|
+
particleCount?: number;
|
|
3
|
+
/** Launch angle in degrees. 0 = right, 90 = up, 180 = left, 270 = down. Default: 90 */
|
|
4
|
+
angle?: number;
|
|
5
|
+
spread?: number;
|
|
6
|
+
origin?: {
|
|
7
|
+
x: number;
|
|
8
|
+
y: number;
|
|
9
|
+
};
|
|
10
|
+
colors?: string[];
|
|
11
|
+
gravity?: number;
|
|
12
|
+
drift?: number;
|
|
13
|
+
ticks?: number;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Fire a confetti burst. Returns a cancellation function.
|
|
17
|
+
* Safe to call in SSR environments (no-ops when `document` is unavailable).
|
|
18
|
+
*/
|
|
19
|
+
declare function confetti(options?: ConfettiOptions): (() => void) | undefined;
|
|
20
|
+
/** Alias for `confetti`. Returns a cancellation function. */
|
|
21
|
+
declare function fireConfetti(options?: ConfettiOptions): (() => void) | undefined;
|
|
22
|
+
/** Multi-burst celebration: left cannon + right cannon + center burst. */
|
|
23
|
+
declare function celebrate(): void;
|
|
24
|
+
|
|
25
|
+
export { type ConfettiOptions, celebrate, confetti, fireConfetti };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
interface ConfettiOptions {
|
|
2
|
+
particleCount?: number;
|
|
3
|
+
/** Launch angle in degrees. 0 = right, 90 = up, 180 = left, 270 = down. Default: 90 */
|
|
4
|
+
angle?: number;
|
|
5
|
+
spread?: number;
|
|
6
|
+
origin?: {
|
|
7
|
+
x: number;
|
|
8
|
+
y: number;
|
|
9
|
+
};
|
|
10
|
+
colors?: string[];
|
|
11
|
+
gravity?: number;
|
|
12
|
+
drift?: number;
|
|
13
|
+
ticks?: number;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Fire a confetti burst. Returns a cancellation function.
|
|
17
|
+
* Safe to call in SSR environments (no-ops when `document` is unavailable).
|
|
18
|
+
*/
|
|
19
|
+
declare function confetti(options?: ConfettiOptions): (() => void) | undefined;
|
|
20
|
+
/** Alias for `confetti`. Returns a cancellation function. */
|
|
21
|
+
declare function fireConfetti(options?: ConfettiOptions): (() => void) | undefined;
|
|
22
|
+
/** Multi-burst celebration: left cannon + right cannon + center burst. */
|
|
23
|
+
declare function celebrate(): void;
|
|
24
|
+
|
|
25
|
+
export { type ConfettiOptions, celebrate, confetti, fireConfetti };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var M=["#ff0000","#00ff00","#0000ff","#ffff00","#ff00ff","#00ffff"];function o(r={}){if(typeof document>"u"||!document.body)return;let{particleCount:g=100,angle:p=90,spread:y=360,origin:l={x:.5,y:.5},colors:c=M,gravity:b=.5,drift:x=0,ticks:v=200}=r,e=document.createElement("canvas");e.style.cssText="position:fixed;top:0;left:0;width:100%;height:100%;pointer-events:none;z-index:9999",document.body.appendChild(e);let d=e.getContext("2d");if(!d){document.body.removeChild(e);return}let n=d;e.width=window.innerWidth,e.height=window.innerHeight;let s=[],C=p*(Math.PI/180),A=y/2*(Math.PI/180);for(let i=0;i<g;i++){let t=C+(Math.random()*2-1)*A,h=Math.random()*10+5;s.push({x:l.x*e.width,y:l.y*e.height,color:c[Math.floor(Math.random()*c.length)],velocity:{x:Math.cos(t)*h,y:-Math.sin(t)*h},gravity:b,drift:x,ticks:v,tilt:Math.random()*10,tiltAngle:0,tiltAngleIncrement:Math.random()*.1+.05});}let f,u=new AbortController;function a(){cancelAnimationFrame(f),u.abort(),e.parentNode&&e.parentNode.removeChild(e);}function m(){n.clearRect(0,0,e.width,e.height);let i=0;for(let t of s)t.ticks>0&&(t.ticks--,t.x+=t.velocity.x+t.drift,t.y+=t.velocity.y,t.velocity.y+=t.gravity,t.tiltAngle+=t.tiltAngleIncrement,t.tilt=Math.sin(t.tiltAngle)*15,n.beginPath(),n.lineWidth=8,n.strokeStyle=t.color,n.moveTo(t.x+t.tilt,t.y),n.lineTo(t.x,t.y+10+t.tilt),n.stroke(),i++);i>0?f=requestAnimationFrame(m):a();}return document.addEventListener("visibilitychange",()=>{document.hidden&&a();},{signal:u.signal}),m(),a}function w(r={}){return o(r)}function k(){setTimeout(()=>{o({origin:{x:.1,y:.8},particleCount:50,spread:60,angle:60});},0),setTimeout(()=>{o({origin:{x:.9,y:.8},particleCount:50,spread:60,angle:120});},100),setTimeout(()=>{o({origin:{x:.5,y:.5},particleCount:100,spread:360});},200);}export{k as celebrate,o as confetti,w as fireConfetti};//# sourceMappingURL=index.js.map
|
|
2
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.tsx"],"names":["defaultColors","confetti","options","particleCount","angle","spread","origin","colors","gravity","drift","ticks","canvas","rawCtx","ctx","particles","launchRad","halfSpreadRad","particleAngle","speed","animationId","controller","cleanup","update","activeParticles","p","fireConfetti","celebrate"],"mappings":"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","file":"index.js","sourcesContent":["// @input-kit/confetti - Lightweight confetti effects\r\n\r\nexport interface ConfettiOptions {\r\n particleCount?: number;\r\n /** Launch angle in degrees. 0 = right, 90 = up, 180 = left, 270 = down. Default: 90 */\r\n angle?: number;\r\n spread?: number;\r\n origin?: { x: number; y: number };\r\n colors?: string[];\r\n gravity?: number;\r\n drift?: number;\r\n ticks?: number;\r\n}\r\n\r\ninterface Particle {\r\n x: number;\r\n y: number;\r\n color: string;\r\n velocity: { x: number; y: number };\r\n gravity: number;\r\n drift: number;\r\n ticks: number;\r\n tilt: number;\r\n tiltAngle: number;\r\n tiltAngleIncrement: number;\r\n}\r\n\r\nconst defaultColors = ['#ff0000', '#00ff00', '#0000ff', '#ffff00', '#ff00ff', '#00ffff'];\r\n\r\n/**\r\n * Fire a confetti burst. Returns a cancellation function.\r\n * Safe to call in SSR environments (no-ops when `document` is unavailable).\r\n */\r\nexport function confetti(options: ConfettiOptions = {}): (() => void) | undefined {\r\n if (typeof document === 'undefined' || !document.body) return undefined;\r\n\r\n const {\r\n particleCount = 100,\r\n angle = 90,\r\n spread = 360,\r\n origin = { x: 0.5, y: 0.5 },\r\n colors = defaultColors,\r\n gravity = 0.5,\r\n drift = 0,\r\n ticks = 200,\r\n } = options;\r\n\r\n const canvas = document.createElement('canvas');\r\n canvas.style.cssText =\r\n 'position:fixed;top:0;left:0;width:100%;height:100%;pointer-events:none;z-index:9999';\r\n document.body.appendChild(canvas);\r\n\r\n const rawCtx = canvas.getContext('2d');\r\n if (!rawCtx) {\r\n document.body.removeChild(canvas);\r\n return undefined;\r\n }\r\n // Rebind with explicit non-null type so TypeScript keeps the narrowing inside closures.\r\n const ctx: CanvasRenderingContext2D = rawCtx;\r\n\r\n canvas.width = window.innerWidth;\r\n canvas.height = window.innerHeight;\r\n\r\n const particles: Particle[] = [];\r\n const launchRad = angle * (Math.PI / 180);\r\n const halfSpreadRad = (spread / 2) * (Math.PI / 180);\r\n\r\n for (let i = 0; i < particleCount; i++) {\r\n const particleAngle = launchRad + (Math.random() * 2 - 1) * halfSpreadRad;\r\n const speed = Math.random() * 10 + 5;\r\n\r\n particles.push({\r\n x: origin.x * canvas.width,\r\n y: origin.y * canvas.height,\r\n color: colors[Math.floor(Math.random() * colors.length)],\r\n velocity: {\r\n x: Math.cos(particleAngle) * speed,\r\n y: -Math.sin(particleAngle) * speed,\r\n },\r\n gravity,\r\n drift,\r\n ticks,\r\n tilt: Math.random() * 10,\r\n tiltAngle: 0,\r\n tiltAngleIncrement: Math.random() * 0.1 + 0.05,\r\n });\r\n }\r\n\r\n let animationId: number;\r\n const controller = new AbortController();\r\n\r\n function cleanup() {\r\n cancelAnimationFrame(animationId);\r\n controller.abort();\r\n if (canvas.parentNode) {\r\n canvas.parentNode.removeChild(canvas);\r\n }\r\n }\r\n\r\n function update() {\r\n ctx.clearRect(0, 0, canvas.width, canvas.height);\r\n\r\n let activeParticles = 0;\r\n\r\n for (const p of particles) {\r\n if (p.ticks > 0) {\r\n p.ticks--;\r\n p.x += p.velocity.x + p.drift;\r\n p.y += p.velocity.y;\r\n p.velocity.y += p.gravity;\r\n p.tiltAngle += p.tiltAngleIncrement;\r\n p.tilt = Math.sin(p.tiltAngle) * 15;\r\n\r\n ctx.beginPath();\r\n ctx.lineWidth = 8;\r\n ctx.strokeStyle = p.color;\r\n ctx.moveTo(p.x + p.tilt, p.y);\r\n ctx.lineTo(p.x, p.y + 10 + p.tilt);\r\n ctx.stroke();\r\n\r\n activeParticles++;\r\n }\r\n }\r\n\r\n if (activeParticles > 0) {\r\n animationId = requestAnimationFrame(update);\r\n } else {\r\n cleanup();\r\n }\r\n }\r\n\r\n // Auto-cancel when the tab is hidden; listener is removed via AbortController\r\n document.addEventListener(\r\n 'visibilitychange',\r\n () => {\r\n if (document.hidden) cleanup();\r\n },\r\n { signal: controller.signal },\r\n );\r\n\r\n update();\r\n\r\n return cleanup;\r\n}\r\n\r\n/** Alias for `confetti`. Returns a cancellation function. */\r\nexport function fireConfetti(options: ConfettiOptions = {}): (() => void) | undefined {\r\n return confetti(options);\r\n}\r\n\r\n/** Multi-burst celebration: left cannon + right cannon + center burst. */\r\nexport function celebrate(): void {\r\n setTimeout(() => {\r\n confetti({ origin: { x: 0.1, y: 0.8 }, particleCount: 50, spread: 60, angle: 60 });\r\n }, 0);\r\n\r\n setTimeout(() => {\r\n confetti({ origin: { x: 0.9, y: 0.8 }, particleCount: 50, spread: 60, angle: 120 });\r\n }, 100);\r\n\r\n setTimeout(() => {\r\n confetti({ origin: { x: 0.5, y: 0.5 }, particleCount: 100, spread: 360 });\r\n }, 200);\r\n}\r\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@input-kit/confetti",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Confetti effects",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"require": "./dist/index.cjs"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"README.md",
|
|
19
|
+
"LICENSE"
|
|
20
|
+
],
|
|
21
|
+
"sideEffects": false,
|
|
22
|
+
"scripts": {
|
|
23
|
+
"build": "tsup",
|
|
24
|
+
"dev": "tsup --watch",
|
|
25
|
+
"test": "vitest run",
|
|
26
|
+
"test:watch": "vitest",
|
|
27
|
+
"typecheck": "tsc --noEmit",
|
|
28
|
+
"prepublishOnly": "npm run build"
|
|
29
|
+
},
|
|
30
|
+
"keywords": [
|
|
31
|
+
"react",
|
|
32
|
+
"input-kit",
|
|
33
|
+
"confetti",
|
|
34
|
+
"typescript",
|
|
35
|
+
"headless"
|
|
36
|
+
],
|
|
37
|
+
"author": "Harshit",
|
|
38
|
+
"license": "MIT",
|
|
39
|
+
"engines": {
|
|
40
|
+
"node": ">=18.0.0"
|
|
41
|
+
},
|
|
42
|
+
"peerDependencies": {
|
|
43
|
+
"react": "^18.0.0 || ^19.0.0",
|
|
44
|
+
"react-dom": "^18.0.0 || ^19.0.0"
|
|
45
|
+
},
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"@types/react": "^18.3.18",
|
|
48
|
+
"@types/react-dom": "^18.3.5",
|
|
49
|
+
"@testing-library/react": "^14.2.0",
|
|
50
|
+
"jsdom": "^26.0.0",
|
|
51
|
+
"react": "^18.3.1",
|
|
52
|
+
"react-dom": "^18.3.1",
|
|
53
|
+
"tsup": "^8.3.5",
|
|
54
|
+
"typescript": "^5.7.3",
|
|
55
|
+
"vitest": "^3.0.4"
|
|
56
|
+
}
|
|
57
|
+
}
|