@tsparticles/template-confetti 4.1.3 → 4.2.1

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.
Files changed (65) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/README.md +3 -0
  3. package/package.json +4 -3
  4. package/template/angular/package.json +34 -0
  5. package/template/angular/src/app/app.component.css +54 -0
  6. package/template/angular/src/app/app.component.html +11 -0
  7. package/template/angular/src/app/app.component.ts +44 -0
  8. package/template/angular/src/app/app.module.ts +13 -0
  9. package/template/angular-confetti/package.json +35 -0
  10. package/template/angular-confetti/src/app/app.component.css +47 -0
  11. package/template/angular-confetti/src/app/app.component.html +11 -0
  12. package/template/angular-confetti/src/app/app.component.ts +44 -0
  13. package/template/angular-fireworks/package.json +35 -0
  14. package/template/angular-fireworks/src/app/app.component.css +47 -0
  15. package/template/angular-fireworks/src/app/app.component.html +11 -0
  16. package/template/angular-fireworks/src/app/app.component.ts +44 -0
  17. package/template/astro/package.json +21 -0
  18. package/template/astro/src/pages/index.astro +77 -0
  19. package/template/ember/app/templates/application.hbs +26 -0
  20. package/template/ember/package.json +41 -0
  21. package/template/inferno/package.json +23 -0
  22. package/template/inferno/src/App.css +54 -0
  23. package/template/inferno/src/App.tsx +69 -0
  24. package/template/jquery/package.json +21 -0
  25. package/template/jquery/src/main.ts +49 -0
  26. package/template/jquery/src/style.css +54 -0
  27. package/template/lit/package.json +21 -0
  28. package/template/lit/src/my-app.ts +68 -0
  29. package/template/nextjs/package.json +24 -0
  30. package/template/nextjs/src/app/page.tsx +59 -0
  31. package/template/nuxt2/package.json +16 -0
  32. package/template/nuxt2/pages/index.vue +72 -0
  33. package/template/nuxt3/app.vue +65 -0
  34. package/template/nuxt3/package.json +20 -0
  35. package/template/nuxt4/app.vue +65 -0
  36. package/template/nuxt4/package.json +20 -0
  37. package/template/preact/package.json +22 -0
  38. package/template/preact/src/App.css +54 -0
  39. package/template/preact/src/App.tsx +62 -0
  40. package/template/qwik/package.json +21 -0
  41. package/template/qwik/src/App.tsx +61 -0
  42. package/template/react/package.json +19 -0
  43. package/template/react/src/App.css +54 -0
  44. package/template/react/src/App.tsx +60 -0
  45. package/template/riot/package.json +23 -0
  46. package/template/riot/src/app.riot +111 -0
  47. package/template/solid/package.json +18 -0
  48. package/template/solid/src/App.tsx +66 -0
  49. package/template/solid/src/index.css +54 -0
  50. package/template/solid/src/main.tsx +9 -0
  51. package/template/stencil/package.json +19 -0
  52. package/template/stencil/src/components/app-home/app-home.tsx +73 -0
  53. package/template/svelte/package.json +20 -0
  54. package/template/svelte/src/App.svelte +119 -0
  55. package/template/svelte/src/main.ts +6 -0
  56. package/template/vanilla/LICENSE +21 -0
  57. package/template/vanilla/package.json +1 -1
  58. package/template/vue2/package.json +23 -0
  59. package/template/vue2/src/App.vue +121 -0
  60. package/template/vue3/package.json +19 -0
  61. package/template/vue3/src/App.vue +110 -0
  62. package/template/vue3/src/main.ts +9 -0
  63. package/template/webcomponents/package.json +20 -0
  64. package/template/webcomponents/src/main.ts +48 -0
  65. package/template/webcomponents/src/style.css +54 -0
@@ -0,0 +1,54 @@
1
+ body {
2
+ margin: 0;
3
+ overflow: hidden;
4
+ background: #1a1a2e;
5
+ font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
6
+ }
7
+
8
+ #app {
9
+ position: absolute;
10
+ top: 50%;
11
+ left: 50%;
12
+ transform: translate(-50%, -50%);
13
+ z-index: 10;
14
+ text-align: center;
15
+ }
16
+
17
+ h1 {
18
+ font-size: 3.2em;
19
+ color: #fff;
20
+ text-shadow: 0 2px 10px rgba(0, 0, 0, 0.5);
21
+ }
22
+
23
+ .controls {
24
+ display: flex;
25
+ gap: 1rem;
26
+ justify-content: center;
27
+ align-items: center;
28
+ margin-top: 2rem;
29
+ }
30
+
31
+ button {
32
+ padding: 0.8em 2em;
33
+ font-size: 1.1em;
34
+ border: none;
35
+ border-radius: 8px;
36
+ background: #e94560;
37
+ color: #fff;
38
+ cursor: pointer;
39
+ transition: background 0.2s;
40
+ }
41
+
42
+ button:hover {
43
+ background: #ff6b81;
44
+ }
45
+
46
+ select {
47
+ padding: 0.8em 1em;
48
+ font-size: 1em;
49
+ border-radius: 8px;
50
+ border: 1px solid #444;
51
+ background: #16213e;
52
+ color: #fff;
53
+ cursor: pointer;
54
+ }
@@ -0,0 +1,69 @@
1
+ import Particles, { ParticlesProvider } from "@tsparticles/inferno";
2
+ import { confetti } from "@tsparticles/confetti";
3
+ import type { Engine } from "@tsparticles/engine";
4
+ import { Component } from "inferno";
5
+ import "./App.css";
6
+
7
+ function randomInRange(min: number, max: number): number {
8
+ return Math.random() * (max - min) + min;
9
+ }
10
+
11
+ interface AppState {
12
+ mode: string;
13
+ }
14
+
15
+ export default class App extends Component<{}, AppState> {
16
+ state = { mode: "cannon" };
17
+
18
+ private fireConfetti = () => {
19
+ switch (this.state.mode) {
20
+ case "cannon":
21
+ confetti({ particleCount: 100, spread: 70, origin: { y: 0.6 } });
22
+ break;
23
+ case "waterfall": {
24
+ const duration = 3000;
25
+ const end = Date.now() + duration;
26
+ const interval = setInterval(() => {
27
+ if (Date.now() > end) {
28
+ clearInterval(interval);
29
+ return;
30
+ }
31
+ confetti({ particleCount: 10, angle: 60, spread: 55, origin: { x: 0, y: 0.6 } });
32
+ confetti({ particleCount: 10, angle: 120, spread: 55, origin: { x: 1, y: 0.6 } });
33
+ }, 100);
34
+ break;
35
+ }
36
+ case "random":
37
+ confetti({
38
+ angle: randomInRange(55, 125),
39
+ spread: randomInRange(50, 70),
40
+ particleCount: randomInRange(50, 100),
41
+ origin: { y: 0.6 },
42
+ });
43
+ break;
44
+ }
45
+ };
46
+
47
+ private handleModeChange = (e: Event) => {
48
+ this.setState({ mode: (e.target as HTMLSelectElement).value });
49
+ };
50
+
51
+ render() {
52
+ return (
53
+ <ParticlesProvider init={async (engine: Engine): Promise<void> => {}}>
54
+ <div id="app">
55
+ <h1>Confetti!</h1>
56
+ <div class="controls">
57
+ <button onClick={this.fireConfetti}>Fire Confetti</button>
58
+ <select value={this.state.mode} onChange={this.handleModeChange}>
59
+ <option value="cannon">Cannon</option>
60
+ <option value="waterfall">Waterfall</option>
61
+ <option value="random">Random</option>
62
+ </select>
63
+ </div>
64
+ </div>
65
+ <Particles id="tsparticles" options={{}} />
66
+ </ParticlesProvider>
67
+ );
68
+ }
69
+ }
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "{{packageName}}",
3
+ "private": true,
4
+ "version": "1.0.0",
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite",
8
+ "build": "tsc && vite build",
9
+ "preview": "vite preview"
10
+ },
11
+ "dependencies": {
12
+ "jquery": "^3.7.1",
13
+ "@tsparticles/jquery": "^4.1.3",
14
+ "@tsparticles/confetti": "^4.1.3",
15
+ "@tsparticles/engine": "^4.1.3"
16
+ },
17
+ "devDependencies": {
18
+ "typescript": "^5.7.2",
19
+ "vite": "^6.0.0"
20
+ }
21
+ }
@@ -0,0 +1,49 @@
1
+ import $ from "jquery";
2
+ import "@tsparticles/jquery";
3
+ import { tsParticles } from "@tsparticles/engine";
4
+ import { confetti } from "@tsparticles/confetti";
5
+ import "./style.css";
6
+
7
+ function randomInRange(min: number, max: number): number {
8
+ return Math.random() * (max - min) + min;
9
+ }
10
+
11
+ (async () => {
12
+ $("#tsparticles").particles().load({});
13
+
14
+ const fireBtn = document.getElementById("fireBtn") as HTMLButtonElement;
15
+ const modeSelect = document.getElementById("modeSelect") as HTMLSelectElement;
16
+
17
+ fireBtn.addEventListener("click", () => {
18
+ const mode = modeSelect.value;
19
+
20
+ switch (mode) {
21
+ case "cannon":
22
+ confetti({ particleCount: 100, spread: 70, origin: { y: 0.6 } });
23
+ break;
24
+
25
+ case "waterfall": {
26
+ const duration = 3000;
27
+ const end = Date.now() + duration;
28
+ const interval = setInterval(() => {
29
+ if (Date.now() > end) {
30
+ clearInterval(interval);
31
+ return;
32
+ }
33
+ confetti({ particleCount: 10, angle: 60, spread: 55, origin: { x: 0, y: 0.6 } });
34
+ confetti({ particleCount: 10, angle: 120, spread: 55, origin: { x: 1, y: 0.6 } });
35
+ }, 100);
36
+ break;
37
+ }
38
+
39
+ case "random":
40
+ confetti({
41
+ angle: randomInRange(55, 125),
42
+ spread: randomInRange(50, 70),
43
+ particleCount: randomInRange(50, 100),
44
+ origin: { y: 0.6 },
45
+ });
46
+ break;
47
+ }
48
+ });
49
+ })();
@@ -0,0 +1,54 @@
1
+ body {
2
+ margin: 0;
3
+ overflow: hidden;
4
+ background: #1a1a2e;
5
+ font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
6
+ }
7
+
8
+ #app {
9
+ position: absolute;
10
+ top: 50%;
11
+ left: 50%;
12
+ transform: translate(-50%, -50%);
13
+ z-index: 10;
14
+ text-align: center;
15
+ }
16
+
17
+ h1 {
18
+ font-size: 3.2em;
19
+ color: #fff;
20
+ text-shadow: 0 2px 10px rgba(0, 0, 0, 0.5);
21
+ }
22
+
23
+ .controls {
24
+ display: flex;
25
+ gap: 1rem;
26
+ justify-content: center;
27
+ align-items: center;
28
+ margin-top: 2rem;
29
+ }
30
+
31
+ button {
32
+ padding: 0.8em 2em;
33
+ font-size: 1.1em;
34
+ border: none;
35
+ border-radius: 8px;
36
+ background: #e94560;
37
+ color: #fff;
38
+ cursor: pointer;
39
+ transition: background 0.2s;
40
+ }
41
+
42
+ button:hover {
43
+ background: #ff6b81;
44
+ }
45
+
46
+ select {
47
+ padding: 0.8em 1em;
48
+ font-size: 1em;
49
+ border-radius: 8px;
50
+ border: 1px solid #444;
51
+ background: #16213e;
52
+ color: #fff;
53
+ cursor: pointer;
54
+ }
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "{{packageName}}",
3
+ "private": true,
4
+ "version": "1.0.0",
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite",
8
+ "build": "tsc && vite build",
9
+ "preview": "vite preview"
10
+ },
11
+ "dependencies": {
12
+ "lit": "^3.5.0",
13
+ "@tsparticles/lit": "^4.1.3",
14
+ "@tsparticles/confetti": "^4.1.3",
15
+ "@tsparticles/engine": "^4.1.3"
16
+ },
17
+ "devDependencies": {
18
+ "typescript": "^5.7.2",
19
+ "vite": "^6.0.0"
20
+ }
21
+ }
@@ -0,0 +1,68 @@
1
+ import { LitElement, html } from "lit";
2
+ import { customElement, state } from "lit/decorators.js";
3
+ import { initParticlesEngine } from "@tsparticles/lit";
4
+ import { confetti } from "@tsparticles/confetti";
5
+
6
+ function randomInRange(min: number, max: number): number {
7
+ return Math.random() * (max - min) + min;
8
+ }
9
+
10
+ @customElement("my-app")
11
+ export class MyApp extends LitElement {
12
+ @state()
13
+ private mode = "cannon";
14
+
15
+ constructor() {
16
+ super();
17
+ void initParticlesEngine(() => Promise.resolve());
18
+ }
19
+
20
+ private fireConfetti(): void {
21
+ switch (this.mode) {
22
+ case "cannon":
23
+ confetti({ particleCount: 100, spread: 70, origin: { y: 0.6 } });
24
+ break;
25
+ case "waterfall": {
26
+ const duration = 3000;
27
+ const end = Date.now() + duration;
28
+ const interval = setInterval(() => {
29
+ if (Date.now() > end) {
30
+ clearInterval(interval);
31
+ return;
32
+ }
33
+ confetti({ particleCount: 10, angle: 60, spread: 55, origin: { x: 0, y: 0.6 } });
34
+ confetti({ particleCount: 10, angle: 120, spread: 55, origin: { x: 1, y: 0.6 } });
35
+ }, 100);
36
+ break;
37
+ }
38
+ case "random":
39
+ confetti({
40
+ angle: randomInRange(55, 125),
41
+ spread: randomInRange(50, 70),
42
+ particleCount: randomInRange(50, 100),
43
+ origin: { y: 0.6 },
44
+ });
45
+ break;
46
+ }
47
+ }
48
+
49
+ private handleModeChange(e: Event): void {
50
+ this.mode = (e.target as HTMLSelectElement).value;
51
+ }
52
+
53
+ render() {
54
+ return html`
55
+ <div id="app">
56
+ <h1>Confetti!</h1>
57
+ <div class="controls">
58
+ <button @click=${this.fireConfetti}>Fire Confetti</button>
59
+ <select @change=${this.handleModeChange}>
60
+ <option value="cannon" ?selected=${this.mode === "cannon"}>Cannon</option>
61
+ <option value="waterfall" ?selected=${this.mode === "waterfall"}>Waterfall</option>
62
+ <option value="random" ?selected=${this.mode === "random"}>Random</option>
63
+ </select>
64
+ </div>
65
+ </div>
66
+ `;
67
+ }
68
+ }
@@ -0,0 +1,24 @@
1
+ {
2
+ "name": "{{packageName}}",
3
+ "private": true,
4
+ "version": "1.0.0",
5
+ "scripts": {
6
+ "dev": "next dev",
7
+ "build": "next build",
8
+ "preview": "next start"
9
+ },
10
+ "dependencies": {
11
+ "next": "^15.3.2",
12
+ "react": "^19.2.5",
13
+ "react-dom": "^19.2.5",
14
+ "@tsparticles/nextjs": "^4.1.3",
15
+ "@tsparticles/confetti": "^4.1.3",
16
+ "@tsparticles/engine": "^4.1.3"
17
+ },
18
+ "devDependencies": {
19
+ "typescript": "^6.0.3",
20
+ "@types/node": "^22.15.3",
21
+ "@types/react": "^19.1.4",
22
+ "@types/react-dom": "^19.1.3"
23
+ }
24
+ }
@@ -0,0 +1,59 @@
1
+ "use client";
2
+
3
+ import { useState } from "react";
4
+ import { NextParticles } from "@tsparticles/nextjs";
5
+ import { confetti } from "@tsparticles/confetti";
6
+
7
+ function randomInRange(min: number, max: number): number {
8
+ return Math.random() * (max - min) + min;
9
+ }
10
+
11
+ export default function Home() {
12
+ const [mode, setMode] = useState("cannon");
13
+
14
+ function fireConfetti() {
15
+ switch (mode) {
16
+ case "cannon":
17
+ confetti({ particleCount: 100, spread: 70, origin: { y: 0.6 } });
18
+ break;
19
+ case "waterfall": {
20
+ const duration = 3000;
21
+ const end = Date.now() + duration;
22
+ const interval = setInterval(() => {
23
+ if (Date.now() > end) {
24
+ clearInterval(interval);
25
+ return;
26
+ }
27
+ confetti({ particleCount: 10, angle: 60, spread: 55, origin: { x: 0, y: 0.6 } });
28
+ confetti({ particleCount: 10, angle: 120, spread: 55, origin: { x: 1, y: 0.6 } });
29
+ }, 100);
30
+ break;
31
+ }
32
+ case "random":
33
+ confetti({
34
+ angle: randomInRange(55, 125),
35
+ spread: randomInRange(50, 70),
36
+ particleCount: randomInRange(50, 100),
37
+ origin: { y: 0.6 },
38
+ });
39
+ break;
40
+ }
41
+ }
42
+
43
+ return (
44
+ <div>
45
+ <div id="app">
46
+ <h1>Confetti!</h1>
47
+ <div className="controls">
48
+ <button onClick={fireConfetti}>Fire Confetti</button>
49
+ <select value={mode} onChange={(e) => setMode(e.target.value)}>
50
+ <option value="cannon">Cannon</option>
51
+ <option value="waterfall">Waterfall</option>
52
+ <option value="random">Random</option>
53
+ </select>
54
+ </div>
55
+ </div>
56
+ <NextParticles id="tsparticles" options={{}} />
57
+ </div>
58
+ );
59
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "name": "{{packageName}}",
3
+ "private": true,
4
+ "version": "1.0.0",
5
+ "scripts": {
6
+ "dev": "nuxt",
7
+ "build": "nuxt build",
8
+ "preview": "nuxt start"
9
+ },
10
+ "dependencies": {
11
+ "nuxt": "^2.18.1",
12
+ "@tsparticles/nuxt2": "^4.1.3",
13
+ "@tsparticles/confetti": "^4.1.3",
14
+ "@tsparticles/engine": "^4.1.3"
15
+ }
16
+ }
@@ -0,0 +1,72 @@
1
+ <template>
2
+ <div id="app">
3
+ <h1>Confetti!</h1>
4
+ <div class="controls">
5
+ <button @click="fireConfetti">Fire Confetti</button>
6
+ <select v-model="mode">
7
+ <option value="cannon">Cannon</option>
8
+ <option value="waterfall">Waterfall</option>
9
+ <option value="random">Random</option>
10
+ </select>
11
+ </div>
12
+ <Particles id="tsparticles" :options="options" />
13
+ </div>
14
+ </template>
15
+
16
+ <script>
17
+ import { confetti } from "@tsparticles/confetti";
18
+
19
+ function randomInRange(min, max) {
20
+ return Math.random() * (max - min) + min;
21
+ }
22
+
23
+ export default {
24
+ name: "HomePage",
25
+ data() {
26
+ return {
27
+ mode: "cannon",
28
+ options: {},
29
+ };
30
+ },
31
+ methods: {
32
+ fireConfetti() {
33
+ switch (this.mode) {
34
+ case "cannon":
35
+ confetti({ particleCount: 100, spread: 70, origin: { y: 0.6 } });
36
+ break;
37
+ case "waterfall": {
38
+ const duration = 3000;
39
+ const end = Date.now() + duration;
40
+ const interval = setInterval(() => {
41
+ if (Date.now() > end) {
42
+ clearInterval(interval);
43
+ return;
44
+ }
45
+ confetti({ particleCount: 10, angle: 60, spread: 55, origin: { x: 0, y: 0.6 } });
46
+ confetti({ particleCount: 10, angle: 120, spread: 55, origin: { x: 1, y: 0.6 } });
47
+ }, 100);
48
+ break;
49
+ }
50
+ case "random":
51
+ confetti({
52
+ angle: randomInRange(55, 125),
53
+ spread: randomInRange(50, 70),
54
+ particleCount: randomInRange(50, 100),
55
+ origin: { y: 0.6 },
56
+ });
57
+ break;
58
+ }
59
+ },
60
+ },
61
+ };
62
+ </script>
63
+
64
+ <style>
65
+ body { margin: 0; overflow: hidden; background: #1a1a2e; font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; }
66
+ #app { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); z-index: 10; text-align: center; color: #fff; }
67
+ h1 { font-size: 3.2em; text-shadow: 0 2px 10px rgba(0, 0, 0, 0.5); }
68
+ .controls { display: flex; gap: 1rem; justify-content: center; align-items: center; margin-top: 2rem; }
69
+ button { padding: 0.8em 2em; font-size: 1.1em; border: none; border-radius: 8px; background: #e94560; color: #fff; cursor: pointer; transition: background 0.2s; }
70
+ button:hover { background: #ff6b81; }
71
+ select { padding: 0.8em 1em; font-size: 1em; border-radius: 8px; border: 1px solid #444; background: #16213e; color: #fff; cursor: pointer; }
72
+ </style>
@@ -0,0 +1,65 @@
1
+ <template>
2
+ <div id="app">
3
+ <h1>Confetti!</h1>
4
+ <div class="controls">
5
+ <button @click="fireConfetti">Fire Confetti</button>
6
+ <select v-model="mode">
7
+ <option value="cannon">Cannon</option>
8
+ <option value="waterfall">Waterfall</option>
9
+ <option value="random">Random</option>
10
+ </select>
11
+ </div>
12
+ <Particles id="tsparticles" :options="options" />
13
+ </div>
14
+ </template>
15
+
16
+ <script setup>
17
+ import { ref } from "vue";
18
+ import { confetti } from "@tsparticles/confetti";
19
+
20
+ function randomInRange(min, max) {
21
+ return Math.random() * (max - min) + min;
22
+ }
23
+
24
+ const mode = ref("cannon");
25
+ const options = {};
26
+
27
+ function fireConfetti() {
28
+ switch (mode.value) {
29
+ case "cannon":
30
+ confetti({ particleCount: 100, spread: 70, origin: { y: 0.6 } });
31
+ break;
32
+ case "waterfall": {
33
+ const duration = 3000;
34
+ const end = Date.now() + duration;
35
+ const interval = setInterval(() => {
36
+ if (Date.now() > end) {
37
+ clearInterval(interval);
38
+ return;
39
+ }
40
+ confetti({ particleCount: 10, angle: 60, spread: 55, origin: { x: 0, y: 0.6 } });
41
+ confetti({ particleCount: 10, angle: 120, spread: 55, origin: { x: 1, y: 0.6 } });
42
+ }, 100);
43
+ break;
44
+ }
45
+ case "random":
46
+ confetti({
47
+ angle: randomInRange(55, 125),
48
+ spread: randomInRange(50, 70),
49
+ particleCount: randomInRange(50, 100),
50
+ origin: { y: 0.6 },
51
+ });
52
+ break;
53
+ }
54
+ }
55
+ </script>
56
+
57
+ <style>
58
+ body { margin: 0; overflow: hidden; background: #1a1a2e; font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; }
59
+ #app { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); z-index: 10; text-align: center; color: #fff; }
60
+ h1 { font-size: 3.2em; text-shadow: 0 2px 10px rgba(0, 0, 0, 0.5); }
61
+ .controls { display: flex; gap: 1rem; justify-content: center; align-items: center; margin-top: 2rem; }
62
+ button { padding: 0.8em 2em; font-size: 1.1em; border: none; border-radius: 8px; background: #e94560; color: #fff; cursor: pointer; transition: background 0.2s; }
63
+ button:hover { background: #ff6b81; }
64
+ select { padding: 0.8em 1em; font-size: 1em; border-radius: 8px; border: 1px solid #444; background: #16213e; color: #fff; cursor: pointer; }
65
+ </style>
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "{{packageName}}",
3
+ "private": true,
4
+ "version": "1.0.0",
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "nuxt dev",
8
+ "build": "nuxt build",
9
+ "preview": "nuxt preview"
10
+ },
11
+ "dependencies": {
12
+ "nuxt": "^3.17.3",
13
+ "@tsparticles/nuxt3": "^4.1.3",
14
+ "@tsparticles/confetti": "^4.1.3",
15
+ "@tsparticles/engine": "^4.1.3"
16
+ },
17
+ "devDependencies": {
18
+ "typescript": "^6.0.3"
19
+ }
20
+ }
@@ -0,0 +1,65 @@
1
+ <template>
2
+ <div id="app">
3
+ <h1>Confetti!</h1>
4
+ <div class="controls">
5
+ <button @click="fireConfetti">Fire Confetti</button>
6
+ <select v-model="mode">
7
+ <option value="cannon">Cannon</option>
8
+ <option value="waterfall">Waterfall</option>
9
+ <option value="random">Random</option>
10
+ </select>
11
+ </div>
12
+ <Particles id="tsparticles" :options="options" />
13
+ </div>
14
+ </template>
15
+
16
+ <script setup>
17
+ import { ref } from "vue";
18
+ import { confetti } from "@tsparticles/confetti";
19
+
20
+ function randomInRange(min, max) {
21
+ return Math.random() * (max - min) + min;
22
+ }
23
+
24
+ const mode = ref("cannon");
25
+ const options = {};
26
+
27
+ function fireConfetti() {
28
+ switch (mode.value) {
29
+ case "cannon":
30
+ confetti({ particleCount: 100, spread: 70, origin: { y: 0.6 } });
31
+ break;
32
+ case "waterfall": {
33
+ const duration = 3000;
34
+ const end = Date.now() + duration;
35
+ const interval = setInterval(() => {
36
+ if (Date.now() > end) {
37
+ clearInterval(interval);
38
+ return;
39
+ }
40
+ confetti({ particleCount: 10, angle: 60, spread: 55, origin: { x: 0, y: 0.6 } });
41
+ confetti({ particleCount: 10, angle: 120, spread: 55, origin: { x: 1, y: 0.6 } });
42
+ }, 100);
43
+ break;
44
+ }
45
+ case "random":
46
+ confetti({
47
+ angle: randomInRange(55, 125),
48
+ spread: randomInRange(50, 70),
49
+ particleCount: randomInRange(50, 100),
50
+ origin: { y: 0.6 },
51
+ });
52
+ break;
53
+ }
54
+ }
55
+ </script>
56
+
57
+ <style>
58
+ body { margin: 0; overflow: hidden; background: #1a1a2e; font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; }
59
+ #app { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); z-index: 10; text-align: center; color: #fff; }
60
+ h1 { font-size: 3.2em; text-shadow: 0 2px 10px rgba(0, 0, 0, 0.5); }
61
+ .controls { display: flex; gap: 1rem; justify-content: center; align-items: center; margin-top: 2rem; }
62
+ button { padding: 0.8em 2em; font-size: 1.1em; border: none; border-radius: 8px; background: #e94560; color: #fff; cursor: pointer; transition: background 0.2s; }
63
+ button:hover { background: #ff6b81; }
64
+ select { padding: 0.8em 1em; font-size: 1em; border-radius: 8px; border: 1px solid #444; background: #16213e; color: #fff; cursor: pointer; }
65
+ </style>
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "{{packageName}}",
3
+ "private": true,
4
+ "version": "1.0.0",
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "nuxt dev",
8
+ "build": "nuxt build",
9
+ "preview": "nuxt preview"
10
+ },
11
+ "dependencies": {
12
+ "nuxt": "^4.4.2",
13
+ "@tsparticles/nuxt4": "^4.1.3",
14
+ "@tsparticles/confetti": "^4.1.3",
15
+ "@tsparticles/engine": "^4.1.3"
16
+ },
17
+ "devDependencies": {
18
+ "typescript": "^6.0.3"
19
+ }
20
+ }