@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.
Files changed (48) hide show
  1. package/assets/fonts/Manrope/Manrope-Bold.ttf +0 -0
  2. package/assets/fonts/Manrope/Manrope-Regular.ttf +0 -0
  3. package/assets/fonts/Others/AbyssinicaSIL-Regular.ttf +0 -0
  4. package/assets/fonts/Others/ChirpRegular.ttf +0 -0
  5. package/assets/fonts/Poppins/Poppins-Bold.ttf +0 -0
  6. package/assets/fonts/Poppins/Poppins-Medium.ttf +0 -0
  7. package/assets/fonts/Poppins/Poppins-Regular.ttf +0 -0
  8. package/assets/placeholders/album_art.png +0 -0
  9. package/assets/placeholders/avatar.png +0 -0
  10. package/assets/placeholders/badge.jpg +0 -0
  11. package/assets/placeholders/badge.png +0 -0
  12. package/assets/placeholders/badge_2.jpg +0 -0
  13. package/assets/placeholders/badge_3.jpg +0 -0
  14. package/assets/placeholders/badge_4.jpg +0 -0
  15. package/assets/placeholders/badge_5.jpg +0 -0
  16. package/assets/placeholders/banner.jpeg +0 -0
  17. package/assets/placeholders/images.jpeg +0 -0
  18. package/index.js +153 -0
  19. package/package.json +34 -0
  20. package/src/animation-effects.js +631 -0
  21. package/src/cache-manager.js +258 -0
  22. package/src/community-banner.js +1536 -0
  23. package/src/constants.js +208 -0
  24. package/src/discord-profile.js +584 -0
  25. package/src/e-commerce-banner.js +1214 -0
  26. package/src/effects.js +355 -0
  27. package/src/error-handler.js +305 -0
  28. package/src/event-banner.js +1319 -0
  29. package/src/facebook-post.js +679 -0
  30. package/src/gradient-welcome.js +430 -0
  31. package/src/image-filters.js +1034 -0
  32. package/src/image-processor.js +1014 -0
  33. package/src/instagram-post.js +504 -0
  34. package/src/interactive-elements.js +1208 -0
  35. package/src/linkedin-post.js +658 -0
  36. package/src/marketing-banner.js +1089 -0
  37. package/src/minimalist-banner.js +892 -0
  38. package/src/modern-profile.js +755 -0
  39. package/src/performance-optimizer.js +216 -0
  40. package/src/telegram-header.js +544 -0
  41. package/src/test-runner.js +645 -0
  42. package/src/tiktok-post.js +713 -0
  43. package/src/twitter-header.js +604 -0
  44. package/src/validator.js +442 -0
  45. package/src/welcome-leave.js +445 -0
  46. package/src/whatsapp-status.js +386 -0
  47. package/src/youtube-thumbnail.js +681 -0
  48. 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
+