@cognima/banners
Version:
Biblioteca avançada para geração de banners dinâmicos para diversas plataformas e aplicações
1,209 lines (1,062 loc) • 41.5 kB
JavaScript
"use strict";
/**
* Módulo de elementos interativos para banners
*
* Este módulo fornece funções para criar elementos que simulam
* interatividade nos banners estáticos.
*
* @module interactive-elements
* @author Cognima Team (melhorado)
* @version 2.0.0
*/
const utils = require("../utils");
const animationEffects = require("./animation-effects");
/**
* Cria um botão com efeito de hover
*
* @param {CanvasRenderingContext2D} ctx - Contexto de renderização
* @param {number} x - Posição X do botão
* @param {number} y - Posição Y do botão
* @param {number} width - Largura do botão
* @param {number} height - Altura do botão
* @param {string} text - Texto do botão
* @param {Object} [options={}] - Opções de configuração
* @param {string} [options.backgroundColor="#007BFF"] - Cor de fundo do botão
* @param {string} [options.textColor="#FFFFFF"] - Cor do texto
* @param {string} [options.font="bold 24px Poppins"] - Fonte do texto
* @param {number} [options.borderRadius=8] - Raio da borda
* @param {boolean} [options.isHovered=false] - Se o botão está em estado de hover
* @param {string} [options.hoverEffect="glow"] - Efeito de hover ("glow", "scale", "shadow", "color")
* @param {string} [options.hoverColor="#0056b3"] - Cor do botão em estado de hover
*/
function drawInteractiveButton(ctx, x, y, width, height, text, options = {}) {
// Valores padrão
const defaults = {
backgroundColor: "#007BFF",
textColor: "#FFFFFF",
font: "bold 24px Poppins",
borderRadius: 8,
isHovered: false,
hoverEffect: "glow",
hoverColor: "#0056b3"
};
// Mescla as opções com os valores padrão
const settings = { ...defaults, ...options };
// Salva o estado atual do contexto
ctx.save();
// Aplica o efeito de hover se necessário
if (settings.isHovered) {
switch (settings.hoverEffect) {
case "glow":
animationEffects.addGlowEffect(
ctx,
x - 5,
y - 5,
width + 10,
height + 10,
settings.backgroundColor,
0.7,
15
);
break;
case "scale":
// Aumenta ligeiramente o tamanho do botão
x -= width * 0.05;
y -= height * 0.05;
width *= 1.1;
height *= 1.1;
break;
case "shadow":
animationEffects.add3DShadowEffect(
ctx,
x,
y,
width,
height,
8,
"#000000",
0.3
);
break;
case "color":
// Muda a cor de fundo
settings.backgroundColor = settings.hoverColor;
break;
}
}
// Desenha o botão
utils.roundRect(ctx, x, y, width, height, settings.borderRadius, true);
// Configura o texto
ctx.font = settings.font;
ctx.fillStyle = settings.textColor;
ctx.textAlign = "center";
ctx.textBaseline = "middle";
// Desenha o texto
ctx.fillText(text, x + width / 2, y + height / 2);
// Restaura o estado do contexto
ctx.restore();
}
/**
* Cria um campo de entrada de texto
*
* @param {CanvasRenderingContext2D} ctx - Contexto de renderização
* @param {number} x - Posição X do campo
* @param {number} y - Posição Y do campo
* @param {number} width - Largura do campo
* @param {number} height - Altura do campo
* @param {string} [placeholder="Digite aqui..."] - Texto de placeholder
* @param {string} [value=""] - Valor atual do campo
* @param {Object} [options={}] - Opções de configuração
* @param {string} [options.backgroundColor="#FFFFFF"] - Cor de fundo do campo
* @param {string} [options.textColor="#333333"] - Cor do texto
* @param {string} [options.placeholderColor="#999999"] - Cor do placeholder
* @param {string} [options.borderColor="#CCCCCC"] - Cor da borda
* @param {number} [options.borderWidth=1] - Largura da borda
* @param {number} [options.borderRadius=4] - Raio da borda
* @param {string} [options.font="16px Poppins"] - Fonte do texto
* @param {boolean} [options.isFocused=false] - Se o campo está em foco
* @param {boolean} [options.showCursor=false] - Se deve mostrar o cursor
*/
function drawInputField(ctx, x, y, width, height, placeholder = "Digite aqui...", value = "", options = {}) {
// Valores padrão
const defaults = {
backgroundColor: "#FFFFFF",
textColor: "#333333",
placeholderColor: "#999999",
borderColor: "#CCCCCC",
borderWidth: 1,
borderRadius: 4,
font: "16px Poppins",
isFocused: false,
showCursor: false
};
// Mescla as opções com os valores padrão
const settings = { ...defaults, ...options };
// Salva o estado atual do contexto
ctx.save();
// Desenha o fundo do campo
ctx.fillStyle = settings.backgroundColor;
utils.roundRect(ctx, x, y, width, height, settings.borderRadius, true);
// Desenha a borda
ctx.strokeStyle = settings.isFocused ? "#007BFF" : settings.borderColor;
ctx.lineWidth = settings.borderWidth;
utils.roundRect(ctx, x, y, width, height, settings.borderRadius, false, true);
// Configura o texto
ctx.font = settings.font;
ctx.textAlign = "left";
ctx.textBaseline = "middle";
// Padding interno
const paddingX = 10;
// Desenha o texto ou placeholder
if (value) {
ctx.fillStyle = settings.textColor;
// Limita o texto à largura do campo
const maxTextWidth = width - paddingX * 2;
const text = utils.truncateText(ctx, value, maxTextWidth);
ctx.fillText(text, x + paddingX, y + height / 2);
// Desenha o cursor se necessário
if (settings.isFocused && settings.showCursor) {
const textWidth = ctx.measureText(text).width;
ctx.fillRect(x + paddingX + textWidth + 2, y + height * 0.25, 2, height * 0.5);
}
} else {
ctx.fillStyle = settings.placeholderColor;
ctx.fillText(placeholder, x + paddingX, y + height / 2);
}
// Restaura o estado do contexto
ctx.restore();
}
/**
* Cria um checkbox
*
* @param {CanvasRenderingContext2D} ctx - Contexto de renderização
* @param {number} x - Posição X do checkbox
* @param {number} y - Posição Y do checkbox
* @param {number} size - Tamanho do checkbox
* @param {string} label - Texto do label
* @param {boolean} [checked=false] - Se o checkbox está marcado
* @param {Object} [options={}] - Opções de configuração
* @param {string} [options.backgroundColor="#FFFFFF"] - Cor de fundo do checkbox
* @param {string} [options.checkedColor="#007BFF"] - Cor quando marcado
* @param {string} [options.borderColor="#CCCCCC"] - Cor da borda
* @param {string} [options.textColor="#333333"] - Cor do texto
* @param {string} [options.font="16px Poppins"] - Fonte do texto
* @param {boolean} [options.isHovered=false] - Se o checkbox está em estado de hover
*/
function drawCheckbox(ctx, x, y, size, label, checked = false, options = {}) {
// Valores padrão
const defaults = {
backgroundColor: "#FFFFFF",
checkedColor: "#007BFF",
borderColor: "#CCCCCC",
textColor: "#333333",
font: "16px Poppins",
isHovered: false
};
// Mescla as opções com os valores padrão
const settings = { ...defaults, ...options };
// Salva o estado atual do contexto
ctx.save();
// Desenha o fundo do checkbox
ctx.fillStyle = settings.backgroundColor;
ctx.strokeStyle = settings.isHovered ? settings.checkedColor : settings.borderColor;
ctx.lineWidth = 2;
// Desenha o quadrado do checkbox
ctx.beginPath();
ctx.rect(x, y, size, size);
ctx.fill();
ctx.stroke();
// Desenha o check se estiver marcado
if (checked) {
ctx.fillStyle = settings.checkedColor;
// Desenha um check estilizado
ctx.beginPath();
ctx.moveTo(x + size * 0.2, y + size * 0.5);
ctx.lineTo(x + size * 0.4, y + size * 0.7);
ctx.lineTo(x + size * 0.8, y + size * 0.3);
ctx.lineWidth = 3;
ctx.strokeStyle = settings.checkedColor;
ctx.stroke();
}
// Desenha o label
ctx.font = settings.font;
ctx.fillStyle = settings.textColor;
ctx.textAlign = "left";
ctx.textBaseline = "middle";
ctx.fillText(label, x + size + 10, y + size / 2);
// Restaura o estado do contexto
ctx.restore();
}
/**
* Cria um radio button
*
* @param {CanvasRenderingContext2D} ctx - Contexto de renderização
* @param {number} x - Posição X do radio button
* @param {number} y - Posição Y do radio button
* @param {number} size - Tamanho do radio button
* @param {string} label - Texto do label
* @param {boolean} [selected=false] - Se o radio button está selecionado
* @param {Object} [options={}] - Opções de configuração
* @param {string} [options.backgroundColor="#FFFFFF"] - Cor de fundo do radio button
* @param {string} [options.selectedColor="#007BFF"] - Cor quando selecionado
* @param {string} [options.borderColor="#CCCCCC"] - Cor da borda
* @param {string} [options.textColor="#333333"] - Cor do texto
* @param {string} [options.font="16px Poppins"] - Fonte do texto
* @param {boolean} [options.isHovered=false] - Se o radio button está em estado de hover
*/
function drawRadioButton(ctx, x, y, size, label, selected = false, options = {}) {
// Valores padrão
const defaults = {
backgroundColor: "#FFFFFF",
selectedColor: "#007BFF",
borderColor: "#CCCCCC",
textColor: "#333333",
font: "16px Poppins",
isHovered: false
};
// Mescla as opções com os valores padrão
const settings = { ...defaults, ...options };
// Salva o estado atual do contexto
ctx.save();
// Desenha o círculo externo
ctx.beginPath();
ctx.arc(x + size / 2, y + size / 2, size / 2, 0, Math.PI * 2);
ctx.fillStyle = settings.backgroundColor;
ctx.fill();
ctx.strokeStyle = settings.isHovered ? settings.selectedColor : settings.borderColor;
ctx.lineWidth = 2;
ctx.stroke();
// Desenha o círculo interno se estiver selecionado
if (selected) {
ctx.beginPath();
ctx.arc(x + size / 2, y + size / 2, size / 4, 0, Math.PI * 2);
ctx.fillStyle = settings.selectedColor;
ctx.fill();
}
// Desenha o label
ctx.font = settings.font;
ctx.fillStyle = settings.textColor;
ctx.textAlign = "left";
ctx.textBaseline = "middle";
ctx.fillText(label, x + size + 10, y + size / 2);
// Restaura o estado do contexto
ctx.restore();
}
/**
* Cria um slider
*
* @param {CanvasRenderingContext2D} ctx - Contexto de renderização
* @param {number} x - Posição X do slider
* @param {number} y - Posição Y do slider
* @param {number} width - Largura do slider
* @param {number} [value=0.5] - Valor atual do slider (0-1)
* @param {Object} [options={}] - Opções de configuração
* @param {string} [options.trackColor="#CCCCCC"] - Cor da trilha
* @param {string} [options.progressColor="#007BFF"] - Cor do progresso
* @param {string} [options.handleColor="#FFFFFF"] - Cor do manipulador
* @param {number} [options.handleSize=20] - Tamanho do manipulador
* @param {number} [options.trackHeight=6] - Altura da trilha
* @param {boolean} [options.showValue=false] - Se deve mostrar o valor
* @param {number} [options.min=0] - Valor mínimo
* @param {number} [options.max=100] - Valor máximo
* @param {string} [options.valueFormat="{value}"] - Formato do valor
* @param {boolean} [options.isActive=false] - Se o slider está ativo
*/
function drawSlider(ctx, x, y, width, value = 0.5, options = {}) {
// Valores padrão
const defaults = {
trackColor: "#CCCCCC",
progressColor: "#007BFF",
handleColor: "#FFFFFF",
handleSize: 20,
trackHeight: 6,
showValue: false,
min: 0,
max: 100,
valueFormat: "{value}",
isActive: false
};
// Mescla as opções com os valores padrão
const settings = { ...defaults, ...options };
// Limita o valor entre 0 e 1
value = Math.max(0, Math.min(1, value));
// Salva o estado atual do contexto
ctx.save();
// Desenha a trilha
ctx.fillStyle = settings.trackColor;
utils.roundRect(ctx, x, y + settings.handleSize / 2 - settings.trackHeight / 2, width, settings.trackHeight, settings.trackHeight / 2, true);
// Desenha o progresso
const progressWidth = width * value;
ctx.fillStyle = settings.progressColor;
utils.roundRect(ctx, x, y + settings.handleSize / 2 - settings.trackHeight / 2, progressWidth, settings.trackHeight, settings.trackHeight / 2, true);
// Desenha o manipulador
const handleX = x + progressWidth - settings.handleSize / 2;
const handleY = y;
// Adiciona sombra se estiver ativo
if (settings.isActive) {
ctx.shadowColor = "rgba(0,0,0,0.3)";
ctx.shadowBlur = 10;
ctx.shadowOffsetX = 2;
ctx.shadowOffsetY = 2;
}
ctx.fillStyle = settings.handleColor;
ctx.strokeStyle = settings.progressColor;
ctx.lineWidth = 2;
ctx.beginPath();
ctx.arc(handleX, handleY + settings.handleSize / 2, settings.handleSize / 2, 0, Math.PI * 2);
ctx.fill();
ctx.stroke();
// Remove a sombra
ctx.shadowColor = "transparent";
ctx.shadowBlur = 0;
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 0;
// Mostra o valor se necessário
if (settings.showValue) {
const actualValue = Math.round(settings.min + (settings.max - settings.min) * value);
const valueText = settings.valueFormat.replace("{value}", actualValue);
ctx.font = "14px Poppins";
ctx.fillStyle = "#333333";
ctx.textAlign = "center";
ctx.fillText(valueText, x + width / 2, y + settings.handleSize + 20);
}
// Restaura o estado do contexto
ctx.restore();
}
/**
* Cria um toggle switch
*
* @param {CanvasRenderingContext2D} ctx - Contexto de renderização
* @param {number} x - Posição X do toggle
* @param {number} y - Posição Y do toggle
* @param {number} width - Largura do toggle
* @param {number} height - Altura do toggle
* @param {boolean} [checked=false] - Se o toggle está ligado
* @param {Object} [options={}] - Opções de configuração
* @param {string} [options.onColor="#007BFF"] - Cor quando ligado
* @param {string} [options.offColor="#CCCCCC"] - Cor quando desligado
* @param {string} [options.handleColor="#FFFFFF"] - Cor do manipulador
* @param {string} [options.label=""] - Texto do label
* @param {string} [options.labelColor="#333333"] - Cor do label
* @param {string} [options.font="16px Poppins"] - Fonte do label
* @param {boolean} [options.isHovered=false] - Se o toggle está em estado de hover
*/
function drawToggleSwitch(ctx, x, y, width, height, checked = false, options = {}) {
// Valores padrão
const defaults = {
onColor: "#007BFF",
offColor: "#CCCCCC",
handleColor: "#FFFFFF",
label: "",
labelColor: "#333333",
font: "16px Poppins",
isHovered: false
};
// Mescla as opções com os valores padrão
const settings = { ...defaults, ...options };
// Salva o estado atual do contexto
ctx.save();
// Desenha o fundo do toggle
ctx.fillStyle = checked ? settings.onColor : settings.offColor;
utils.roundRect(ctx, x, y, width, height, height / 2, true);
// Calcula a posição do manipulador
const handleSize = height - 4;
const handleX = checked ? x + width - handleSize - 2 : x + 2;
const handleY = y + 2;
// Adiciona sombra se estiver em hover
if (settings.isHovered) {
ctx.shadowColor = "rgba(0,0,0,0.2)";
ctx.shadowBlur = 5;
ctx.shadowOffsetX = 1;
ctx.shadowOffsetY = 1;
}
// Desenha o manipulador
ctx.fillStyle = settings.handleColor;
ctx.beginPath();
ctx.arc(handleX + handleSize / 2, handleY + handleSize / 2, handleSize / 2, 0, Math.PI * 2);
ctx.fill();
// Remove a sombra
ctx.shadowColor = "transparent";
ctx.shadowBlur = 0;
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 0;
// Desenha o label se necessário
if (settings.label) {
ctx.font = settings.font;
ctx.fillStyle = settings.labelColor;
ctx.textAlign = "left";
ctx.textBaseline = "middle";
ctx.fillText(settings.label, x + width + 10, y + height / 2);
}
// Restaura o estado do contexto
ctx.restore();
}
/**
* Cria um dropdown
*
* @param {CanvasRenderingContext2D} ctx - Contexto de renderização
* @param {number} x - Posição X do dropdown
* @param {number} y - Posição Y do dropdown
* @param {number} width - Largura do dropdown
* @param {number} height - Altura do dropdown
* @param {string} [selectedOption="Selecione..."] - Opção selecionada
* @param {Array<string>} [options=[]] - Lista de opções
* @param {Object} [settings={}] - Opções de configuração
* @param {string} [settings.backgroundColor="#FFFFFF"] - Cor de fundo do dropdown
* @param {string} [settings.textColor="#333333"] - Cor do texto
* @param {string} [settings.borderColor="#CCCCCC"] - Cor da borda
* @param {number} [settings.borderWidth=1] - Largura da borda
* @param {number} [settings.borderRadius=4] - Raio da borda
* @param {string} [settings.font="16px Poppins"] - Fonte do texto
* @param {boolean} [settings.isOpen=false] - Se o dropdown está aberto
* @param {boolean} [settings.isHovered=false] - Se o dropdown está em estado de hover
* @param {number} [settings.maxHeight=200] - Altura máxima do dropdown aberto
*/
function drawDropdown(ctx, x, y, width, height, selectedOption = "Selecione...", options = [], settings = {}) {
// Valores padrão
const defaults = {
backgroundColor: "#FFFFFF",
textColor: "#333333",
borderColor: "#CCCCCC",
borderWidth: 1,
borderRadius: 4,
font: "16px Poppins",
isOpen: false,
isHovered: false,
maxHeight: 200
};
// Mescla as opções com os valores padrão
const config = { ...defaults, ...settings };
// Salva o estado atual do contexto
ctx.save();
// Desenha o dropdown fechado
ctx.fillStyle = config.backgroundColor;
utils.roundRect(ctx, x, y, width, height, config.borderRadius, true);
// Desenha a borda
ctx.strokeStyle = config.isHovered || config.isOpen ? "#007BFF" : config.borderColor;
ctx.lineWidth = config.borderWidth;
utils.roundRect(ctx, x, y, width, height, config.borderRadius, false, true);
// Configura o texto
ctx.font = config.font;
ctx.fillStyle = config.textColor;
ctx.textAlign = "left";
ctx.textBaseline = "middle";
// Padding interno
const paddingX = 10;
// Desenha o texto selecionado
const maxTextWidth = width - paddingX * 2 - 20; // 20px para o ícone de seta
const truncatedText = utils.truncateText(ctx, selectedOption, maxTextWidth);
ctx.fillText(truncatedText, x + paddingX, y + height / 2);
// Desenha o ícone de seta
const arrowX = x + width - 20;
const arrowY = y + height / 2;
ctx.beginPath();
if (config.isOpen) {
// Seta para cima
ctx.moveTo(arrowX - 5, arrowY + 3);
ctx.lineTo(arrowX, arrowY - 3);
ctx.lineTo(arrowX + 5, arrowY + 3);
} else {
// Seta para baixo
ctx.moveTo(arrowX - 5, arrowY - 3);
ctx.lineTo(arrowX, arrowY + 3);
ctx.lineTo(arrowX + 5, arrowY - 3);
}
ctx.fillStyle = config.textColor;
ctx.fill();
// Desenha o dropdown aberto se necessário
if (config.isOpen && options.length > 0) {
const optionHeight = height;
const dropdownHeight = Math.min(options.length * optionHeight, config.maxHeight);
// Desenha o fundo do dropdown
ctx.fillStyle = config.backgroundColor;
utils.roundRect(ctx, x, y + height, width, dropdownHeight, config.borderRadius, true);
// Desenha a borda
ctx.strokeStyle = config.borderColor;
ctx.lineWidth = config.borderWidth;
utils.roundRect(ctx, x, y + height, width, dropdownHeight, config.borderRadius, false, true);
// Desenha as opções
for (let i = 0; i < options.length; i++) {
const optionY = y + height + i * optionHeight;
// Verifica se a opção está visível (para scrolling)
if (optionY < y + height + dropdownHeight) {
// Destaca a opção selecionada
if (options[i] === selectedOption) {
ctx.fillStyle = "#F0F0F0";
ctx.fillRect(x + 1, optionY, width - 2, optionHeight);
}
// Desenha o texto da opção
ctx.fillStyle = config.textColor;
const truncatedOption = utils.truncateText(ctx, options[i], width - paddingX * 2);
ctx.fillText(truncatedOption, x + paddingX, optionY + optionHeight / 2);
}
}
}
// Restaura o estado do contexto
ctx.restore();
}
/**
* Cria um tooltip
*
* @param {CanvasRenderingContext2D} ctx - Contexto de renderização
* @param {number} x - Posição X do tooltip
* @param {number} y - Posição Y do tooltip
* @param {number} width - Largura do tooltip
* @param {number} height - Altura do tooltip
* @param {string} text - Texto do tooltip
* @param {Object} [options={}] - Opções de configuração
* @param {string} [options.backgroundColor="#333333"] - Cor de fundo do tooltip
* @param {string} [options.textColor="#FFFFFF"] - Cor do texto
* @param {number} [options.borderRadius=4] - Raio da borda
* @param {string} [options.position="top"] - Posição do tooltip ("top", "bottom", "left", "right")
* @param {string} [options.font="14px Poppins"] - Fonte do texto
* @param {number} [options.arrowSize=8] - Tamanho da seta
*/
function drawTooltip(ctx, x, y, width, height, text, options = {}) {
// Valores padrão
const defaults = {
backgroundColor: "#333333",
textColor: "#FFFFFF",
borderRadius: 4,
position: "top",
font: "14px Poppins",
arrowSize: 8
};
// Mescla as opções com os valores padrão
const settings = { ...defaults, ...options };
// Salva o estado atual do contexto
ctx.save();
// Calcula a posição do tooltip e da seta
let tooltipX = x;
let tooltipY = y;
let arrowX, arrowY;
switch (settings.position) {
case "bottom":
tooltipY = y + settings.arrowSize;
arrowX = tooltipX + width / 2;
arrowY = tooltipY - settings.arrowSize;
break;
case "left":
tooltipX = x - width - settings.arrowSize;
arrowX = tooltipX + width;
arrowY = tooltipY + height / 2;
break;
case "right":
tooltipX = x + settings.arrowSize;
arrowX = tooltipX - settings.arrowSize;
arrowY = tooltipY + height / 2;
break;
case "top":
default:
tooltipY = y - height - settings.arrowSize;
arrowX = tooltipX + width / 2;
arrowY = tooltipY + height;
break;
}
// Adiciona sombra
ctx.shadowColor = "rgba(0,0,0,0.3)";
ctx.shadowBlur = 5;
ctx.shadowOffsetX = 2;
ctx.shadowOffsetY = 2;
// Desenha o fundo do tooltip
ctx.fillStyle = settings.backgroundColor;
utils.roundRect(ctx, tooltipX, tooltipY, width, height, settings.borderRadius, true);
// Remove a sombra para a seta
ctx.shadowColor = "transparent";
ctx.shadowBlur = 0;
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 0;
// Desenha a seta
ctx.fillStyle = settings.backgroundColor;
ctx.beginPath();
switch (settings.position) {
case "bottom":
ctx.moveTo(arrowX - settings.arrowSize, arrowY + settings.arrowSize);
ctx.lineTo(arrowX, arrowY);
ctx.lineTo(arrowX + settings.arrowSize, arrowY + settings.arrowSize);
break;
case "left":
ctx.moveTo(arrowX - settings.arrowSize, arrowY - settings.arrowSize);
ctx.lineTo(arrowX, arrowY);
ctx.lineTo(arrowX - settings.arrowSize, arrowY + settings.arrowSize);
break;
case "right":
ctx.moveTo(arrowX + settings.arrowSize, arrowY - settings.arrowSize);
ctx.lineTo(arrowX, arrowY);
ctx.lineTo(arrowX + settings.arrowSize, arrowY + settings.arrowSize);
break;
case "top":
default:
ctx.moveTo(arrowX - settings.arrowSize, arrowY - settings.arrowSize);
ctx.lineTo(arrowX, arrowY);
ctx.lineTo(arrowX + settings.arrowSize, arrowY - settings.arrowSize);
break;
}
ctx.closePath();
ctx.fill();
// Desenha o texto
ctx.font = settings.font;
ctx.fillStyle = settings.textColor;
ctx.textAlign = "center";
ctx.textBaseline = "middle";
// Quebra o texto se necessário
utils.wrapTextCentered(
ctx,
text,
tooltipX + width / 2,
tooltipY + height / 2,
width - 16,
20
);
// Restaura o estado do contexto
ctx.restore();
}
/**
* Cria um badge
*
* @param {CanvasRenderingContext2D} ctx - Contexto de renderização
* @param {number} x - Posição X do badge
* @param {number} y - Posição Y do badge
* @param {string} text - Texto do badge
* @param {Object} [options={}] - Opções de configuração
* @param {string} [options.backgroundColor="#007BFF"] - Cor de fundo do badge
* @param {string} [options.textColor="#FFFFFF"] - Cor do texto
* @param {number} [options.borderRadius=12] - Raio da borda
* @param {string} [options.font="bold 12px Poppins"] - Fonte do texto
* @param {number} [options.paddingX=8] - Padding horizontal
* @param {number} [options.paddingY=4] - Padding vertical
* @param {boolean} [options.isAnimated=false] - Se o badge deve ter efeito de pulso
*/
function drawBadge(ctx, x, y, text, options = {}) {
// Valores padrão
const defaults = {
backgroundColor: "#007BFF",
textColor: "#FFFFFF",
borderRadius: 12,
font: "bold 12px Poppins",
paddingX: 8,
paddingY: 4,
isAnimated: false
};
// Mescla as opções com os valores padrão
const settings = { ...defaults, ...options };
// Salva o estado atual do contexto
ctx.save();
// Configura o texto para medir suas dimensões
ctx.font = settings.font;
const textWidth = ctx.measureText(text).width;
const textHeight = parseInt(settings.font) * 0.7; // Aproximação da altura
// Calcula as dimensões do badge
const width = textWidth + settings.paddingX * 2;
const height = textHeight + settings.paddingY * 2;
// Adiciona efeito de pulso se necessário
if (settings.isAnimated) {
animationEffects.addPulseEffect(
ctx,
x + width / 2,
y + height / 2,
Math.max(width, height) / 2,
settings.backgroundColor,
0.3
);
}
// Desenha o fundo do badge
ctx.fillStyle = settings.backgroundColor;
utils.roundRect(ctx, x, y, width, height, settings.borderRadius, true);
// Desenha o texto
ctx.fillStyle = settings.textColor;
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillText(text, x + width / 2, y + height / 2);
// Restaura o estado do contexto
ctx.restore();
// Retorna as dimensões do badge para uso posterior
return { width, height };
}
/**
* Cria um spinner de carregamento
*
* @param {CanvasRenderingContext2D} ctx - Contexto de renderização
* @param {number} x - Posição X do spinner
* @param {number} y - Posição Y do spinner
* @param {number} radius - Raio do spinner
* @param {Object} [options={}] - Opções de configuração
* @param {string} [options.color="#007BFF"] - Cor do spinner
* @param {number} [options.lineWidth=3] - Largura da linha
* @param {number} [options.segments=8] - Número de segmentos
* @param {number} [options.rotation=0.7] - Rotação simulada (0-1)
* @param {boolean} [options.showText=false] - Se deve mostrar texto
* @param {string} [options.text="Carregando..."] - Texto a ser mostrado
* @param {string} [options.textColor="#333333"] - Cor do texto
* @param {string} [options.font="14px Poppins"] - Fonte do texto
*/
function drawLoadingSpinner(ctx, x, y, radius, options = {}) {
// Valores padrão
const defaults = {
color: "#007BFF",
lineWidth: 3,
segments: 8,
rotation: 0.7,
showText: false,
text: "Carregando...",
textColor: "#333333",
font: "14px Poppins"
};
// Mescla as opções com os valores padrão
const settings = { ...defaults, ...options };
// Salva o estado atual do contexto
ctx.save();
// Desenha os segmentos do spinner
const angleStep = (Math.PI * 2) / settings.segments;
const startAngle = settings.rotation * Math.PI * 2;
for (let i = 0; i < settings.segments; i++) {
const angle = startAngle + i * angleStep;
const opacity = 0.2 + (0.8 * i) / settings.segments;
ctx.beginPath();
ctx.arc(x, y, radius, angle, angle + angleStep * 0.8);
ctx.strokeStyle = utils.hexToRgba(settings.color, opacity);
ctx.lineWidth = settings.lineWidth;
ctx.stroke();
}
// Desenha o texto se necessário
if (settings.showText) {
ctx.font = settings.font;
ctx.fillStyle = settings.textColor;
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillText(settings.text, x, y + radius + 20);
}
// Restaura o estado do contexto
ctx.restore();
}
/**
* Cria uma barra de progresso
*
* @param {CanvasRenderingContext2D} ctx - Contexto de renderização
* @param {number} x - Posição X da barra
* @param {number} y - Posição Y da barra
* @param {number} width - Largura da barra
* @param {number} height - Altura da barra
* @param {number} [progress=0.5] - Progresso (0-1)
* @param {Object} [options={}] - Opções de configuração
* @param {string} [options.backgroundColor="#EEEEEE"] - Cor de fundo da barra
* @param {string} [options.progressColor="#007BFF"] - Cor do progresso
* @param {number} [options.borderRadius=4] - Raio da borda
* @param {boolean} [options.showPercentage=false] - Se deve mostrar porcentagem
* @param {string} [options.textColor="#333333"] - Cor do texto
* @param {string} [options.font="12px Poppins"] - Fonte do texto
* @param {boolean} [options.isAnimated=false] - Se a barra deve ter efeito de brilho
*/
function drawProgressBar(ctx, x, y, width, height, progress = 0.5, options = {}) {
// Valores padrão
const defaults = {
backgroundColor: "#EEEEEE",
progressColor: "#007BFF",
borderRadius: 4,
showPercentage: false,
textColor: "#333333",
font: "12px Poppins",
isAnimated: false
};
// Mescla as opções com os valores padrão
const settings = { ...defaults, ...options };
// Limita o progresso entre 0 e 1
progress = Math.max(0, Math.min(1, progress));
// Salva o estado atual do contexto
ctx.save();
// Desenha o fundo da barra
ctx.fillStyle = settings.backgroundColor;
utils.roundRect(ctx, x, y, width, height, settings.borderRadius, true);
// Calcula a largura do progresso
const progressWidth = width * progress;
// Desenha o progresso
if (progressWidth > 0) {
ctx.fillStyle = settings.progressColor;
utils.roundRect(ctx, x, y, progressWidth, height, settings.borderRadius, true);
// Adiciona efeito de brilho se necessário
if (settings.isAnimated) {
animationEffects.addHighlightEffect(
ctx,
x,
y,
progressWidth,
height,
"#FFFFFF",
45,
0.3
);
}
}
// Desenha a porcentagem se necessário
if (settings.showPercentage) {
const percentage = Math.round(progress * 100);
ctx.font = settings.font;
ctx.fillStyle = settings.textColor;
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillText(`${percentage}%`, x + width / 2, y + height / 2);
}
// Restaura o estado do contexto
ctx.restore();
}
/**
* Cria um botão de mídia social
*
* @param {CanvasRenderingContext2D} ctx - Contexto de renderização
* @param {number} x - Posição X do botão
* @param {number} y - Posição Y do botão
* @param {number} size - Tamanho do botão
* @param {string} platform - Plataforma de mídia social ("facebook", "twitter", "instagram", "linkedin", "youtube", "tiktok")
* @param {Object} [options={}] - Opções de configuração
* @param {boolean} [options.isHovered=false] - Se o botão está em estado de hover
* @param {boolean} [options.isRound=true] - Se o botão deve ser redondo
* @param {boolean} [options.showName=false] - Se deve mostrar o nome da plataforma
* @param {string} [options.namePosition="right"] - Posição do nome ("right", "bottom")
* @param {string} [options.font="14px Poppins"] - Fonte do nome
*/
function drawSocialButton(ctx, x, y, size, platform, options = {}) {
// Valores padrão
const defaults = {
isHovered: false,
isRound: true,
showName: false,
namePosition: "right",
font: "14px Poppins"
};
// Mescla as opções com os valores padrão
const settings = { ...defaults, ...options };
// Configurações específicas para cada plataforma
const platforms = {
facebook: {
color: "#1877F2",
icon: "f", // Simplificado para exemplo
name: "Facebook"
},
twitter: {
color: "#1DA1F2",
icon: "t",
name: "Twitter"
},
instagram: {
color: "#E4405F",
icon: "i",
name: "Instagram"
},
linkedin: {
color: "#0A66C2",
icon: "in",
name: "LinkedIn"
},
youtube: {
color: "#FF0000",
icon: "yt",
name: "YouTube"
},
tiktok: {
color: "#000000",
icon: "tk",
name: "TikTok"
}
};
// Obtém as configurações da plataforma
const platformConfig = platforms[platform.toLowerCase()] || {
color: "#999999",
icon: "?",
name: platform
};
// Salva o estado atual do contexto
ctx.save();
// Adiciona efeito de hover se necessário
if (settings.isHovered) {
ctx.shadowColor = "rgba(0,0,0,0.3)";
ctx.shadowBlur = 10;
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 0;
}
// Desenha o fundo do botão
ctx.fillStyle = platformConfig.color;
if (settings.isRound) {
ctx.beginPath();
ctx.arc(x + size / 2, y + size / 2, size / 2, 0, Math.PI * 2);
ctx.fill();
} else {
utils.roundRect(ctx, x, y, size, size, size * 0.2, true);
}
// Remove a sombra
ctx.shadowColor = "transparent";
ctx.shadowBlur = 0;
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 0;
// Desenha o ícone (simplificado para exemplo)
ctx.fillStyle = "#FFFFFF";
ctx.font = `bold ${size * 0.5}px Arial`;
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillText(platformConfig.icon, x + size / 2, y + size / 2);
// Desenha o nome se necessário
if (settings.showName) {
ctx.font = settings.font;
ctx.fillStyle = "#333333";
ctx.textAlign = "left";
if (settings.namePosition === "bottom") {
ctx.textAlign = "center";
ctx.fillText(platformConfig.name, x + size / 2, y + size + 20);
} else {
ctx.textBaseline = "middle";
ctx.fillText(platformConfig.name, x + size + 10, y + size / 2);
}
}
// Restaura o estado do contexto
ctx.restore();
}
/**
* Cria um botão de reprodução de mídia
*
* @param {CanvasRenderingContext2D} ctx - Contexto de renderização
* @param {number} x - Posição X do botão
* @param {number} y - Posição Y do botão
* @param {number} size - Tamanho do botão
* @param {string} type - Tipo do botão ("play", "pause", "stop", "next", "previous")
* @param {Object} [options={}] - Opções de configuração
* @param {string} [options.backgroundColor="rgba(0,0,0,0.7)"] - Cor de fundo do botão
* @param {string} [options.iconColor="#FFFFFF"] - Cor do ícone
* @param {boolean} [options.isHovered=false] - Se o botão está em estado de hover
* @param {boolean} [options.isActive=false] - Se o botão está ativo
*/
function drawMediaButton(ctx, x, y, size, type, options = {}) {
// Valores padrão
const defaults = {
backgroundColor: "rgba(0,0,0,0.7)",
iconColor: "#FFFFFF",
isHovered: false,
isActive: false
};
// Mescla as opções com os valores padrão
const settings = { ...defaults, ...options };
// Salva o estado atual do contexto
ctx.save();
// Adiciona efeito de hover ou ativo
if (settings.isHovered || settings.isActive) {
ctx.shadowColor = "rgba(0,0,0,0.5)";
ctx.shadowBlur = 10;
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 0;
}
// Desenha o fundo do botão
ctx.fillStyle = settings.isActive ? utils.adjustColor(settings.backgroundColor, 0.2) : settings.backgroundColor;
ctx.beginPath();
ctx.arc(x + size / 2, y + size / 2, size / 2, 0, Math.PI * 2);
ctx.fill();
// Remove a sombra
ctx.shadowColor = "transparent";
ctx.shadowBlur = 0;
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 0;
// Desenha o ícone
ctx.fillStyle = settings.iconColor;
const iconSize = size * 0.4;
const centerX = x + size / 2;
const centerY = y + size / 2;
switch (type) {
case "play":
ctx.beginPath();
ctx.moveTo(centerX - iconSize / 3, centerY - iconSize / 2);
ctx.lineTo(centerX - iconSize / 3, centerY + iconSize / 2);
ctx.lineTo(centerX + iconSize / 2, centerY);
ctx.closePath();
ctx.fill();
break;
case "pause":
ctx.fillRect(centerX - iconSize / 2, centerY - iconSize / 2, iconSize / 3, iconSize);
ctx.fillRect(centerX + iconSize / 6, centerY - iconSize / 2, iconSize / 3, iconSize);
break;
case "stop":
ctx.fillRect(centerX - iconSize / 2, centerY - iconSize / 2, iconSize, iconSize);
break;
case "next":
ctx.beginPath();
ctx.moveTo(centerX - iconSize / 2, centerY - iconSize / 2);
ctx.lineTo(centerX, centerY);
ctx.lineTo(centerX - iconSize / 2, centerY + iconSize / 2);
ctx.closePath();
ctx.fill();
ctx.beginPath();
ctx.moveTo(centerX, centerY - iconSize / 2);
ctx.lineTo(centerX + iconSize / 2, centerY);
ctx.lineTo(centerX, centerY + iconSize / 2);
ctx.closePath();
ctx.fill();
break;
case "previous":
ctx.beginPath();
ctx.moveTo(centerX + iconSize / 2, centerY - iconSize / 2);
ctx.lineTo(centerX, centerY);
ctx.lineTo(centerX + iconSize / 2, centerY + iconSize / 2);
ctx.closePath();
ctx.fill();
ctx.beginPath();
ctx.moveTo(centerX, centerY - iconSize / 2);
ctx.lineTo(centerX - iconSize / 2, centerY);
ctx.lineTo(centerX, centerY + iconSize / 2);
ctx.closePath();
ctx.fill();
break;
}
// Restaura o estado do contexto
ctx.restore();
}
module.exports = {
drawInteractiveButton,
drawInputField,
drawCheckbox,
drawRadioButton,
drawSlider,
drawToggleSwitch,
drawDropdown,
drawTooltip,
drawBadge,
drawLoadingSpinner,
drawProgressBar,
drawSocialButton,
drawMediaButton
};