@cognima/banners
Version:
Biblioteca avançada para geração de banners dinâmicos para diversas plataformas e aplicações
357 lines (312 loc) • 11.3 kB
JavaScript
;
/**
* Módulo de Efeitos Visuais para @cognima/banners
*
* Este módulo contém funções para aplicar efeitos visuais avançados aos banners,
* como gradientes, sombras, brilhos, padrões e outros efeitos decorativos.
*
* @author Cognima Team (melhorado)
* @version 2.0.0
*/
const {
createLinearGradient,
hexToRgba,
roundRect
} = require("../utils");
/**
* Aplica um efeito de vidro fosco (glassmorphism)
* @param {Object} ctx - Contexto de renderização 2D
* @param {number} x - Posição X
* @param {number} y - Posição Y
* @param {number} width - Largura
* @param {number} height - Altura
* @param {number} radius - Raio dos cantos
* @param {number} opacity - Opacidade (0-1)
* @param {string} color - Cor base (hexadecimal)
*/
function applyGlassmorphism(ctx, x, y, width, height, radius = 10, opacity = 0.2, color = "#FFFFFF") {
// Fundo semi-transparente
ctx.fillStyle = hexToRgba(color, opacity);
roundRect(ctx, x, y, width, height, radius, true, false);
// Borda sutil
ctx.strokeStyle = hexToRgba(color, opacity + 0.1);
ctx.lineWidth = 1;
roundRect(ctx, x, y, width, height, radius, false, true);
// Brilho superior (efeito de vidro)
const gradient = createLinearGradient(
ctx,
x,
y,
x,
y + height * 0.5,
hexToRgba(color, opacity + 0.1),
hexToRgba(color, 0),
"vertical"
);
ctx.fillStyle = gradient;
roundRect(ctx, x, y, width, height * 0.5, { tl: radius, tr: radius, br: 0, bl: 0 }, true, false);
}
/**
* Aplica um efeito de neomorfismo
* @param {Object} ctx - Contexto de renderização 2D
* @param {number} x - Posição X
* @param {number} y - Posição Y
* @param {number} width - Largura
* @param {number} height - Altura
* @param {number} radius - Raio dos cantos
* @param {string} baseColor - Cor base (hexadecimal)
* @param {boolean} pressed - Se o elemento está pressionado
*/
function applyNeomorphism(ctx, x, y, width, height, radius = 10, baseColor = "#E0E0E0", pressed = false) {
// Salva o estado atual
ctx.save();
// Desenha o fundo base
ctx.fillStyle = baseColor;
roundRect(ctx, x, y, width, height, radius, true, false);
if (pressed) {
// Efeito pressionado (sombra interna)
ctx.shadowColor = "rgba(0, 0, 0, 0.2)";
ctx.shadowBlur = 15;
ctx.shadowOffsetX = 5;
ctx.shadowOffsetY = 5;
roundRect(ctx, x + 2, y + 2, width - 4, height - 4, radius - 2, false, true);
ctx.shadowColor = "rgba(255, 255, 255, 0.7)";
ctx.shadowOffsetX = -5;
ctx.shadowOffsetY = -5;
roundRect(ctx, x + 2, y + 2, width - 4, height - 4, radius - 2, false, true);
} else {
// Efeito normal (sombra externa)
ctx.shadowColor = "rgba(0, 0, 0, 0.2)";
ctx.shadowBlur = 15;
ctx.shadowOffsetX = 5;
ctx.shadowOffsetY = 5;
roundRect(ctx, x, y, width, height, radius, false, true);
ctx.shadowColor = "rgba(255, 255, 255, 0.7)";
ctx.shadowOffsetX = -5;
ctx.shadowOffsetY = -5;
roundRect(ctx, x, y, width, height, radius, false, true);
}
// Restaura o estado
ctx.restore();
}
/**
* Aplica um efeito de gradiente com várias cores
* @param {Object} ctx - Contexto de renderização 2D
* @param {number} x - Posição X
* @param {number} y - Posição Y
* @param {number} width - Largura
* @param {number} height - Altura
* @param {Array} colors - Array de cores (hexadecimal)
* @param {string} direction - Direção do gradiente ('horizontal', 'vertical', 'diagonal', 'radial')
*/
function applyMultiColorGradient(ctx, x, y, width, height, colors, direction = 'horizontal') {
if (!colors || colors.length < 2) {
throw new Error("São necessárias pelo menos duas cores para criar um gradiente.");
}
let gradient;
if (direction === 'radial') {
// Gradiente radial
const centerX = x + width / 2;
const centerY = y + height / 2;
const radius = Math.max(width, height) / 2;
gradient = ctx.createRadialGradient(centerX, centerY, 0, centerX, centerY, radius);
} else {
// Gradiente linear
switch (direction) {
case 'horizontal':
gradient = ctx.createLinearGradient(x, y, x + width, y);
break;
case 'vertical':
gradient = ctx.createLinearGradient(x, y, x, y + height);
break;
case 'diagonal':
default:
gradient = ctx.createLinearGradient(x, y, x + width, y + height);
break;
}
}
// Adiciona as cores ao gradiente
const step = 1 / (colors.length - 1);
colors.forEach((color, index) => {
gradient.addColorStop(index * step, color);
});
return gradient;
}
/**
* Aplica um efeito de padrão de pontos
* @param {Object} ctx - Contexto de renderização 2D
* @param {number} x - Posição X
* @param {number} y - Posição Y
* @param {number} width - Largura
* @param {number} height - Altura
* @param {number} spacing - Espaçamento entre os pontos
* @param {number} radius - Raio dos pontos
* @param {string} color - Cor dos pontos (hexadecimal)
* @param {number} opacity - Opacidade dos pontos (0-1)
*/
function applyDotPattern(ctx, x, y, width, height, spacing = 20, radius = 2, color = "#000000", opacity = 0.2) {
// Salva o estado atual
ctx.save();
// Define a cor e opacidade
ctx.fillStyle = hexToRgba(color, opacity);
// Desenha os pontos
for (let posX = x; posX < x + width; posX += spacing) {
for (let posY = y; posY < y + height; posY += spacing) {
ctx.beginPath();
ctx.arc(posX, posY, radius, 0, Math.PI * 2);
ctx.fill();
}
}
// Restaura o estado
ctx.restore();
}
/**
* Aplica um efeito de padrão de linhas
* @param {Object} ctx - Contexto de renderização 2D
* @param {number} x - Posição X
* @param {number} y - Posição Y
* @param {number} width - Largura
* @param {number} height - Altura
* @param {number} spacing - Espaçamento entre as linhas
* @param {number} lineWidth - Largura das linhas
* @param {string} color - Cor das linhas (hexadecimal)
* @param {number} opacity - Opacidade das linhas (0-1)
* @param {string} direction - Direção das linhas ('horizontal', 'vertical', 'diagonal')
*/
function applyLinePattern(ctx, x, y, width, height, spacing = 20, lineWidth = 1, color = "#000000", opacity = 0.2, direction = 'horizontal') {
// Salva o estado atual
ctx.save();
// Define a cor, opacidade e largura da linha
ctx.strokeStyle = hexToRgba(color, opacity);
ctx.lineWidth = lineWidth;
// Desenha as linhas
switch (direction) {
case 'vertical':
for (let posX = x; posX < x + width; posX += spacing) {
ctx.beginPath();
ctx.moveTo(posX, y);
ctx.lineTo(posX, y + height);
ctx.stroke();
}
break;
case 'diagonal':
const diagonal = Math.sqrt(width * width + height * height);
const steps = diagonal / spacing;
const stepX = width / steps;
const stepY = height / steps;
for (let i = -steps; i <= steps * 2; i++) {
ctx.beginPath();
ctx.moveTo(x + i * stepX, y);
ctx.lineTo(x, y + i * stepY);
ctx.stroke();
}
break;
case 'horizontal':
default:
for (let posY = y; posY < y + height; posY += spacing) {
ctx.beginPath();
ctx.moveTo(x, posY);
ctx.lineTo(x + width, posY);
ctx.stroke();
}
break;
}
// Restaura o estado
ctx.restore();
}
/**
* Aplica um efeito de brilho (glow)
* @param {Object} ctx - Contexto de renderização 2D
* @param {number} x - Posição X
* @param {number} y - Posição Y
* @param {number} width - Largura
* @param {number} height - Altura
* @param {number} radius - Raio dos cantos
* @param {string} color - Cor do brilho (hexadecimal)
* @param {number} blurSize - Tamanho do desfoque
*/
function applyGlow(ctx, x, y, width, height, radius = 10, color = "#3498DB", blurSize = 20) {
// Salva o estado atual
ctx.save();
// Aplica o efeito de brilho
ctx.shadowColor = color;
ctx.shadowBlur = blurSize;
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 0;
// Desenha a forma com o brilho
ctx.fillStyle = color;
roundRect(ctx, x, y, width, height, radius, true, false);
// Restaura o estado
ctx.restore();
}
/**
* Aplica um efeito de textura de ruído
* @param {Object} ctx - Contexto de renderização 2D
* @param {number} x - Posição X
* @param {number} y - Posição Y
* @param {number} width - Largura
* @param {number} height - Altura
* @param {number} opacity - Opacidade do ruído (0-1)
* @param {string} color - Cor do ruído (hexadecimal)
*/
function applyNoiseTexture(ctx, x, y, width, height, opacity = 0.1, color = "#000000") {
// Cria um canvas temporário para o ruído
const tempCanvas = document.createElement('canvas');
tempCanvas.width = width;
tempCanvas.height = height;
const tempCtx = tempCanvas.getContext('2d');
// Cria os dados da imagem
const imageData = tempCtx.createImageData(width, height);
const data = imageData.data;
// Preenche os dados com ruído
for (let i = 0; i < data.length; i += 4) {
const value = Math.random() * 255;
data[i] = value; // R
data[i + 1] = value; // G
data[i + 2] = value; // B
data[i + 3] = Math.random() * 255 * opacity; // A
}
// Aplica os dados da imagem
tempCtx.putImageData(imageData, 0, 0);
// Desenha o ruído no contexto principal
ctx.save();
ctx.globalAlpha = opacity;
ctx.drawImage(tempCanvas, x, y, width, height);
ctx.restore();
}
/**
* Aplica um efeito de recorte de imagem em forma de texto
* @param {Object} ctx - Contexto de renderização 2D
* @param {string} text - Texto a ser usado como máscara
* @param {number} x - Posição X
* @param {number} y - Posição Y
* @param {Object} image - Imagem a ser recortada
* @param {string} font - Fonte do texto
*/
function applyTextClipping(ctx, text, x, y, image, font) {
// Salva o estado atual
ctx.save();
// Define a fonte
ctx.font = font;
// Cria o caminho do texto
ctx.fillStyle = "#000000";
ctx.textBaseline = "top";
ctx.fillText(text, x, y);
// Usa o texto como máscara de recorte
ctx.globalCompositeOperation = "source-in";
// Desenha a imagem
ctx.drawImage(image, x, y);
// Restaura o estado
ctx.restore();
}
// --- Exportações ---
module.exports = {
applyGlassmorphism,
applyNeomorphism,
applyMultiColorGradient,
applyDotPattern,
applyLinePattern,
applyGlow,
applyNoiseTexture,
applyTextClipping
};