@fylib/adapter-angular 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/dist/base/base-component.d.ts +18 -0
- package/dist/base/base-component.js +36 -0
- package/dist/base/interaction.utils.d.ts +7 -0
- package/dist/base/interaction.utils.js +34 -0
- package/dist/base/interaction.utils.test.d.ts +1 -0
- package/dist/base/interaction.utils.test.js +25 -0
- package/dist/components/accordion.component.d.ts +32 -0
- package/dist/components/accordion.component.js +337 -0
- package/dist/components/badge.component.d.ts +10 -0
- package/dist/components/badge.component.js +112 -0
- package/dist/components/button.component.d.ts +33 -0
- package/dist/components/button.component.js +272 -0
- package/dist/components/card.component.d.ts +29 -0
- package/dist/components/card.component.js +236 -0
- package/dist/components/chart.component.d.ts +39 -0
- package/dist/components/chart.component.js +307 -0
- package/dist/components/icon.component.d.ts +18 -0
- package/dist/components/icon.component.js +144 -0
- package/dist/components/input.component.d.ts +50 -0
- package/dist/components/input.component.js +383 -0
- package/dist/components/modal.component.d.ts +46 -0
- package/dist/components/modal.component.js +404 -0
- package/dist/components/nav-link.component.d.ts +11 -0
- package/dist/components/nav-link.component.js +121 -0
- package/dist/components/notification-menu.component.d.ts +68 -0
- package/dist/components/notification-menu.component.js +695 -0
- package/dist/components/select.component.d.ts +52 -0
- package/dist/components/select.component.js +395 -0
- package/dist/components/table.component.d.ts +55 -0
- package/dist/components/table.component.js +643 -0
- package/dist/components/text.component.d.ts +8 -0
- package/dist/components/text.component.js +58 -0
- package/dist/components/toast.component.d.ts +27 -0
- package/dist/components/toast.component.js +260 -0
- package/dist/directives/animation.directive.d.ts +5 -0
- package/dist/directives/animation.directive.js +34 -0
- package/dist/directives/theme-vars.directive.d.ts +7 -0
- package/dist/directives/theme-vars.directive.js +70 -0
- package/dist/directives/wallpaper.directive.d.ts +28 -0
- package/dist/directives/wallpaper.directive.js +195 -0
- package/dist/effects/confetti.plugin.d.ts +1 -0
- package/dist/effects/confetti.plugin.js +151 -0
- package/dist/effects/extra-effects.plugin.d.ts +3 -0
- package/dist/effects/extra-effects.plugin.js +288 -0
- package/dist/effects/hearts.plugin.d.ts +1 -0
- package/dist/effects/hearts.plugin.js +172 -0
- package/dist/effects/register-all.d.ts +1 -0
- package/dist/effects/register-all.js +16 -0
- package/dist/effects/ui-effects.plugin.d.ts +1 -0
- package/dist/effects/ui-effects.plugin.js +134 -0
- package/dist/icons/providers/fontawesome.provider.d.ts +3 -0
- package/dist/icons/providers/fontawesome.provider.js +25 -0
- package/dist/icons/providers/mdi.provider.d.ts +3 -0
- package/dist/icons/providers/mdi.provider.js +13 -0
- package/dist/icons/providers/phosphor.provider.d.ts +3 -0
- package/dist/icons/providers/phosphor.provider.js +17 -0
- package/dist/icons/registry.d.ts +22 -0
- package/dist/icons/registry.js +12 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.js +29 -0
- package/dist/layouts/layout.component.d.ts +24 -0
- package/dist/layouts/layout.component.js +255 -0
- package/dist/layouts/slot.component.d.ts +61 -0
- package/dist/layouts/slot.component.js +937 -0
- package/dist/providers.d.ts +12 -0
- package/dist/providers.js +18 -0
- package/dist/services/fylib.service.d.ts +31 -0
- package/dist/services/fylib.service.js +214 -0
- package/dist/services/notification.service.d.ts +27 -0
- package/dist/services/notification.service.js +118 -0
- package/dist/services/sse.service.d.ts +16 -0
- package/dist/services/sse.service.js +109 -0
- package/dist/services/webclient.service.d.ts +38 -0
- package/dist/services/webclient.service.js +144 -0
- package/package.json +43 -0
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { animationEngine } from '@fylib/animation';
|
|
2
|
+
let registered = false;
|
|
3
|
+
function createCanvas() {
|
|
4
|
+
const existing = document.querySelector('#fy-confetti-canvas');
|
|
5
|
+
if (existing) {
|
|
6
|
+
return existing;
|
|
7
|
+
}
|
|
8
|
+
const canvas = document.createElement('canvas');
|
|
9
|
+
canvas.id = 'fy-confetti-canvas';
|
|
10
|
+
canvas.style.position = 'fixed';
|
|
11
|
+
canvas.style.top = '0';
|
|
12
|
+
canvas.style.left = '0';
|
|
13
|
+
canvas.style.pointerEvents = 'none';
|
|
14
|
+
canvas.style.zIndex = '9999';
|
|
15
|
+
canvas.width = window.innerWidth;
|
|
16
|
+
canvas.height = window.innerHeight;
|
|
17
|
+
window.addEventListener('resize', () => {
|
|
18
|
+
canvas.width = window.innerWidth;
|
|
19
|
+
canvas.height = window.innerHeight;
|
|
20
|
+
});
|
|
21
|
+
document.body.appendChild(canvas);
|
|
22
|
+
return canvas;
|
|
23
|
+
}
|
|
24
|
+
const activeLoops = new Map();
|
|
25
|
+
function randomColor() {
|
|
26
|
+
const colors = ['#f97316', '#22c55e', '#3b82f6', '#e11d48', '#eab308', '#6366f1'];
|
|
27
|
+
const index = Math.floor(Math.random() * colors.length);
|
|
28
|
+
return colors[index];
|
|
29
|
+
}
|
|
30
|
+
function createParticles(count, width, height, speed = 1) {
|
|
31
|
+
const particles = [];
|
|
32
|
+
for (let i = 0; i < count; i++) {
|
|
33
|
+
particles.push({
|
|
34
|
+
x: Math.random() * width,
|
|
35
|
+
y: -Math.random() * height,
|
|
36
|
+
vx: (Math.random() - 0.5) * 4 * speed,
|
|
37
|
+
vy: (Math.random() * 4 + 2) * speed,
|
|
38
|
+
size: Math.random() * 8 + 4,
|
|
39
|
+
color: randomColor(),
|
|
40
|
+
rotation: Math.random() * Math.PI * 2,
|
|
41
|
+
vr: (Math.random() - 0.5) * 0.2
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
return particles;
|
|
45
|
+
}
|
|
46
|
+
function renderConfettiOnce(count, duration, speed = 1, loop = false, effectId) {
|
|
47
|
+
if (typeof window === 'undefined' || typeof document === 'undefined') {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
const canvas = createCanvas();
|
|
51
|
+
const ctx = canvas.getContext('2d');
|
|
52
|
+
if (!ctx) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
// Se já existe um loop com esse ID, para ele antes de começar um novo
|
|
56
|
+
if (effectId && activeLoops.has(effectId)) {
|
|
57
|
+
activeLoops.get(effectId)?.stop();
|
|
58
|
+
}
|
|
59
|
+
const particles = createParticles(count, canvas.width, canvas.height, speed);
|
|
60
|
+
const start = performance.now();
|
|
61
|
+
let animationId;
|
|
62
|
+
let isRunning = true;
|
|
63
|
+
const stop = () => {
|
|
64
|
+
isRunning = false;
|
|
65
|
+
cancelAnimationFrame(animationId);
|
|
66
|
+
if (effectId)
|
|
67
|
+
activeLoops.delete(effectId);
|
|
68
|
+
};
|
|
69
|
+
if (effectId && loop) {
|
|
70
|
+
activeLoops.set(effectId, { stop });
|
|
71
|
+
}
|
|
72
|
+
function frame(now) {
|
|
73
|
+
if (!isRunning)
|
|
74
|
+
return;
|
|
75
|
+
const elapsed = now - start;
|
|
76
|
+
if (!ctx) {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
80
|
+
// Stop recycling after duration, but let existing particles finish falling
|
|
81
|
+
const isRecycling = loop || elapsed < duration;
|
|
82
|
+
let anyVisible = false;
|
|
83
|
+
particles.forEach(p => {
|
|
84
|
+
p.x += p.vx;
|
|
85
|
+
p.y += p.vy;
|
|
86
|
+
p.vy += 0.05 * speed;
|
|
87
|
+
p.rotation += p.vr;
|
|
88
|
+
if (p.y - p.size > canvas.height) {
|
|
89
|
+
if (isRecycling) {
|
|
90
|
+
p.y = -p.size;
|
|
91
|
+
p.x = Math.random() * canvas.width;
|
|
92
|
+
p.vy = (Math.random() * 4 + 2) * speed;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
anyVisible = true;
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
if (anyVisible || isRecycling) {
|
|
100
|
+
particles.forEach(p => {
|
|
101
|
+
if (!ctx) {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
ctx.save();
|
|
105
|
+
ctx.translate(p.x, p.y);
|
|
106
|
+
ctx.rotate(p.rotation);
|
|
107
|
+
ctx.fillStyle = p.color;
|
|
108
|
+
ctx.fillRect(-p.size / 2, -p.size / 2, p.size, p.size);
|
|
109
|
+
ctx.restore();
|
|
110
|
+
});
|
|
111
|
+
animationId = requestAnimationFrame(frame);
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
if (!ctx) {
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
118
|
+
if (effectId)
|
|
119
|
+
activeLoops.delete(effectId);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
animationId = requestAnimationFrame(frame);
|
|
123
|
+
}
|
|
124
|
+
const confettiPlugin = {
|
|
125
|
+
name: 'confetti-renderer',
|
|
126
|
+
renderEffect(effect) {
|
|
127
|
+
if (effect.name === 'confetti' && effect.type === 'global') {
|
|
128
|
+
const params = effect.params || {};
|
|
129
|
+
if (params['stop'] && params['id']) {
|
|
130
|
+
activeLoops.get(params['id'])?.stop();
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
const count = params['intensity'] || 80;
|
|
134
|
+
const duration = params['duration'] || 1200;
|
|
135
|
+
const speed = params['speed'] || 1;
|
|
136
|
+
const loop = !!params['loop'];
|
|
137
|
+
const id = params['id'];
|
|
138
|
+
renderConfettiOnce(count, duration, speed, loop, id);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
export function registerConfettiPlugin() {
|
|
143
|
+
if (registered) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
if (typeof window === 'undefined' || typeof document === 'undefined') {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
animationEngine.registerGlobalEffectPlugin(confettiPlugin);
|
|
150
|
+
registered = true;
|
|
151
|
+
}
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
import { animationEngine } from '@fylib/animation';
|
|
2
|
+
let registered = false;
|
|
3
|
+
const activeLoops = new Map();
|
|
4
|
+
function createCanvas(id) {
|
|
5
|
+
if (typeof window === 'undefined' || typeof document === 'undefined')
|
|
6
|
+
return null;
|
|
7
|
+
const canvasId = `fy-${id}-canvas`;
|
|
8
|
+
const existing = document.querySelector(`#${canvasId}`);
|
|
9
|
+
if (existing)
|
|
10
|
+
return existing;
|
|
11
|
+
const canvas = document.createElement('canvas');
|
|
12
|
+
canvas.id = canvasId;
|
|
13
|
+
canvas.style.position = 'fixed';
|
|
14
|
+
canvas.style.top = '0';
|
|
15
|
+
canvas.style.left = '0';
|
|
16
|
+
canvas.style.pointerEvents = 'none';
|
|
17
|
+
canvas.style.zIndex = '9999';
|
|
18
|
+
canvas.width = window.innerWidth;
|
|
19
|
+
canvas.height = window.innerHeight;
|
|
20
|
+
window.addEventListener('resize', () => {
|
|
21
|
+
canvas.width = window.innerWidth;
|
|
22
|
+
canvas.height = window.innerHeight;
|
|
23
|
+
});
|
|
24
|
+
document.body.appendChild(canvas);
|
|
25
|
+
return canvas;
|
|
26
|
+
}
|
|
27
|
+
function renderSnow(count, speed, loop, id) {
|
|
28
|
+
const canvas = createCanvas('snow');
|
|
29
|
+
if (!canvas)
|
|
30
|
+
return;
|
|
31
|
+
const ctx = canvas.getContext('2d');
|
|
32
|
+
if (!ctx)
|
|
33
|
+
return;
|
|
34
|
+
const particles = [];
|
|
35
|
+
for (let i = 0; i < count; i++) {
|
|
36
|
+
particles.push({
|
|
37
|
+
x: Math.random() * canvas.width,
|
|
38
|
+
y: Math.random() * canvas.height,
|
|
39
|
+
vx: (Math.random() - 0.5) * speed,
|
|
40
|
+
vy: (Math.random() * 2 + 1) * speed,
|
|
41
|
+
radius: Math.random() * 3 + 1,
|
|
42
|
+
opacity: Math.random() * 0.5 + 0.3
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
let animationId;
|
|
46
|
+
const stop = () => {
|
|
47
|
+
cancelAnimationFrame(animationId);
|
|
48
|
+
activeLoops.delete(id);
|
|
49
|
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
50
|
+
};
|
|
51
|
+
activeLoops.set(id, { stop });
|
|
52
|
+
function frame() {
|
|
53
|
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
54
|
+
ctx.fillStyle = 'white';
|
|
55
|
+
particles.forEach(p => {
|
|
56
|
+
p.x += p.vx;
|
|
57
|
+
p.y += p.vy;
|
|
58
|
+
if (p.y > canvas.height) {
|
|
59
|
+
p.y = -p.radius;
|
|
60
|
+
p.x = Math.random() * canvas.width;
|
|
61
|
+
}
|
|
62
|
+
ctx.globalAlpha = p.opacity;
|
|
63
|
+
ctx.beginPath();
|
|
64
|
+
ctx.arc(p.x, p.y, p.radius, 0, Math.PI * 2);
|
|
65
|
+
ctx.fill();
|
|
66
|
+
});
|
|
67
|
+
animationId = requestAnimationFrame(frame);
|
|
68
|
+
}
|
|
69
|
+
frame();
|
|
70
|
+
}
|
|
71
|
+
function renderBubbles(count, speed, loop, id) {
|
|
72
|
+
const canvas = createCanvas('bubbles');
|
|
73
|
+
if (!canvas)
|
|
74
|
+
return;
|
|
75
|
+
const ctx = canvas.getContext('2d');
|
|
76
|
+
if (!ctx)
|
|
77
|
+
return;
|
|
78
|
+
const particles = [];
|
|
79
|
+
for (let i = 0; i < count; i++) {
|
|
80
|
+
particles.push({
|
|
81
|
+
x: Math.random() * canvas.width,
|
|
82
|
+
y: canvas.height + Math.random() * 100,
|
|
83
|
+
vy: -(Math.random() * 1 + 0.5) * speed,
|
|
84
|
+
radius: Math.random() * 15 + 5,
|
|
85
|
+
opacity: Math.random() * 0.3 + 0.1,
|
|
86
|
+
hue: 200 + Math.random() * 40 // Blueish
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
let animationId;
|
|
90
|
+
const stop = () => {
|
|
91
|
+
cancelAnimationFrame(animationId);
|
|
92
|
+
activeLoops.delete(id);
|
|
93
|
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
94
|
+
};
|
|
95
|
+
activeLoops.set(id, { stop });
|
|
96
|
+
function frame() {
|
|
97
|
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
98
|
+
particles.forEach(p => {
|
|
99
|
+
p.y += p.vy;
|
|
100
|
+
if (p.y < -p.radius) {
|
|
101
|
+
p.y = canvas.height + p.radius;
|
|
102
|
+
p.x = Math.random() * canvas.width;
|
|
103
|
+
}
|
|
104
|
+
ctx.strokeStyle = `hsla(${p.hue}, 70%, 70%, ${p.opacity})`;
|
|
105
|
+
ctx.lineWidth = 2;
|
|
106
|
+
ctx.beginPath();
|
|
107
|
+
ctx.arc(p.x, p.y, p.radius, 0, Math.PI * 2);
|
|
108
|
+
ctx.stroke();
|
|
109
|
+
// Reflection
|
|
110
|
+
ctx.beginPath();
|
|
111
|
+
ctx.arc(p.x - p.radius * 0.3, p.y - p.radius * 0.3, p.radius * 0.2, 0, Math.PI * 2);
|
|
112
|
+
ctx.fillStyle = `hsla(${p.hue}, 70%, 90%, ${p.opacity})`;
|
|
113
|
+
ctx.fill();
|
|
114
|
+
});
|
|
115
|
+
animationId = requestAnimationFrame(frame);
|
|
116
|
+
}
|
|
117
|
+
frame();
|
|
118
|
+
}
|
|
119
|
+
// --- MATRIX (Nexus) ---
|
|
120
|
+
function renderMatrix(count, speed, loop, id) {
|
|
121
|
+
const canvas = createCanvas('matrix');
|
|
122
|
+
if (!canvas)
|
|
123
|
+
return;
|
|
124
|
+
const ctx = canvas.getContext('2d');
|
|
125
|
+
if (!ctx)
|
|
126
|
+
return;
|
|
127
|
+
const fontSize = 16;
|
|
128
|
+
const columns = Math.floor(canvas.width / fontSize);
|
|
129
|
+
const drops = new Array(columns).fill(1);
|
|
130
|
+
const chars = "0123456789ABCDEFHIJKLMNOPQRSTUVWXYZ@#$%^&*()".split("");
|
|
131
|
+
let animationId;
|
|
132
|
+
const stop = () => {
|
|
133
|
+
cancelAnimationFrame(animationId);
|
|
134
|
+
activeLoops.delete(id);
|
|
135
|
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
136
|
+
};
|
|
137
|
+
activeLoops.set(id, { stop });
|
|
138
|
+
let lastTime = 0;
|
|
139
|
+
const interval = 50 / speed;
|
|
140
|
+
function frame(time) {
|
|
141
|
+
if (time - lastTime > interval) {
|
|
142
|
+
// Usar uma opacidade muito baixa para o rastro, para não escurecer a tela (overlay sutil)
|
|
143
|
+
ctx.fillStyle = "rgba(10, 15, 18, 0.03)";
|
|
144
|
+
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
145
|
+
// Caracteres bem sutis
|
|
146
|
+
ctx.fillStyle = "rgba(34, 197, 94, 0.15)";
|
|
147
|
+
ctx.font = fontSize + "px monospace";
|
|
148
|
+
for (let i = 0; i < drops.length; i++) {
|
|
149
|
+
const text = chars[Math.floor(Math.random() * chars.length)];
|
|
150
|
+
ctx.fillText(text, i * fontSize, drops[i] * fontSize);
|
|
151
|
+
if (drops[i] * fontSize > canvas.height && Math.random() > 0.975) {
|
|
152
|
+
drops[i] = 0;
|
|
153
|
+
}
|
|
154
|
+
drops[i]++;
|
|
155
|
+
}
|
|
156
|
+
lastTime = time;
|
|
157
|
+
}
|
|
158
|
+
animationId = requestAnimationFrame(frame);
|
|
159
|
+
}
|
|
160
|
+
frame(0);
|
|
161
|
+
}
|
|
162
|
+
function renderParticles(count, speed, loop, id, color = '#3b82f6') {
|
|
163
|
+
const canvas = createCanvas('particles');
|
|
164
|
+
if (!canvas)
|
|
165
|
+
return;
|
|
166
|
+
const ctx = canvas.getContext('2d');
|
|
167
|
+
if (!ctx)
|
|
168
|
+
return;
|
|
169
|
+
const particles = [];
|
|
170
|
+
for (let i = 0; i < count; i++) {
|
|
171
|
+
particles.push({
|
|
172
|
+
x: Math.random() * canvas.width,
|
|
173
|
+
y: Math.random() * canvas.height,
|
|
174
|
+
vx: (Math.random() - 0.5) * speed,
|
|
175
|
+
vy: (Math.random() - 0.5) * speed,
|
|
176
|
+
radius: Math.random() * 2 + 1,
|
|
177
|
+
color: color
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
let animationId;
|
|
181
|
+
const stop = () => {
|
|
182
|
+
cancelAnimationFrame(animationId);
|
|
183
|
+
activeLoops.delete(id);
|
|
184
|
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
185
|
+
};
|
|
186
|
+
activeLoops.set(id, { stop });
|
|
187
|
+
function frame() {
|
|
188
|
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
189
|
+
particles.forEach(p => {
|
|
190
|
+
p.x += p.vx;
|
|
191
|
+
p.y += p.vy;
|
|
192
|
+
if (p.x < 0 || p.x > canvas.width)
|
|
193
|
+
p.vx *= -1;
|
|
194
|
+
if (p.y < 0 || p.y > canvas.height)
|
|
195
|
+
p.vy *= -1;
|
|
196
|
+
ctx.beginPath();
|
|
197
|
+
ctx.arc(p.x, p.y, p.radius, 0, Math.PI * 2);
|
|
198
|
+
ctx.fillStyle = p.color;
|
|
199
|
+
ctx.globalAlpha = 0.3;
|
|
200
|
+
ctx.fill();
|
|
201
|
+
});
|
|
202
|
+
animationId = requestAnimationFrame(frame);
|
|
203
|
+
}
|
|
204
|
+
frame();
|
|
205
|
+
}
|
|
206
|
+
// --- STARS (Workbench 3) ---
|
|
207
|
+
function renderStars(count, speed, loop, id) {
|
|
208
|
+
const canvas = createCanvas('stars');
|
|
209
|
+
if (!canvas)
|
|
210
|
+
return;
|
|
211
|
+
const ctx = canvas.getContext('2d');
|
|
212
|
+
if (!ctx)
|
|
213
|
+
return;
|
|
214
|
+
const stars = [];
|
|
215
|
+
for (let i = 0; i < count; i++) {
|
|
216
|
+
stars.push({
|
|
217
|
+
x: Math.random() * canvas.width,
|
|
218
|
+
y: Math.random() * canvas.height,
|
|
219
|
+
size: Math.random() * 1.5,
|
|
220
|
+
twinkle: Math.random() * 0.05 + 0.01,
|
|
221
|
+
opacity: Math.random()
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
let animationId;
|
|
225
|
+
const stop = () => {
|
|
226
|
+
cancelAnimationFrame(animationId);
|
|
227
|
+
activeLoops.delete(id);
|
|
228
|
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
229
|
+
};
|
|
230
|
+
activeLoops.set(id, { stop });
|
|
231
|
+
function frame() {
|
|
232
|
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
233
|
+
stars.forEach(s => {
|
|
234
|
+
s.opacity += s.twinkle;
|
|
235
|
+
if (s.opacity > 1 || s.opacity < 0)
|
|
236
|
+
s.twinkle *= -1;
|
|
237
|
+
ctx.globalAlpha = s.opacity;
|
|
238
|
+
ctx.fillStyle = 'white';
|
|
239
|
+
ctx.beginPath();
|
|
240
|
+
ctx.arc(s.x, s.y, s.size, 0, Math.PI * 2);
|
|
241
|
+
ctx.fill();
|
|
242
|
+
});
|
|
243
|
+
animationId = requestAnimationFrame(frame);
|
|
244
|
+
}
|
|
245
|
+
frame();
|
|
246
|
+
}
|
|
247
|
+
export const extraEffectsPlugin = {
|
|
248
|
+
name: 'extra-effects-renderer',
|
|
249
|
+
renderEffect(effect) {
|
|
250
|
+
if (effect.type !== 'global')
|
|
251
|
+
return;
|
|
252
|
+
const params = effect.params || {};
|
|
253
|
+
const id = params['id'] || 'default-extra-loop';
|
|
254
|
+
if (params['stop'] && params['id']) {
|
|
255
|
+
activeLoops.get(params['id'])?.stop();
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
const count = params['intensity'] || 50;
|
|
259
|
+
const speed = params['speed'] || 1;
|
|
260
|
+
const loop = !!params['loop'];
|
|
261
|
+
switch (effect.name) {
|
|
262
|
+
case 'snow':
|
|
263
|
+
renderSnow(count, speed, loop, id);
|
|
264
|
+
break;
|
|
265
|
+
case 'bubbles':
|
|
266
|
+
renderBubbles(count, speed, loop, id);
|
|
267
|
+
break;
|
|
268
|
+
case 'matrix':
|
|
269
|
+
renderMatrix(count, speed, loop, id);
|
|
270
|
+
break;
|
|
271
|
+
case 'particles':
|
|
272
|
+
renderParticles(count, speed, loop, id);
|
|
273
|
+
break;
|
|
274
|
+
case 'stars':
|
|
275
|
+
renderStars(count, speed, loop, id);
|
|
276
|
+
break;
|
|
277
|
+
case 'aurora':
|
|
278
|
+
renderParticles(count, speed, loop, id, '#5ac8fa');
|
|
279
|
+
break; // Aurora uses light blue particles for now
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
export function registerExtraEffectsPlugin() {
|
|
284
|
+
if (registered)
|
|
285
|
+
return;
|
|
286
|
+
animationEngine.registerGlobalEffectPlugin(extraEffectsPlugin);
|
|
287
|
+
registered = true;
|
|
288
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function registerHeartsPlugin(): void;
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { animationEngine } from '@fylib/animation';
|
|
2
|
+
let registered = false;
|
|
3
|
+
function createCanvas() {
|
|
4
|
+
const existing = document.querySelector('#fy-hearts-canvas');
|
|
5
|
+
if (existing) {
|
|
6
|
+
return existing;
|
|
7
|
+
}
|
|
8
|
+
const canvas = document.createElement('canvas');
|
|
9
|
+
canvas.id = 'fy-hearts-canvas';
|
|
10
|
+
canvas.style.position = 'fixed';
|
|
11
|
+
canvas.style.top = '0';
|
|
12
|
+
canvas.style.left = '0';
|
|
13
|
+
canvas.style.pointerEvents = 'none';
|
|
14
|
+
canvas.style.zIndex = '9999';
|
|
15
|
+
canvas.width = window.innerWidth;
|
|
16
|
+
canvas.height = window.innerHeight;
|
|
17
|
+
window.addEventListener('resize', () => {
|
|
18
|
+
canvas.width = window.innerWidth;
|
|
19
|
+
canvas.height = window.innerHeight;
|
|
20
|
+
});
|
|
21
|
+
document.body.appendChild(canvas);
|
|
22
|
+
return canvas;
|
|
23
|
+
}
|
|
24
|
+
const activeLoops = new Map();
|
|
25
|
+
function getPuffyColors() {
|
|
26
|
+
// Cores do tema finey-puffy-1
|
|
27
|
+
return [
|
|
28
|
+
'#ff85a2', // primary (rosa vibrante)
|
|
29
|
+
'#ffb3c6', // secondary (rosa pastel)
|
|
30
|
+
'#a2d2ff', // success (azul candy)
|
|
31
|
+
'#ff4d6d', // danger (rosa forte)
|
|
32
|
+
'#ffe5ec', // warning (rosa claríssimo)
|
|
33
|
+
'#c8b6ff', // info (roxo lavanda)
|
|
34
|
+
'#ffccd5' // surface (rosa suave)
|
|
35
|
+
];
|
|
36
|
+
}
|
|
37
|
+
function createParticles(count, width, height, speed = 1) {
|
|
38
|
+
const particles = [];
|
|
39
|
+
const colors = getPuffyColors();
|
|
40
|
+
for (let i = 0; i < count; i++) {
|
|
41
|
+
particles.push({
|
|
42
|
+
x: Math.random() * width,
|
|
43
|
+
y: -Math.random() * height,
|
|
44
|
+
vx: (Math.random() - 0.5) * 2 * speed,
|
|
45
|
+
vy: (Math.random() * 2 + 1.5) * speed,
|
|
46
|
+
size: Math.random() * 12 + 8,
|
|
47
|
+
color: colors[Math.floor(Math.random() * colors.length)],
|
|
48
|
+
rotation: Math.random() * Math.PI * 2,
|
|
49
|
+
vr: (Math.random() - 0.5) * 0.05,
|
|
50
|
+
opacity: Math.random() * 0.5 + 0.5
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
return particles;
|
|
54
|
+
}
|
|
55
|
+
function drawHeart(ctx, x, y, size, color, rotation) {
|
|
56
|
+
ctx.save();
|
|
57
|
+
ctx.translate(x, y);
|
|
58
|
+
ctx.rotate(rotation);
|
|
59
|
+
ctx.beginPath();
|
|
60
|
+
// Desenha o coração centralizado no (0,0)
|
|
61
|
+
const d = size;
|
|
62
|
+
ctx.moveTo(0, d / 4);
|
|
63
|
+
ctx.bezierCurveTo(0, 0, -d / 2, 0, -d / 2, d / 4);
|
|
64
|
+
ctx.bezierCurveTo(-d / 2, d / 2, 0, d * 0.75, 0, d);
|
|
65
|
+
ctx.bezierCurveTo(0, d * 0.75, d / 2, d / 2, d / 2, d / 4);
|
|
66
|
+
ctx.bezierCurveTo(d / 2, 0, 0, 0, 0, d / 4);
|
|
67
|
+
ctx.fillStyle = color;
|
|
68
|
+
ctx.fill();
|
|
69
|
+
ctx.restore();
|
|
70
|
+
}
|
|
71
|
+
function renderHeartsOnce(count, duration, speed = 1, loop = false, effectId) {
|
|
72
|
+
if (typeof window === 'undefined' || typeof document === 'undefined') {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
const canvas = createCanvas();
|
|
76
|
+
const ctx = canvas.getContext('2d');
|
|
77
|
+
if (!ctx) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
// Se já existe um loop com esse ID, para ele antes de começar um novo
|
|
81
|
+
if (effectId && activeLoops.has(effectId)) {
|
|
82
|
+
activeLoops.get(effectId)?.stop();
|
|
83
|
+
}
|
|
84
|
+
const particles = createParticles(count, canvas.width, canvas.height, speed);
|
|
85
|
+
const start = performance.now();
|
|
86
|
+
let animationId;
|
|
87
|
+
let isRunning = true;
|
|
88
|
+
const stop = () => {
|
|
89
|
+
isRunning = false;
|
|
90
|
+
cancelAnimationFrame(animationId);
|
|
91
|
+
if (effectId)
|
|
92
|
+
activeLoops.delete(effectId);
|
|
93
|
+
};
|
|
94
|
+
if (effectId && loop) {
|
|
95
|
+
activeLoops.set(effectId, { stop });
|
|
96
|
+
}
|
|
97
|
+
function frame(now) {
|
|
98
|
+
if (!isRunning)
|
|
99
|
+
return;
|
|
100
|
+
const elapsed = now - start;
|
|
101
|
+
if (!ctx) {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
105
|
+
const isRecycling = loop || elapsed < duration;
|
|
106
|
+
let anyVisible = false;
|
|
107
|
+
particles.forEach(p => {
|
|
108
|
+
p.x += p.vx;
|
|
109
|
+
p.y += p.vy;
|
|
110
|
+
p.vy += 0.02 * speed; // Gravidade proporcional à velocidade
|
|
111
|
+
p.rotation += p.vr;
|
|
112
|
+
if (p.y - p.size > canvas.height) {
|
|
113
|
+
if (isRecycling) {
|
|
114
|
+
p.y = -p.size;
|
|
115
|
+
p.x = Math.random() * canvas.width;
|
|
116
|
+
p.vy = (Math.random() * 2 + 1.5) * speed;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
anyVisible = true;
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
if (anyVisible || isRecycling) {
|
|
124
|
+
particles.forEach(p => {
|
|
125
|
+
if (!ctx) {
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
ctx.globalAlpha = p.opacity;
|
|
129
|
+
drawHeart(ctx, p.x, p.y, p.size, p.color, p.rotation);
|
|
130
|
+
});
|
|
131
|
+
ctx.globalAlpha = 1.0;
|
|
132
|
+
animationId = requestAnimationFrame(frame);
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
if (!ctx) {
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
139
|
+
if (effectId)
|
|
140
|
+
activeLoops.delete(effectId);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
animationId = requestAnimationFrame(frame);
|
|
144
|
+
}
|
|
145
|
+
const heartsPlugin = {
|
|
146
|
+
name: 'hearts-renderer',
|
|
147
|
+
renderEffect(effect) {
|
|
148
|
+
if (effect.name === 'hearts' && effect.type === 'global') {
|
|
149
|
+
const params = effect.params || {};
|
|
150
|
+
if (params['stop'] && params['id']) {
|
|
151
|
+
activeLoops.get(params['id'])?.stop();
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
const count = params['intensity'] || 40;
|
|
155
|
+
const duration = params['duration'] || 2000;
|
|
156
|
+
const speed = params['speed'] || 1;
|
|
157
|
+
const loop = !!params['loop'];
|
|
158
|
+
const id = params['id'];
|
|
159
|
+
renderHeartsOnce(count, duration, speed, loop, id);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
export function registerHeartsPlugin() {
|
|
164
|
+
if (registered) {
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
if (typeof window === 'undefined' || typeof document === 'undefined') {
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
animationEngine.registerGlobalEffectPlugin(heartsPlugin);
|
|
171
|
+
registered = true;
|
|
172
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function registerAllEffects(): void;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { registerConfettiPlugin } from './confetti.plugin';
|
|
2
|
+
import { registerHeartsPlugin } from './hearts.plugin';
|
|
3
|
+
import { registerUIEffectsPlugin } from './ui-effects.plugin';
|
|
4
|
+
import { registerExtraEffectsPlugin } from './extra-effects.plugin';
|
|
5
|
+
export function registerAllEffects() {
|
|
6
|
+
try {
|
|
7
|
+
if (typeof window === 'undefined' || typeof document === 'undefined') {
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
registerConfettiPlugin();
|
|
11
|
+
registerHeartsPlugin();
|
|
12
|
+
registerExtraEffectsPlugin();
|
|
13
|
+
registerUIEffectsPlugin();
|
|
14
|
+
}
|
|
15
|
+
catch { }
|
|
16
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function registerUIEffectsPlugin(): void;
|