@cognima/banners 0.0.1-beta
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/assets/fonts/Manrope/Manrope-Bold.ttf +0 -0
- package/assets/fonts/Manrope/Manrope-Regular.ttf +0 -0
- package/assets/fonts/Others/AbyssinicaSIL-Regular.ttf +0 -0
- package/assets/fonts/Others/ChirpRegular.ttf +0 -0
- package/assets/fonts/Poppins/Poppins-Bold.ttf +0 -0
- package/assets/fonts/Poppins/Poppins-Medium.ttf +0 -0
- package/assets/fonts/Poppins/Poppins-Regular.ttf +0 -0
- package/assets/placeholders/album_art.png +0 -0
- package/assets/placeholders/avatar.png +0 -0
- package/assets/placeholders/badge.jpg +0 -0
- package/assets/placeholders/badge.png +0 -0
- package/assets/placeholders/badge_2.jpg +0 -0
- package/assets/placeholders/badge_3.jpg +0 -0
- package/assets/placeholders/badge_4.jpg +0 -0
- package/assets/placeholders/badge_5.jpg +0 -0
- package/assets/placeholders/banner.jpeg +0 -0
- package/assets/placeholders/images.jpeg +0 -0
- package/index.js +153 -0
- package/package.json +34 -0
- package/src/animation-effects.js +631 -0
- package/src/cache-manager.js +258 -0
- package/src/community-banner.js +1536 -0
- package/src/constants.js +208 -0
- package/src/discord-profile.js +584 -0
- package/src/e-commerce-banner.js +1214 -0
- package/src/effects.js +355 -0
- package/src/error-handler.js +305 -0
- package/src/event-banner.js +1319 -0
- package/src/facebook-post.js +679 -0
- package/src/gradient-welcome.js +430 -0
- package/src/image-filters.js +1034 -0
- package/src/image-processor.js +1014 -0
- package/src/instagram-post.js +504 -0
- package/src/interactive-elements.js +1208 -0
- package/src/linkedin-post.js +658 -0
- package/src/marketing-banner.js +1089 -0
- package/src/minimalist-banner.js +892 -0
- package/src/modern-profile.js +755 -0
- package/src/performance-optimizer.js +216 -0
- package/src/telegram-header.js +544 -0
- package/src/test-runner.js +645 -0
- package/src/tiktok-post.js +713 -0
- package/src/twitter-header.js +604 -0
- package/src/validator.js +442 -0
- package/src/welcome-leave.js +445 -0
- package/src/whatsapp-status.js +386 -0
- package/src/youtube-thumbnail.js +681 -0
- package/utils.js +710 -0
|
@@ -0,0 +1,1208 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Módulo de elementos interativos para banners
|
|
5
|
+
*
|
|
6
|
+
* Este módulo fornece funções para criar elementos que simulam
|
|
7
|
+
* interatividade nos banners estáticos.
|
|
8
|
+
*
|
|
9
|
+
* @module interactive-elements
|
|
10
|
+
* @author Cognima Team (melhorado)
|
|
11
|
+
* @version 2.0.0
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const utils = require("../utils");
|
|
15
|
+
const animationEffects = require("./animation-effects");
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Cria um botão com efeito de hover
|
|
19
|
+
*
|
|
20
|
+
* @param {CanvasRenderingContext2D} ctx - Contexto de renderização
|
|
21
|
+
* @param {number} x - Posição X do botão
|
|
22
|
+
* @param {number} y - Posição Y do botão
|
|
23
|
+
* @param {number} width - Largura do botão
|
|
24
|
+
* @param {number} height - Altura do botão
|
|
25
|
+
* @param {string} text - Texto do botão
|
|
26
|
+
* @param {Object} [options={}] - Opções de configuração
|
|
27
|
+
* @param {string} [options.backgroundColor="#007BFF"] - Cor de fundo do botão
|
|
28
|
+
* @param {string} [options.textColor="#FFFFFF"] - Cor do texto
|
|
29
|
+
* @param {string} [options.font="bold 24px Poppins"] - Fonte do texto
|
|
30
|
+
* @param {number} [options.borderRadius=8] - Raio da borda
|
|
31
|
+
* @param {boolean} [options.isHovered=false] - Se o botão está em estado de hover
|
|
32
|
+
* @param {string} [options.hoverEffect="glow"] - Efeito de hover ("glow", "scale", "shadow", "color")
|
|
33
|
+
* @param {string} [options.hoverColor="#0056b3"] - Cor do botão em estado de hover
|
|
34
|
+
*/
|
|
35
|
+
function drawInteractiveButton(ctx, x, y, width, height, text, options = {}) {
|
|
36
|
+
// Valores padrão
|
|
37
|
+
const defaults = {
|
|
38
|
+
backgroundColor: "#007BFF",
|
|
39
|
+
textColor: "#FFFFFF",
|
|
40
|
+
font: "bold 24px Poppins",
|
|
41
|
+
borderRadius: 8,
|
|
42
|
+
isHovered: false,
|
|
43
|
+
hoverEffect: "glow",
|
|
44
|
+
hoverColor: "#0056b3"
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
// Mescla as opções com os valores padrão
|
|
48
|
+
const settings = { ...defaults, ...options };
|
|
49
|
+
|
|
50
|
+
// Salva o estado atual do contexto
|
|
51
|
+
ctx.save();
|
|
52
|
+
|
|
53
|
+
// Aplica o efeito de hover se necessário
|
|
54
|
+
if (settings.isHovered) {
|
|
55
|
+
switch (settings.hoverEffect) {
|
|
56
|
+
case "glow":
|
|
57
|
+
animationEffects.addGlowEffect(
|
|
58
|
+
ctx,
|
|
59
|
+
x - 5,
|
|
60
|
+
y - 5,
|
|
61
|
+
width + 10,
|
|
62
|
+
height + 10,
|
|
63
|
+
settings.backgroundColor,
|
|
64
|
+
0.7,
|
|
65
|
+
15
|
|
66
|
+
);
|
|
67
|
+
break;
|
|
68
|
+
case "scale":
|
|
69
|
+
// Aumenta ligeiramente o tamanho do botão
|
|
70
|
+
x -= width * 0.05;
|
|
71
|
+
y -= height * 0.05;
|
|
72
|
+
width *= 1.1;
|
|
73
|
+
height *= 1.1;
|
|
74
|
+
break;
|
|
75
|
+
case "shadow":
|
|
76
|
+
animationEffects.add3DShadowEffect(
|
|
77
|
+
ctx,
|
|
78
|
+
x,
|
|
79
|
+
y,
|
|
80
|
+
width,
|
|
81
|
+
height,
|
|
82
|
+
8,
|
|
83
|
+
"#000000",
|
|
84
|
+
0.3
|
|
85
|
+
);
|
|
86
|
+
break;
|
|
87
|
+
case "color":
|
|
88
|
+
// Muda a cor de fundo
|
|
89
|
+
settings.backgroundColor = settings.hoverColor;
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Desenha o botão
|
|
95
|
+
utils.roundRect(ctx, x, y, width, height, settings.borderRadius, true);
|
|
96
|
+
|
|
97
|
+
// Configura o texto
|
|
98
|
+
ctx.font = settings.font;
|
|
99
|
+
ctx.fillStyle = settings.textColor;
|
|
100
|
+
ctx.textAlign = "center";
|
|
101
|
+
ctx.textBaseline = "middle";
|
|
102
|
+
|
|
103
|
+
// Desenha o texto
|
|
104
|
+
ctx.fillText(text, x + width / 2, y + height / 2);
|
|
105
|
+
|
|
106
|
+
// Restaura o estado do contexto
|
|
107
|
+
ctx.restore();
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Cria um campo de entrada de texto
|
|
112
|
+
*
|
|
113
|
+
* @param {CanvasRenderingContext2D} ctx - Contexto de renderização
|
|
114
|
+
* @param {number} x - Posição X do campo
|
|
115
|
+
* @param {number} y - Posição Y do campo
|
|
116
|
+
* @param {number} width - Largura do campo
|
|
117
|
+
* @param {number} height - Altura do campo
|
|
118
|
+
* @param {string} [placeholder="Digite aqui..."] - Texto de placeholder
|
|
119
|
+
* @param {string} [value=""] - Valor atual do campo
|
|
120
|
+
* @param {Object} [options={}] - Opções de configuração
|
|
121
|
+
* @param {string} [options.backgroundColor="#FFFFFF"] - Cor de fundo do campo
|
|
122
|
+
* @param {string} [options.textColor="#333333"] - Cor do texto
|
|
123
|
+
* @param {string} [options.placeholderColor="#999999"] - Cor do placeholder
|
|
124
|
+
* @param {string} [options.borderColor="#CCCCCC"] - Cor da borda
|
|
125
|
+
* @param {number} [options.borderWidth=1] - Largura da borda
|
|
126
|
+
* @param {number} [options.borderRadius=4] - Raio da borda
|
|
127
|
+
* @param {string} [options.font="16px Poppins"] - Fonte do texto
|
|
128
|
+
* @param {boolean} [options.isFocused=false] - Se o campo está em foco
|
|
129
|
+
* @param {boolean} [options.showCursor=false] - Se deve mostrar o cursor
|
|
130
|
+
*/
|
|
131
|
+
function drawInputField(ctx, x, y, width, height, placeholder = "Digite aqui...", value = "", options = {}) {
|
|
132
|
+
// Valores padrão
|
|
133
|
+
const defaults = {
|
|
134
|
+
backgroundColor: "#FFFFFF",
|
|
135
|
+
textColor: "#333333",
|
|
136
|
+
placeholderColor: "#999999",
|
|
137
|
+
borderColor: "#CCCCCC",
|
|
138
|
+
borderWidth: 1,
|
|
139
|
+
borderRadius: 4,
|
|
140
|
+
font: "16px Poppins",
|
|
141
|
+
isFocused: false,
|
|
142
|
+
showCursor: false
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
// Mescla as opções com os valores padrão
|
|
146
|
+
const settings = { ...defaults, ...options };
|
|
147
|
+
|
|
148
|
+
// Salva o estado atual do contexto
|
|
149
|
+
ctx.save();
|
|
150
|
+
|
|
151
|
+
// Desenha o fundo do campo
|
|
152
|
+
ctx.fillStyle = settings.backgroundColor;
|
|
153
|
+
utils.roundRect(ctx, x, y, width, height, settings.borderRadius, true);
|
|
154
|
+
|
|
155
|
+
// Desenha a borda
|
|
156
|
+
ctx.strokeStyle = settings.isFocused ? "#007BFF" : settings.borderColor;
|
|
157
|
+
ctx.lineWidth = settings.borderWidth;
|
|
158
|
+
utils.roundRect(ctx, x, y, width, height, settings.borderRadius, false, true);
|
|
159
|
+
|
|
160
|
+
// Configura o texto
|
|
161
|
+
ctx.font = settings.font;
|
|
162
|
+
ctx.textAlign = "left";
|
|
163
|
+
ctx.textBaseline = "middle";
|
|
164
|
+
|
|
165
|
+
// Padding interno
|
|
166
|
+
const paddingX = 10;
|
|
167
|
+
|
|
168
|
+
// Desenha o texto ou placeholder
|
|
169
|
+
if (value) {
|
|
170
|
+
ctx.fillStyle = settings.textColor;
|
|
171
|
+
|
|
172
|
+
// Limita o texto à largura do campo
|
|
173
|
+
const maxTextWidth = width - paddingX * 2;
|
|
174
|
+
const text = utils.truncateText(ctx, value, maxTextWidth);
|
|
175
|
+
|
|
176
|
+
ctx.fillText(text, x + paddingX, y + height / 2);
|
|
177
|
+
|
|
178
|
+
// Desenha o cursor se necessário
|
|
179
|
+
if (settings.isFocused && settings.showCursor) {
|
|
180
|
+
const textWidth = ctx.measureText(text).width;
|
|
181
|
+
ctx.fillRect(x + paddingX + textWidth + 2, y + height * 0.25, 2, height * 0.5);
|
|
182
|
+
}
|
|
183
|
+
} else {
|
|
184
|
+
ctx.fillStyle = settings.placeholderColor;
|
|
185
|
+
ctx.fillText(placeholder, x + paddingX, y + height / 2);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Restaura o estado do contexto
|
|
189
|
+
ctx.restore();
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Cria um checkbox
|
|
194
|
+
*
|
|
195
|
+
* @param {CanvasRenderingContext2D} ctx - Contexto de renderização
|
|
196
|
+
* @param {number} x - Posição X do checkbox
|
|
197
|
+
* @param {number} y - Posição Y do checkbox
|
|
198
|
+
* @param {number} size - Tamanho do checkbox
|
|
199
|
+
* @param {string} label - Texto do label
|
|
200
|
+
* @param {boolean} [checked=false] - Se o checkbox está marcado
|
|
201
|
+
* @param {Object} [options={}] - Opções de configuração
|
|
202
|
+
* @param {string} [options.backgroundColor="#FFFFFF"] - Cor de fundo do checkbox
|
|
203
|
+
* @param {string} [options.checkedColor="#007BFF"] - Cor quando marcado
|
|
204
|
+
* @param {string} [options.borderColor="#CCCCCC"] - Cor da borda
|
|
205
|
+
* @param {string} [options.textColor="#333333"] - Cor do texto
|
|
206
|
+
* @param {string} [options.font="16px Poppins"] - Fonte do texto
|
|
207
|
+
* @param {boolean} [options.isHovered=false] - Se o checkbox está em estado de hover
|
|
208
|
+
*/
|
|
209
|
+
function drawCheckbox(ctx, x, y, size, label, checked = false, options = {}) {
|
|
210
|
+
// Valores padrão
|
|
211
|
+
const defaults = {
|
|
212
|
+
backgroundColor: "#FFFFFF",
|
|
213
|
+
checkedColor: "#007BFF",
|
|
214
|
+
borderColor: "#CCCCCC",
|
|
215
|
+
textColor: "#333333",
|
|
216
|
+
font: "16px Poppins",
|
|
217
|
+
isHovered: false
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
// Mescla as opções com os valores padrão
|
|
221
|
+
const settings = { ...defaults, ...options };
|
|
222
|
+
|
|
223
|
+
// Salva o estado atual do contexto
|
|
224
|
+
ctx.save();
|
|
225
|
+
|
|
226
|
+
// Desenha o fundo do checkbox
|
|
227
|
+
ctx.fillStyle = settings.backgroundColor;
|
|
228
|
+
ctx.strokeStyle = settings.isHovered ? settings.checkedColor : settings.borderColor;
|
|
229
|
+
ctx.lineWidth = 2;
|
|
230
|
+
|
|
231
|
+
// Desenha o quadrado do checkbox
|
|
232
|
+
ctx.beginPath();
|
|
233
|
+
ctx.rect(x, y, size, size);
|
|
234
|
+
ctx.fill();
|
|
235
|
+
ctx.stroke();
|
|
236
|
+
|
|
237
|
+
// Desenha o check se estiver marcado
|
|
238
|
+
if (checked) {
|
|
239
|
+
ctx.fillStyle = settings.checkedColor;
|
|
240
|
+
|
|
241
|
+
// Desenha um check estilizado
|
|
242
|
+
ctx.beginPath();
|
|
243
|
+
ctx.moveTo(x + size * 0.2, y + size * 0.5);
|
|
244
|
+
ctx.lineTo(x + size * 0.4, y + size * 0.7);
|
|
245
|
+
ctx.lineTo(x + size * 0.8, y + size * 0.3);
|
|
246
|
+
ctx.lineWidth = 3;
|
|
247
|
+
ctx.strokeStyle = settings.checkedColor;
|
|
248
|
+
ctx.stroke();
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Desenha o label
|
|
252
|
+
ctx.font = settings.font;
|
|
253
|
+
ctx.fillStyle = settings.textColor;
|
|
254
|
+
ctx.textAlign = "left";
|
|
255
|
+
ctx.textBaseline = "middle";
|
|
256
|
+
ctx.fillText(label, x + size + 10, y + size / 2);
|
|
257
|
+
|
|
258
|
+
// Restaura o estado do contexto
|
|
259
|
+
ctx.restore();
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Cria um radio button
|
|
264
|
+
*
|
|
265
|
+
* @param {CanvasRenderingContext2D} ctx - Contexto de renderização
|
|
266
|
+
* @param {number} x - Posição X do radio button
|
|
267
|
+
* @param {number} y - Posição Y do radio button
|
|
268
|
+
* @param {number} size - Tamanho do radio button
|
|
269
|
+
* @param {string} label - Texto do label
|
|
270
|
+
* @param {boolean} [selected=false] - Se o radio button está selecionado
|
|
271
|
+
* @param {Object} [options={}] - Opções de configuração
|
|
272
|
+
* @param {string} [options.backgroundColor="#FFFFFF"] - Cor de fundo do radio button
|
|
273
|
+
* @param {string} [options.selectedColor="#007BFF"] - Cor quando selecionado
|
|
274
|
+
* @param {string} [options.borderColor="#CCCCCC"] - Cor da borda
|
|
275
|
+
* @param {string} [options.textColor="#333333"] - Cor do texto
|
|
276
|
+
* @param {string} [options.font="16px Poppins"] - Fonte do texto
|
|
277
|
+
* @param {boolean} [options.isHovered=false] - Se o radio button está em estado de hover
|
|
278
|
+
*/
|
|
279
|
+
function drawRadioButton(ctx, x, y, size, label, selected = false, options = {}) {
|
|
280
|
+
// Valores padrão
|
|
281
|
+
const defaults = {
|
|
282
|
+
backgroundColor: "#FFFFFF",
|
|
283
|
+
selectedColor: "#007BFF",
|
|
284
|
+
borderColor: "#CCCCCC",
|
|
285
|
+
textColor: "#333333",
|
|
286
|
+
font: "16px Poppins",
|
|
287
|
+
isHovered: false
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
// Mescla as opções com os valores padrão
|
|
291
|
+
const settings = { ...defaults, ...options };
|
|
292
|
+
|
|
293
|
+
// Salva o estado atual do contexto
|
|
294
|
+
ctx.save();
|
|
295
|
+
|
|
296
|
+
// Desenha o círculo externo
|
|
297
|
+
ctx.beginPath();
|
|
298
|
+
ctx.arc(x + size / 2, y + size / 2, size / 2, 0, Math.PI * 2);
|
|
299
|
+
ctx.fillStyle = settings.backgroundColor;
|
|
300
|
+
ctx.fill();
|
|
301
|
+
ctx.strokeStyle = settings.isHovered ? settings.selectedColor : settings.borderColor;
|
|
302
|
+
ctx.lineWidth = 2;
|
|
303
|
+
ctx.stroke();
|
|
304
|
+
|
|
305
|
+
// Desenha o círculo interno se estiver selecionado
|
|
306
|
+
if (selected) {
|
|
307
|
+
ctx.beginPath();
|
|
308
|
+
ctx.arc(x + size / 2, y + size / 2, size / 4, 0, Math.PI * 2);
|
|
309
|
+
ctx.fillStyle = settings.selectedColor;
|
|
310
|
+
ctx.fill();
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// Desenha o label
|
|
314
|
+
ctx.font = settings.font;
|
|
315
|
+
ctx.fillStyle = settings.textColor;
|
|
316
|
+
ctx.textAlign = "left";
|
|
317
|
+
ctx.textBaseline = "middle";
|
|
318
|
+
ctx.fillText(label, x + size + 10, y + size / 2);
|
|
319
|
+
|
|
320
|
+
// Restaura o estado do contexto
|
|
321
|
+
ctx.restore();
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Cria um slider
|
|
326
|
+
*
|
|
327
|
+
* @param {CanvasRenderingContext2D} ctx - Contexto de renderização
|
|
328
|
+
* @param {number} x - Posição X do slider
|
|
329
|
+
* @param {number} y - Posição Y do slider
|
|
330
|
+
* @param {number} width - Largura do slider
|
|
331
|
+
* @param {number} [value=0.5] - Valor atual do slider (0-1)
|
|
332
|
+
* @param {Object} [options={}] - Opções de configuração
|
|
333
|
+
* @param {string} [options.trackColor="#CCCCCC"] - Cor da trilha
|
|
334
|
+
* @param {string} [options.progressColor="#007BFF"] - Cor do progresso
|
|
335
|
+
* @param {string} [options.handleColor="#FFFFFF"] - Cor do manipulador
|
|
336
|
+
* @param {number} [options.handleSize=20] - Tamanho do manipulador
|
|
337
|
+
* @param {number} [options.trackHeight=6] - Altura da trilha
|
|
338
|
+
* @param {boolean} [options.showValue=false] - Se deve mostrar o valor
|
|
339
|
+
* @param {number} [options.min=0] - Valor mínimo
|
|
340
|
+
* @param {number} [options.max=100] - Valor máximo
|
|
341
|
+
* @param {string} [options.valueFormat="{value}"] - Formato do valor
|
|
342
|
+
* @param {boolean} [options.isActive=false] - Se o slider está ativo
|
|
343
|
+
*/
|
|
344
|
+
function drawSlider(ctx, x, y, width, value = 0.5, options = {}) {
|
|
345
|
+
// Valores padrão
|
|
346
|
+
const defaults = {
|
|
347
|
+
trackColor: "#CCCCCC",
|
|
348
|
+
progressColor: "#007BFF",
|
|
349
|
+
handleColor: "#FFFFFF",
|
|
350
|
+
handleSize: 20,
|
|
351
|
+
trackHeight: 6,
|
|
352
|
+
showValue: false,
|
|
353
|
+
min: 0,
|
|
354
|
+
max: 100,
|
|
355
|
+
valueFormat: "{value}",
|
|
356
|
+
isActive: false
|
|
357
|
+
};
|
|
358
|
+
|
|
359
|
+
// Mescla as opções com os valores padrão
|
|
360
|
+
const settings = { ...defaults, ...options };
|
|
361
|
+
|
|
362
|
+
// Limita o valor entre 0 e 1
|
|
363
|
+
value = Math.max(0, Math.min(1, value));
|
|
364
|
+
|
|
365
|
+
// Salva o estado atual do contexto
|
|
366
|
+
ctx.save();
|
|
367
|
+
|
|
368
|
+
// Desenha a trilha
|
|
369
|
+
ctx.fillStyle = settings.trackColor;
|
|
370
|
+
utils.roundRect(ctx, x, y + settings.handleSize / 2 - settings.trackHeight / 2, width, settings.trackHeight, settings.trackHeight / 2, true);
|
|
371
|
+
|
|
372
|
+
// Desenha o progresso
|
|
373
|
+
const progressWidth = width * value;
|
|
374
|
+
ctx.fillStyle = settings.progressColor;
|
|
375
|
+
utils.roundRect(ctx, x, y + settings.handleSize / 2 - settings.trackHeight / 2, progressWidth, settings.trackHeight, settings.trackHeight / 2, true);
|
|
376
|
+
|
|
377
|
+
// Desenha o manipulador
|
|
378
|
+
const handleX = x + progressWidth - settings.handleSize / 2;
|
|
379
|
+
const handleY = y;
|
|
380
|
+
|
|
381
|
+
// Adiciona sombra se estiver ativo
|
|
382
|
+
if (settings.isActive) {
|
|
383
|
+
ctx.shadowColor = "rgba(0,0,0,0.3)";
|
|
384
|
+
ctx.shadowBlur = 10;
|
|
385
|
+
ctx.shadowOffsetX = 2;
|
|
386
|
+
ctx.shadowOffsetY = 2;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
ctx.fillStyle = settings.handleColor;
|
|
390
|
+
ctx.strokeStyle = settings.progressColor;
|
|
391
|
+
ctx.lineWidth = 2;
|
|
392
|
+
ctx.beginPath();
|
|
393
|
+
ctx.arc(handleX, handleY + settings.handleSize / 2, settings.handleSize / 2, 0, Math.PI * 2);
|
|
394
|
+
ctx.fill();
|
|
395
|
+
ctx.stroke();
|
|
396
|
+
|
|
397
|
+
// Remove a sombra
|
|
398
|
+
ctx.shadowColor = "transparent";
|
|
399
|
+
ctx.shadowBlur = 0;
|
|
400
|
+
ctx.shadowOffsetX = 0;
|
|
401
|
+
ctx.shadowOffsetY = 0;
|
|
402
|
+
|
|
403
|
+
// Mostra o valor se necessário
|
|
404
|
+
if (settings.showValue) {
|
|
405
|
+
const actualValue = Math.round(settings.min + (settings.max - settings.min) * value);
|
|
406
|
+
const valueText = settings.valueFormat.replace("{value}", actualValue);
|
|
407
|
+
|
|
408
|
+
ctx.font = "14px Poppins";
|
|
409
|
+
ctx.fillStyle = "#333333";
|
|
410
|
+
ctx.textAlign = "center";
|
|
411
|
+
ctx.fillText(valueText, x + width / 2, y + settings.handleSize + 20);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
// Restaura o estado do contexto
|
|
415
|
+
ctx.restore();
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
/**
|
|
419
|
+
* Cria um toggle switch
|
|
420
|
+
*
|
|
421
|
+
* @param {CanvasRenderingContext2D} ctx - Contexto de renderização
|
|
422
|
+
* @param {number} x - Posição X do toggle
|
|
423
|
+
* @param {number} y - Posição Y do toggle
|
|
424
|
+
* @param {number} width - Largura do toggle
|
|
425
|
+
* @param {number} height - Altura do toggle
|
|
426
|
+
* @param {boolean} [checked=false] - Se o toggle está ligado
|
|
427
|
+
* @param {Object} [options={}] - Opções de configuração
|
|
428
|
+
* @param {string} [options.onColor="#007BFF"] - Cor quando ligado
|
|
429
|
+
* @param {string} [options.offColor="#CCCCCC"] - Cor quando desligado
|
|
430
|
+
* @param {string} [options.handleColor="#FFFFFF"] - Cor do manipulador
|
|
431
|
+
* @param {string} [options.label=""] - Texto do label
|
|
432
|
+
* @param {string} [options.labelColor="#333333"] - Cor do label
|
|
433
|
+
* @param {string} [options.font="16px Poppins"] - Fonte do label
|
|
434
|
+
* @param {boolean} [options.isHovered=false] - Se o toggle está em estado de hover
|
|
435
|
+
*/
|
|
436
|
+
function drawToggleSwitch(ctx, x, y, width, height, checked = false, options = {}) {
|
|
437
|
+
// Valores padrão
|
|
438
|
+
const defaults = {
|
|
439
|
+
onColor: "#007BFF",
|
|
440
|
+
offColor: "#CCCCCC",
|
|
441
|
+
handleColor: "#FFFFFF",
|
|
442
|
+
label: "",
|
|
443
|
+
labelColor: "#333333",
|
|
444
|
+
font: "16px Poppins",
|
|
445
|
+
isHovered: false
|
|
446
|
+
};
|
|
447
|
+
|
|
448
|
+
// Mescla as opções com os valores padrão
|
|
449
|
+
const settings = { ...defaults, ...options };
|
|
450
|
+
|
|
451
|
+
// Salva o estado atual do contexto
|
|
452
|
+
ctx.save();
|
|
453
|
+
|
|
454
|
+
// Desenha o fundo do toggle
|
|
455
|
+
ctx.fillStyle = checked ? settings.onColor : settings.offColor;
|
|
456
|
+
utils.roundRect(ctx, x, y, width, height, height / 2, true);
|
|
457
|
+
|
|
458
|
+
// Calcula a posição do manipulador
|
|
459
|
+
const handleSize = height - 4;
|
|
460
|
+
const handleX = checked ? x + width - handleSize - 2 : x + 2;
|
|
461
|
+
const handleY = y + 2;
|
|
462
|
+
|
|
463
|
+
// Adiciona sombra se estiver em hover
|
|
464
|
+
if (settings.isHovered) {
|
|
465
|
+
ctx.shadowColor = "rgba(0,0,0,0.2)";
|
|
466
|
+
ctx.shadowBlur = 5;
|
|
467
|
+
ctx.shadowOffsetX = 1;
|
|
468
|
+
ctx.shadowOffsetY = 1;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
// Desenha o manipulador
|
|
472
|
+
ctx.fillStyle = settings.handleColor;
|
|
473
|
+
ctx.beginPath();
|
|
474
|
+
ctx.arc(handleX + handleSize / 2, handleY + handleSize / 2, handleSize / 2, 0, Math.PI * 2);
|
|
475
|
+
ctx.fill();
|
|
476
|
+
|
|
477
|
+
// Remove a sombra
|
|
478
|
+
ctx.shadowColor = "transparent";
|
|
479
|
+
ctx.shadowBlur = 0;
|
|
480
|
+
ctx.shadowOffsetX = 0;
|
|
481
|
+
ctx.shadowOffsetY = 0;
|
|
482
|
+
|
|
483
|
+
// Desenha o label se necessário
|
|
484
|
+
if (settings.label) {
|
|
485
|
+
ctx.font = settings.font;
|
|
486
|
+
ctx.fillStyle = settings.labelColor;
|
|
487
|
+
ctx.textAlign = "left";
|
|
488
|
+
ctx.textBaseline = "middle";
|
|
489
|
+
ctx.fillText(settings.label, x + width + 10, y + height / 2);
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
// Restaura o estado do contexto
|
|
493
|
+
ctx.restore();
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
/**
|
|
497
|
+
* Cria um dropdown
|
|
498
|
+
*
|
|
499
|
+
* @param {CanvasRenderingContext2D} ctx - Contexto de renderização
|
|
500
|
+
* @param {number} x - Posição X do dropdown
|
|
501
|
+
* @param {number} y - Posição Y do dropdown
|
|
502
|
+
* @param {number} width - Largura do dropdown
|
|
503
|
+
* @param {number} height - Altura do dropdown
|
|
504
|
+
* @param {string} [selectedOption="Selecione..."] - Opção selecionada
|
|
505
|
+
* @param {Array<string>} [options=[]] - Lista de opções
|
|
506
|
+
* @param {Object} [settings={}] - Opções de configuração
|
|
507
|
+
* @param {string} [settings.backgroundColor="#FFFFFF"] - Cor de fundo do dropdown
|
|
508
|
+
* @param {string} [settings.textColor="#333333"] - Cor do texto
|
|
509
|
+
* @param {string} [settings.borderColor="#CCCCCC"] - Cor da borda
|
|
510
|
+
* @param {number} [settings.borderWidth=1] - Largura da borda
|
|
511
|
+
* @param {number} [settings.borderRadius=4] - Raio da borda
|
|
512
|
+
* @param {string} [settings.font="16px Poppins"] - Fonte do texto
|
|
513
|
+
* @param {boolean} [settings.isOpen=false] - Se o dropdown está aberto
|
|
514
|
+
* @param {boolean} [settings.isHovered=false] - Se o dropdown está em estado de hover
|
|
515
|
+
* @param {number} [settings.maxHeight=200] - Altura máxima do dropdown aberto
|
|
516
|
+
*/
|
|
517
|
+
function drawDropdown(ctx, x, y, width, height, selectedOption = "Selecione...", options = [], settings = {}) {
|
|
518
|
+
// Valores padrão
|
|
519
|
+
const defaults = {
|
|
520
|
+
backgroundColor: "#FFFFFF",
|
|
521
|
+
textColor: "#333333",
|
|
522
|
+
borderColor: "#CCCCCC",
|
|
523
|
+
borderWidth: 1,
|
|
524
|
+
borderRadius: 4,
|
|
525
|
+
font: "16px Poppins",
|
|
526
|
+
isOpen: false,
|
|
527
|
+
isHovered: false,
|
|
528
|
+
maxHeight: 200
|
|
529
|
+
};
|
|
530
|
+
|
|
531
|
+
// Mescla as opções com os valores padrão
|
|
532
|
+
const config = { ...defaults, ...settings };
|
|
533
|
+
|
|
534
|
+
// Salva o estado atual do contexto
|
|
535
|
+
ctx.save();
|
|
536
|
+
|
|
537
|
+
// Desenha o dropdown fechado
|
|
538
|
+
ctx.fillStyle = config.backgroundColor;
|
|
539
|
+
utils.roundRect(ctx, x, y, width, height, config.borderRadius, true);
|
|
540
|
+
|
|
541
|
+
// Desenha a borda
|
|
542
|
+
ctx.strokeStyle = config.isHovered || config.isOpen ? "#007BFF" : config.borderColor;
|
|
543
|
+
ctx.lineWidth = config.borderWidth;
|
|
544
|
+
utils.roundRect(ctx, x, y, width, height, config.borderRadius, false, true);
|
|
545
|
+
|
|
546
|
+
// Configura o texto
|
|
547
|
+
ctx.font = config.font;
|
|
548
|
+
ctx.fillStyle = config.textColor;
|
|
549
|
+
ctx.textAlign = "left";
|
|
550
|
+
ctx.textBaseline = "middle";
|
|
551
|
+
|
|
552
|
+
// Padding interno
|
|
553
|
+
const paddingX = 10;
|
|
554
|
+
|
|
555
|
+
// Desenha o texto selecionado
|
|
556
|
+
const maxTextWidth = width - paddingX * 2 - 20; // 20px para o ícone de seta
|
|
557
|
+
const truncatedText = utils.truncateText(ctx, selectedOption, maxTextWidth);
|
|
558
|
+
ctx.fillText(truncatedText, x + paddingX, y + height / 2);
|
|
559
|
+
|
|
560
|
+
// Desenha o ícone de seta
|
|
561
|
+
const arrowX = x + width - 20;
|
|
562
|
+
const arrowY = y + height / 2;
|
|
563
|
+
ctx.beginPath();
|
|
564
|
+
if (config.isOpen) {
|
|
565
|
+
// Seta para cima
|
|
566
|
+
ctx.moveTo(arrowX - 5, arrowY + 3);
|
|
567
|
+
ctx.lineTo(arrowX, arrowY - 3);
|
|
568
|
+
ctx.lineTo(arrowX + 5, arrowY + 3);
|
|
569
|
+
} else {
|
|
570
|
+
// Seta para baixo
|
|
571
|
+
ctx.moveTo(arrowX - 5, arrowY - 3);
|
|
572
|
+
ctx.lineTo(arrowX, arrowY + 3);
|
|
573
|
+
ctx.lineTo(arrowX + 5, arrowY - 3);
|
|
574
|
+
}
|
|
575
|
+
ctx.fillStyle = config.textColor;
|
|
576
|
+
ctx.fill();
|
|
577
|
+
|
|
578
|
+
// Desenha o dropdown aberto se necessário
|
|
579
|
+
if (config.isOpen && options.length > 0) {
|
|
580
|
+
const optionHeight = height;
|
|
581
|
+
const dropdownHeight = Math.min(options.length * optionHeight, config.maxHeight);
|
|
582
|
+
|
|
583
|
+
// Desenha o fundo do dropdown
|
|
584
|
+
ctx.fillStyle = config.backgroundColor;
|
|
585
|
+
utils.roundRect(ctx, x, y + height, width, dropdownHeight, config.borderRadius, true);
|
|
586
|
+
|
|
587
|
+
// Desenha a borda
|
|
588
|
+
ctx.strokeStyle = config.borderColor;
|
|
589
|
+
ctx.lineWidth = config.borderWidth;
|
|
590
|
+
utils.roundRect(ctx, x, y + height, width, dropdownHeight, config.borderRadius, false, true);
|
|
591
|
+
|
|
592
|
+
// Desenha as opções
|
|
593
|
+
for (let i = 0; i < options.length; i++) {
|
|
594
|
+
const optionY = y + height + i * optionHeight;
|
|
595
|
+
|
|
596
|
+
// Verifica se a opção está visível (para scrolling)
|
|
597
|
+
if (optionY < y + height + dropdownHeight) {
|
|
598
|
+
// Destaca a opção selecionada
|
|
599
|
+
if (options[i] === selectedOption) {
|
|
600
|
+
ctx.fillStyle = "#F0F0F0";
|
|
601
|
+
ctx.fillRect(x + 1, optionY, width - 2, optionHeight);
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
// Desenha o texto da opção
|
|
605
|
+
ctx.fillStyle = config.textColor;
|
|
606
|
+
const truncatedOption = utils.truncateText(ctx, options[i], width - paddingX * 2);
|
|
607
|
+
ctx.fillText(truncatedOption, x + paddingX, optionY + optionHeight / 2);
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
// Restaura o estado do contexto
|
|
613
|
+
ctx.restore();
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
/**
|
|
617
|
+
* Cria um tooltip
|
|
618
|
+
*
|
|
619
|
+
* @param {CanvasRenderingContext2D} ctx - Contexto de renderização
|
|
620
|
+
* @param {number} x - Posição X do tooltip
|
|
621
|
+
* @param {number} y - Posição Y do tooltip
|
|
622
|
+
* @param {number} width - Largura do tooltip
|
|
623
|
+
* @param {number} height - Altura do tooltip
|
|
624
|
+
* @param {string} text - Texto do tooltip
|
|
625
|
+
* @param {Object} [options={}] - Opções de configuração
|
|
626
|
+
* @param {string} [options.backgroundColor="#333333"] - Cor de fundo do tooltip
|
|
627
|
+
* @param {string} [options.textColor="#FFFFFF"] - Cor do texto
|
|
628
|
+
* @param {number} [options.borderRadius=4] - Raio da borda
|
|
629
|
+
* @param {string} [options.position="top"] - Posição do tooltip ("top", "bottom", "left", "right")
|
|
630
|
+
* @param {string} [options.font="14px Poppins"] - Fonte do texto
|
|
631
|
+
* @param {number} [options.arrowSize=8] - Tamanho da seta
|
|
632
|
+
*/
|
|
633
|
+
function drawTooltip(ctx, x, y, width, height, text, options = {}) {
|
|
634
|
+
// Valores padrão
|
|
635
|
+
const defaults = {
|
|
636
|
+
backgroundColor: "#333333",
|
|
637
|
+
textColor: "#FFFFFF",
|
|
638
|
+
borderRadius: 4,
|
|
639
|
+
position: "top",
|
|
640
|
+
font: "14px Poppins",
|
|
641
|
+
arrowSize: 8
|
|
642
|
+
};
|
|
643
|
+
|
|
644
|
+
// Mescla as opções com os valores padrão
|
|
645
|
+
const settings = { ...defaults, ...options };
|
|
646
|
+
|
|
647
|
+
// Salva o estado atual do contexto
|
|
648
|
+
ctx.save();
|
|
649
|
+
|
|
650
|
+
// Calcula a posição do tooltip e da seta
|
|
651
|
+
let tooltipX = x;
|
|
652
|
+
let tooltipY = y;
|
|
653
|
+
let arrowX, arrowY;
|
|
654
|
+
|
|
655
|
+
switch (settings.position) {
|
|
656
|
+
case "bottom":
|
|
657
|
+
tooltipY = y + settings.arrowSize;
|
|
658
|
+
arrowX = tooltipX + width / 2;
|
|
659
|
+
arrowY = tooltipY - settings.arrowSize;
|
|
660
|
+
break;
|
|
661
|
+
case "left":
|
|
662
|
+
tooltipX = x - width - settings.arrowSize;
|
|
663
|
+
arrowX = tooltipX + width;
|
|
664
|
+
arrowY = tooltipY + height / 2;
|
|
665
|
+
break;
|
|
666
|
+
case "right":
|
|
667
|
+
tooltipX = x + settings.arrowSize;
|
|
668
|
+
arrowX = tooltipX - settings.arrowSize;
|
|
669
|
+
arrowY = tooltipY + height / 2;
|
|
670
|
+
break;
|
|
671
|
+
case "top":
|
|
672
|
+
default:
|
|
673
|
+
tooltipY = y - height - settings.arrowSize;
|
|
674
|
+
arrowX = tooltipX + width / 2;
|
|
675
|
+
arrowY = tooltipY + height;
|
|
676
|
+
break;
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
// Adiciona sombra
|
|
680
|
+
ctx.shadowColor = "rgba(0,0,0,0.3)";
|
|
681
|
+
ctx.shadowBlur = 5;
|
|
682
|
+
ctx.shadowOffsetX = 2;
|
|
683
|
+
ctx.shadowOffsetY = 2;
|
|
684
|
+
|
|
685
|
+
// Desenha o fundo do tooltip
|
|
686
|
+
ctx.fillStyle = settings.backgroundColor;
|
|
687
|
+
utils.roundRect(ctx, tooltipX, tooltipY, width, height, settings.borderRadius, true);
|
|
688
|
+
|
|
689
|
+
// Remove a sombra para a seta
|
|
690
|
+
ctx.shadowColor = "transparent";
|
|
691
|
+
ctx.shadowBlur = 0;
|
|
692
|
+
ctx.shadowOffsetX = 0;
|
|
693
|
+
ctx.shadowOffsetY = 0;
|
|
694
|
+
|
|
695
|
+
// Desenha a seta
|
|
696
|
+
ctx.fillStyle = settings.backgroundColor;
|
|
697
|
+
ctx.beginPath();
|
|
698
|
+
|
|
699
|
+
switch (settings.position) {
|
|
700
|
+
case "bottom":
|
|
701
|
+
ctx.moveTo(arrowX - settings.arrowSize, arrowY + settings.arrowSize);
|
|
702
|
+
ctx.lineTo(arrowX, arrowY);
|
|
703
|
+
ctx.lineTo(arrowX + settings.arrowSize, arrowY + settings.arrowSize);
|
|
704
|
+
break;
|
|
705
|
+
case "left":
|
|
706
|
+
ctx.moveTo(arrowX - settings.arrowSize, arrowY - settings.arrowSize);
|
|
707
|
+
ctx.lineTo(arrowX, arrowY);
|
|
708
|
+
ctx.lineTo(arrowX - settings.arrowSize, arrowY + settings.arrowSize);
|
|
709
|
+
break;
|
|
710
|
+
case "right":
|
|
711
|
+
ctx.moveTo(arrowX + settings.arrowSize, arrowY - settings.arrowSize);
|
|
712
|
+
ctx.lineTo(arrowX, arrowY);
|
|
713
|
+
ctx.lineTo(arrowX + settings.arrowSize, arrowY + settings.arrowSize);
|
|
714
|
+
break;
|
|
715
|
+
case "top":
|
|
716
|
+
default:
|
|
717
|
+
ctx.moveTo(arrowX - settings.arrowSize, arrowY - settings.arrowSize);
|
|
718
|
+
ctx.lineTo(arrowX, arrowY);
|
|
719
|
+
ctx.lineTo(arrowX + settings.arrowSize, arrowY - settings.arrowSize);
|
|
720
|
+
break;
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
ctx.closePath();
|
|
724
|
+
ctx.fill();
|
|
725
|
+
|
|
726
|
+
// Desenha o texto
|
|
727
|
+
ctx.font = settings.font;
|
|
728
|
+
ctx.fillStyle = settings.textColor;
|
|
729
|
+
ctx.textAlign = "center";
|
|
730
|
+
ctx.textBaseline = "middle";
|
|
731
|
+
|
|
732
|
+
// Quebra o texto se necessário
|
|
733
|
+
utils.wrapTextCentered(
|
|
734
|
+
ctx,
|
|
735
|
+
text,
|
|
736
|
+
tooltipX + width / 2,
|
|
737
|
+
tooltipY + height / 2,
|
|
738
|
+
width - 16,
|
|
739
|
+
20
|
|
740
|
+
);
|
|
741
|
+
|
|
742
|
+
// Restaura o estado do contexto
|
|
743
|
+
ctx.restore();
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
/**
|
|
747
|
+
* Cria um badge
|
|
748
|
+
*
|
|
749
|
+
* @param {CanvasRenderingContext2D} ctx - Contexto de renderização
|
|
750
|
+
* @param {number} x - Posição X do badge
|
|
751
|
+
* @param {number} y - Posição Y do badge
|
|
752
|
+
* @param {string} text - Texto do badge
|
|
753
|
+
* @param {Object} [options={}] - Opções de configuração
|
|
754
|
+
* @param {string} [options.backgroundColor="#007BFF"] - Cor de fundo do badge
|
|
755
|
+
* @param {string} [options.textColor="#FFFFFF"] - Cor do texto
|
|
756
|
+
* @param {number} [options.borderRadius=12] - Raio da borda
|
|
757
|
+
* @param {string} [options.font="bold 12px Poppins"] - Fonte do texto
|
|
758
|
+
* @param {number} [options.paddingX=8] - Padding horizontal
|
|
759
|
+
* @param {number} [options.paddingY=4] - Padding vertical
|
|
760
|
+
* @param {boolean} [options.isAnimated=false] - Se o badge deve ter efeito de pulso
|
|
761
|
+
*/
|
|
762
|
+
function drawBadge(ctx, x, y, text, options = {}) {
|
|
763
|
+
// Valores padrão
|
|
764
|
+
const defaults = {
|
|
765
|
+
backgroundColor: "#007BFF",
|
|
766
|
+
textColor: "#FFFFFF",
|
|
767
|
+
borderRadius: 12,
|
|
768
|
+
font: "bold 12px Poppins",
|
|
769
|
+
paddingX: 8,
|
|
770
|
+
paddingY: 4,
|
|
771
|
+
isAnimated: false
|
|
772
|
+
};
|
|
773
|
+
|
|
774
|
+
// Mescla as opções com os valores padrão
|
|
775
|
+
const settings = { ...defaults, ...options };
|
|
776
|
+
|
|
777
|
+
// Salva o estado atual do contexto
|
|
778
|
+
ctx.save();
|
|
779
|
+
|
|
780
|
+
// Configura o texto para medir suas dimensões
|
|
781
|
+
ctx.font = settings.font;
|
|
782
|
+
const textWidth = ctx.measureText(text).width;
|
|
783
|
+
const textHeight = parseInt(settings.font) * 0.7; // Aproximação da altura
|
|
784
|
+
|
|
785
|
+
// Calcula as dimensões do badge
|
|
786
|
+
const width = textWidth + settings.paddingX * 2;
|
|
787
|
+
const height = textHeight + settings.paddingY * 2;
|
|
788
|
+
|
|
789
|
+
// Adiciona efeito de pulso se necessário
|
|
790
|
+
if (settings.isAnimated) {
|
|
791
|
+
animationEffects.addPulseEffect(
|
|
792
|
+
ctx,
|
|
793
|
+
x + width / 2,
|
|
794
|
+
y + height / 2,
|
|
795
|
+
Math.max(width, height) / 2,
|
|
796
|
+
settings.backgroundColor,
|
|
797
|
+
0.3
|
|
798
|
+
);
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
// Desenha o fundo do badge
|
|
802
|
+
ctx.fillStyle = settings.backgroundColor;
|
|
803
|
+
utils.roundRect(ctx, x, y, width, height, settings.borderRadius, true);
|
|
804
|
+
|
|
805
|
+
// Desenha o texto
|
|
806
|
+
ctx.fillStyle = settings.textColor;
|
|
807
|
+
ctx.textAlign = "center";
|
|
808
|
+
ctx.textBaseline = "middle";
|
|
809
|
+
ctx.fillText(text, x + width / 2, y + height / 2);
|
|
810
|
+
|
|
811
|
+
// Restaura o estado do contexto
|
|
812
|
+
ctx.restore();
|
|
813
|
+
|
|
814
|
+
// Retorna as dimensões do badge para uso posterior
|
|
815
|
+
return { width, height };
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
/**
|
|
819
|
+
* Cria um spinner de carregamento
|
|
820
|
+
*
|
|
821
|
+
* @param {CanvasRenderingContext2D} ctx - Contexto de renderização
|
|
822
|
+
* @param {number} x - Posição X do spinner
|
|
823
|
+
* @param {number} y - Posição Y do spinner
|
|
824
|
+
* @param {number} radius - Raio do spinner
|
|
825
|
+
* @param {Object} [options={}] - Opções de configuração
|
|
826
|
+
* @param {string} [options.color="#007BFF"] - Cor do spinner
|
|
827
|
+
* @param {number} [options.lineWidth=3] - Largura da linha
|
|
828
|
+
* @param {number} [options.segments=8] - Número de segmentos
|
|
829
|
+
* @param {number} [options.rotation=0.7] - Rotação simulada (0-1)
|
|
830
|
+
* @param {boolean} [options.showText=false] - Se deve mostrar texto
|
|
831
|
+
* @param {string} [options.text="Carregando..."] - Texto a ser mostrado
|
|
832
|
+
* @param {string} [options.textColor="#333333"] - Cor do texto
|
|
833
|
+
* @param {string} [options.font="14px Poppins"] - Fonte do texto
|
|
834
|
+
*/
|
|
835
|
+
function drawLoadingSpinner(ctx, x, y, radius, options = {}) {
|
|
836
|
+
// Valores padrão
|
|
837
|
+
const defaults = {
|
|
838
|
+
color: "#007BFF",
|
|
839
|
+
lineWidth: 3,
|
|
840
|
+
segments: 8,
|
|
841
|
+
rotation: 0.7,
|
|
842
|
+
showText: false,
|
|
843
|
+
text: "Carregando...",
|
|
844
|
+
textColor: "#333333",
|
|
845
|
+
font: "14px Poppins"
|
|
846
|
+
};
|
|
847
|
+
|
|
848
|
+
// Mescla as opções com os valores padrão
|
|
849
|
+
const settings = { ...defaults, ...options };
|
|
850
|
+
|
|
851
|
+
// Salva o estado atual do contexto
|
|
852
|
+
ctx.save();
|
|
853
|
+
|
|
854
|
+
// Desenha os segmentos do spinner
|
|
855
|
+
const angleStep = (Math.PI * 2) / settings.segments;
|
|
856
|
+
const startAngle = settings.rotation * Math.PI * 2;
|
|
857
|
+
|
|
858
|
+
for (let i = 0; i < settings.segments; i++) {
|
|
859
|
+
const angle = startAngle + i * angleStep;
|
|
860
|
+
const opacity = 0.2 + (0.8 * i) / settings.segments;
|
|
861
|
+
|
|
862
|
+
ctx.beginPath();
|
|
863
|
+
ctx.arc(x, y, radius, angle, angle + angleStep * 0.8);
|
|
864
|
+
ctx.strokeStyle = utils.hexToRgba(settings.color, opacity);
|
|
865
|
+
ctx.lineWidth = settings.lineWidth;
|
|
866
|
+
ctx.stroke();
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
// Desenha o texto se necessário
|
|
870
|
+
if (settings.showText) {
|
|
871
|
+
ctx.font = settings.font;
|
|
872
|
+
ctx.fillStyle = settings.textColor;
|
|
873
|
+
ctx.textAlign = "center";
|
|
874
|
+
ctx.textBaseline = "middle";
|
|
875
|
+
ctx.fillText(settings.text, x, y + radius + 20);
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
// Restaura o estado do contexto
|
|
879
|
+
ctx.restore();
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
/**
|
|
883
|
+
* Cria uma barra de progresso
|
|
884
|
+
*
|
|
885
|
+
* @param {CanvasRenderingContext2D} ctx - Contexto de renderização
|
|
886
|
+
* @param {number} x - Posição X da barra
|
|
887
|
+
* @param {number} y - Posição Y da barra
|
|
888
|
+
* @param {number} width - Largura da barra
|
|
889
|
+
* @param {number} height - Altura da barra
|
|
890
|
+
* @param {number} [progress=0.5] - Progresso (0-1)
|
|
891
|
+
* @param {Object} [options={}] - Opções de configuração
|
|
892
|
+
* @param {string} [options.backgroundColor="#EEEEEE"] - Cor de fundo da barra
|
|
893
|
+
* @param {string} [options.progressColor="#007BFF"] - Cor do progresso
|
|
894
|
+
* @param {number} [options.borderRadius=4] - Raio da borda
|
|
895
|
+
* @param {boolean} [options.showPercentage=false] - Se deve mostrar porcentagem
|
|
896
|
+
* @param {string} [options.textColor="#333333"] - Cor do texto
|
|
897
|
+
* @param {string} [options.font="12px Poppins"] - Fonte do texto
|
|
898
|
+
* @param {boolean} [options.isAnimated=false] - Se a barra deve ter efeito de brilho
|
|
899
|
+
*/
|
|
900
|
+
function drawProgressBar(ctx, x, y, width, height, progress = 0.5, options = {}) {
|
|
901
|
+
// Valores padrão
|
|
902
|
+
const defaults = {
|
|
903
|
+
backgroundColor: "#EEEEEE",
|
|
904
|
+
progressColor: "#007BFF",
|
|
905
|
+
borderRadius: 4,
|
|
906
|
+
showPercentage: false,
|
|
907
|
+
textColor: "#333333",
|
|
908
|
+
font: "12px Poppins",
|
|
909
|
+
isAnimated: false
|
|
910
|
+
};
|
|
911
|
+
|
|
912
|
+
// Mescla as opções com os valores padrão
|
|
913
|
+
const settings = { ...defaults, ...options };
|
|
914
|
+
|
|
915
|
+
// Limita o progresso entre 0 e 1
|
|
916
|
+
progress = Math.max(0, Math.min(1, progress));
|
|
917
|
+
|
|
918
|
+
// Salva o estado atual do contexto
|
|
919
|
+
ctx.save();
|
|
920
|
+
|
|
921
|
+
// Desenha o fundo da barra
|
|
922
|
+
ctx.fillStyle = settings.backgroundColor;
|
|
923
|
+
utils.roundRect(ctx, x, y, width, height, settings.borderRadius, true);
|
|
924
|
+
|
|
925
|
+
// Calcula a largura do progresso
|
|
926
|
+
const progressWidth = width * progress;
|
|
927
|
+
|
|
928
|
+
// Desenha o progresso
|
|
929
|
+
if (progressWidth > 0) {
|
|
930
|
+
ctx.fillStyle = settings.progressColor;
|
|
931
|
+
utils.roundRect(ctx, x, y, progressWidth, height, settings.borderRadius, true);
|
|
932
|
+
|
|
933
|
+
// Adiciona efeito de brilho se necessário
|
|
934
|
+
if (settings.isAnimated) {
|
|
935
|
+
animationEffects.addHighlightEffect(
|
|
936
|
+
ctx,
|
|
937
|
+
x,
|
|
938
|
+
y,
|
|
939
|
+
progressWidth,
|
|
940
|
+
height,
|
|
941
|
+
"#FFFFFF",
|
|
942
|
+
45,
|
|
943
|
+
0.3
|
|
944
|
+
);
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
// Desenha a porcentagem se necessário
|
|
949
|
+
if (settings.showPercentage) {
|
|
950
|
+
const percentage = Math.round(progress * 100);
|
|
951
|
+
ctx.font = settings.font;
|
|
952
|
+
ctx.fillStyle = settings.textColor;
|
|
953
|
+
ctx.textAlign = "center";
|
|
954
|
+
ctx.textBaseline = "middle";
|
|
955
|
+
ctx.fillText(`${percentage}%`, x + width / 2, y + height / 2);
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
// Restaura o estado do contexto
|
|
959
|
+
ctx.restore();
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
/**
|
|
963
|
+
* Cria um botão de mídia social
|
|
964
|
+
*
|
|
965
|
+
* @param {CanvasRenderingContext2D} ctx - Contexto de renderização
|
|
966
|
+
* @param {number} x - Posição X do botão
|
|
967
|
+
* @param {number} y - Posição Y do botão
|
|
968
|
+
* @param {number} size - Tamanho do botão
|
|
969
|
+
* @param {string} platform - Plataforma de mídia social ("facebook", "twitter", "instagram", "linkedin", "youtube", "tiktok")
|
|
970
|
+
* @param {Object} [options={}] - Opções de configuração
|
|
971
|
+
* @param {boolean} [options.isHovered=false] - Se o botão está em estado de hover
|
|
972
|
+
* @param {boolean} [options.isRound=true] - Se o botão deve ser redondo
|
|
973
|
+
* @param {boolean} [options.showName=false] - Se deve mostrar o nome da plataforma
|
|
974
|
+
* @param {string} [options.namePosition="right"] - Posição do nome ("right", "bottom")
|
|
975
|
+
* @param {string} [options.font="14px Poppins"] - Fonte do nome
|
|
976
|
+
*/
|
|
977
|
+
function drawSocialButton(ctx, x, y, size, platform, options = {}) {
|
|
978
|
+
// Valores padrão
|
|
979
|
+
const defaults = {
|
|
980
|
+
isHovered: false,
|
|
981
|
+
isRound: true,
|
|
982
|
+
showName: false,
|
|
983
|
+
namePosition: "right",
|
|
984
|
+
font: "14px Poppins"
|
|
985
|
+
};
|
|
986
|
+
|
|
987
|
+
// Mescla as opções com os valores padrão
|
|
988
|
+
const settings = { ...defaults, ...options };
|
|
989
|
+
|
|
990
|
+
// Configurações específicas para cada plataforma
|
|
991
|
+
const platforms = {
|
|
992
|
+
facebook: {
|
|
993
|
+
color: "#1877F2",
|
|
994
|
+
icon: "f", // Simplificado para exemplo
|
|
995
|
+
name: "Facebook"
|
|
996
|
+
},
|
|
997
|
+
twitter: {
|
|
998
|
+
color: "#1DA1F2",
|
|
999
|
+
icon: "t",
|
|
1000
|
+
name: "Twitter"
|
|
1001
|
+
},
|
|
1002
|
+
instagram: {
|
|
1003
|
+
color: "#E4405F",
|
|
1004
|
+
icon: "i",
|
|
1005
|
+
name: "Instagram"
|
|
1006
|
+
},
|
|
1007
|
+
linkedin: {
|
|
1008
|
+
color: "#0A66C2",
|
|
1009
|
+
icon: "in",
|
|
1010
|
+
name: "LinkedIn"
|
|
1011
|
+
},
|
|
1012
|
+
youtube: {
|
|
1013
|
+
color: "#FF0000",
|
|
1014
|
+
icon: "yt",
|
|
1015
|
+
name: "YouTube"
|
|
1016
|
+
},
|
|
1017
|
+
tiktok: {
|
|
1018
|
+
color: "#000000",
|
|
1019
|
+
icon: "tk",
|
|
1020
|
+
name: "TikTok"
|
|
1021
|
+
}
|
|
1022
|
+
};
|
|
1023
|
+
|
|
1024
|
+
// Obtém as configurações da plataforma
|
|
1025
|
+
const platformConfig = platforms[platform.toLowerCase()] || {
|
|
1026
|
+
color: "#999999",
|
|
1027
|
+
icon: "?",
|
|
1028
|
+
name: platform
|
|
1029
|
+
};
|
|
1030
|
+
|
|
1031
|
+
// Salva o estado atual do contexto
|
|
1032
|
+
ctx.save();
|
|
1033
|
+
|
|
1034
|
+
// Adiciona efeito de hover se necessário
|
|
1035
|
+
if (settings.isHovered) {
|
|
1036
|
+
ctx.shadowColor = "rgba(0,0,0,0.3)";
|
|
1037
|
+
ctx.shadowBlur = 10;
|
|
1038
|
+
ctx.shadowOffsetX = 0;
|
|
1039
|
+
ctx.shadowOffsetY = 0;
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
// Desenha o fundo do botão
|
|
1043
|
+
ctx.fillStyle = platformConfig.color;
|
|
1044
|
+
|
|
1045
|
+
if (settings.isRound) {
|
|
1046
|
+
ctx.beginPath();
|
|
1047
|
+
ctx.arc(x + size / 2, y + size / 2, size / 2, 0, Math.PI * 2);
|
|
1048
|
+
ctx.fill();
|
|
1049
|
+
} else {
|
|
1050
|
+
utils.roundRect(ctx, x, y, size, size, size * 0.2, true);
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
// Remove a sombra
|
|
1054
|
+
ctx.shadowColor = "transparent";
|
|
1055
|
+
ctx.shadowBlur = 0;
|
|
1056
|
+
ctx.shadowOffsetX = 0;
|
|
1057
|
+
ctx.shadowOffsetY = 0;
|
|
1058
|
+
|
|
1059
|
+
// Desenha o ícone (simplificado para exemplo)
|
|
1060
|
+
ctx.fillStyle = "#FFFFFF";
|
|
1061
|
+
ctx.font = `bold ${size * 0.5}px Arial`;
|
|
1062
|
+
ctx.textAlign = "center";
|
|
1063
|
+
ctx.textBaseline = "middle";
|
|
1064
|
+
ctx.fillText(platformConfig.icon, x + size / 2, y + size / 2);
|
|
1065
|
+
|
|
1066
|
+
// Desenha o nome se necessário
|
|
1067
|
+
if (settings.showName) {
|
|
1068
|
+
ctx.font = settings.font;
|
|
1069
|
+
ctx.fillStyle = "#333333";
|
|
1070
|
+
ctx.textAlign = "left";
|
|
1071
|
+
|
|
1072
|
+
if (settings.namePosition === "bottom") {
|
|
1073
|
+
ctx.textAlign = "center";
|
|
1074
|
+
ctx.fillText(platformConfig.name, x + size / 2, y + size + 20);
|
|
1075
|
+
} else {
|
|
1076
|
+
ctx.textBaseline = "middle";
|
|
1077
|
+
ctx.fillText(platformConfig.name, x + size + 10, y + size / 2);
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
// Restaura o estado do contexto
|
|
1082
|
+
ctx.restore();
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
/**
|
|
1086
|
+
* Cria um botão de reprodução de mídia
|
|
1087
|
+
*
|
|
1088
|
+
* @param {CanvasRenderingContext2D} ctx - Contexto de renderização
|
|
1089
|
+
* @param {number} x - Posição X do botão
|
|
1090
|
+
* @param {number} y - Posição Y do botão
|
|
1091
|
+
* @param {number} size - Tamanho do botão
|
|
1092
|
+
* @param {string} type - Tipo do botão ("play", "pause", "stop", "next", "previous")
|
|
1093
|
+
* @param {Object} [options={}] - Opções de configuração
|
|
1094
|
+
* @param {string} [options.backgroundColor="rgba(0,0,0,0.7)"] - Cor de fundo do botão
|
|
1095
|
+
* @param {string} [options.iconColor="#FFFFFF"] - Cor do ícone
|
|
1096
|
+
* @param {boolean} [options.isHovered=false] - Se o botão está em estado de hover
|
|
1097
|
+
* @param {boolean} [options.isActive=false] - Se o botão está ativo
|
|
1098
|
+
*/
|
|
1099
|
+
function drawMediaButton(ctx, x, y, size, type, options = {}) {
|
|
1100
|
+
// Valores padrão
|
|
1101
|
+
const defaults = {
|
|
1102
|
+
backgroundColor: "rgba(0,0,0,0.7)",
|
|
1103
|
+
iconColor: "#FFFFFF",
|
|
1104
|
+
isHovered: false,
|
|
1105
|
+
isActive: false
|
|
1106
|
+
};
|
|
1107
|
+
|
|
1108
|
+
// Mescla as opções com os valores padrão
|
|
1109
|
+
const settings = { ...defaults, ...options };
|
|
1110
|
+
|
|
1111
|
+
// Salva o estado atual do contexto
|
|
1112
|
+
ctx.save();
|
|
1113
|
+
|
|
1114
|
+
// Adiciona efeito de hover ou ativo
|
|
1115
|
+
if (settings.isHovered || settings.isActive) {
|
|
1116
|
+
ctx.shadowColor = "rgba(0,0,0,0.5)";
|
|
1117
|
+
ctx.shadowBlur = 10;
|
|
1118
|
+
ctx.shadowOffsetX = 0;
|
|
1119
|
+
ctx.shadowOffsetY = 0;
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
// Desenha o fundo do botão
|
|
1123
|
+
ctx.fillStyle = settings.isActive ? utils.adjustColor(settings.backgroundColor, 0.2) : settings.backgroundColor;
|
|
1124
|
+
ctx.beginPath();
|
|
1125
|
+
ctx.arc(x + size / 2, y + size / 2, size / 2, 0, Math.PI * 2);
|
|
1126
|
+
ctx.fill();
|
|
1127
|
+
|
|
1128
|
+
// Remove a sombra
|
|
1129
|
+
ctx.shadowColor = "transparent";
|
|
1130
|
+
ctx.shadowBlur = 0;
|
|
1131
|
+
ctx.shadowOffsetX = 0;
|
|
1132
|
+
ctx.shadowOffsetY = 0;
|
|
1133
|
+
|
|
1134
|
+
// Desenha o ícone
|
|
1135
|
+
ctx.fillStyle = settings.iconColor;
|
|
1136
|
+
|
|
1137
|
+
const iconSize = size * 0.4;
|
|
1138
|
+
const centerX = x + size / 2;
|
|
1139
|
+
const centerY = y + size / 2;
|
|
1140
|
+
|
|
1141
|
+
switch (type) {
|
|
1142
|
+
case "play":
|
|
1143
|
+
ctx.beginPath();
|
|
1144
|
+
ctx.moveTo(centerX - iconSize / 3, centerY - iconSize / 2);
|
|
1145
|
+
ctx.lineTo(centerX - iconSize / 3, centerY + iconSize / 2);
|
|
1146
|
+
ctx.lineTo(centerX + iconSize / 2, centerY);
|
|
1147
|
+
ctx.closePath();
|
|
1148
|
+
ctx.fill();
|
|
1149
|
+
break;
|
|
1150
|
+
case "pause":
|
|
1151
|
+
ctx.fillRect(centerX - iconSize / 2, centerY - iconSize / 2, iconSize / 3, iconSize);
|
|
1152
|
+
ctx.fillRect(centerX + iconSize / 6, centerY - iconSize / 2, iconSize / 3, iconSize);
|
|
1153
|
+
break;
|
|
1154
|
+
case "stop":
|
|
1155
|
+
ctx.fillRect(centerX - iconSize / 2, centerY - iconSize / 2, iconSize, iconSize);
|
|
1156
|
+
break;
|
|
1157
|
+
case "next":
|
|
1158
|
+
ctx.beginPath();
|
|
1159
|
+
ctx.moveTo(centerX - iconSize / 2, centerY - iconSize / 2);
|
|
1160
|
+
ctx.lineTo(centerX, centerY);
|
|
1161
|
+
ctx.lineTo(centerX - iconSize / 2, centerY + iconSize / 2);
|
|
1162
|
+
ctx.closePath();
|
|
1163
|
+
ctx.fill();
|
|
1164
|
+
|
|
1165
|
+
ctx.beginPath();
|
|
1166
|
+
ctx.moveTo(centerX, centerY - iconSize / 2);
|
|
1167
|
+
ctx.lineTo(centerX + iconSize / 2, centerY);
|
|
1168
|
+
ctx.lineTo(centerX, centerY + iconSize / 2);
|
|
1169
|
+
ctx.closePath();
|
|
1170
|
+
ctx.fill();
|
|
1171
|
+
break;
|
|
1172
|
+
case "previous":
|
|
1173
|
+
ctx.beginPath();
|
|
1174
|
+
ctx.moveTo(centerX + iconSize / 2, centerY - iconSize / 2);
|
|
1175
|
+
ctx.lineTo(centerX, centerY);
|
|
1176
|
+
ctx.lineTo(centerX + iconSize / 2, centerY + iconSize / 2);
|
|
1177
|
+
ctx.closePath();
|
|
1178
|
+
ctx.fill();
|
|
1179
|
+
|
|
1180
|
+
ctx.beginPath();
|
|
1181
|
+
ctx.moveTo(centerX, centerY - iconSize / 2);
|
|
1182
|
+
ctx.lineTo(centerX - iconSize / 2, centerY);
|
|
1183
|
+
ctx.lineTo(centerX, centerY + iconSize / 2);
|
|
1184
|
+
ctx.closePath();
|
|
1185
|
+
ctx.fill();
|
|
1186
|
+
break;
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1189
|
+
// Restaura o estado do contexto
|
|
1190
|
+
ctx.restore();
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
module.exports = {
|
|
1194
|
+
drawInteractiveButton,
|
|
1195
|
+
drawInputField,
|
|
1196
|
+
drawCheckbox,
|
|
1197
|
+
drawRadioButton,
|
|
1198
|
+
drawSlider,
|
|
1199
|
+
drawToggleSwitch,
|
|
1200
|
+
drawDropdown,
|
|
1201
|
+
drawTooltip,
|
|
1202
|
+
drawBadge,
|
|
1203
|
+
drawLoadingSpinner,
|
|
1204
|
+
drawProgressBar,
|
|
1205
|
+
drawSocialButton,
|
|
1206
|
+
drawMediaButton
|
|
1207
|
+
};
|
|
1208
|
+
|