You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(originalImg, customText = "С Днём родителей!", frameColor = "#ffe6e6", textColor = "#a31d1d") {
// Dynamically load a beautiful Google Font for the text overlay
const fontName = 'Caveat';
const style = document.createElement('style');
style.appendChild(document.createTextNode(`
@import url('https://fonts.googleapis.com/css2?family=${fontName}:wght@700&display=swap');
`));
document.head.appendChild(style);
// Wait until the font is likely loaded.
try {
await document.fonts.load(`700 30px "${fontName}"`);
} catch (e) {
console.warn("Custom font loading failed, falling back to standard fonts.");
}
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
// Calculate dimensions dynamically relative to the image size
const baseScale = Math.min(originalImg.width, originalImg.height);
const marginX = Math.max(originalImg.width * 0.10, baseScale * 0.12);
const marginYTop = Math.max(originalImg.height * 0.10, baseScale * 0.12);
const marginYBottom = marginYTop + baseScale * 0.18; // Extra padding for text at the bottom
canvas.width = originalImg.width + marginX * 2;
canvas.height = originalImg.height + marginYTop + marginYBottom;
// 1. Draw elegant Frame Background gradient
const bgGrad = ctx.createLinearGradient(0, 0, canvas.width, canvas.height);
bgGrad.addColorStop(0, '#ffffff'); // Lighter top-left
bgGrad.addColorStop(1, frameColor); // Themed color bottom-right
ctx.fillStyle = bgGrad;
ctx.fillRect(0, 0, canvas.width, canvas.height);
// 2. Draw Decorative outer borders (classic look)
const inset1 = baseScale * 0.015;
const inset2 = baseScale * 0.03;
// Outer thin solid line
ctx.strokeStyle = "rgba(255, 255, 255, 0.8)";
ctx.lineWidth = Math.max(1, baseScale * 0.005);
ctx.strokeRect(inset1, inset1, canvas.width - inset1 * 2, canvas.height - inset1 * 2);
// Inner dotted playful line
ctx.strokeStyle = textColor;
ctx.lineWidth = Math.max(2, baseScale * 0.008);
ctx.lineCap = "round";
const dotSpacing = Math.max(10, baseScale * 0.02);
ctx.setLineDash([0, dotSpacing]);
ctx.strokeRect(inset2, inset2, canvas.width - inset2 * 2, canvas.height - inset2 * 2);
ctx.setLineDash([]); // Reset line dash for further drawing
// 3. Draw the Original Image with drop shadow
ctx.save();
ctx.shadowColor = 'rgba(0, 0, 0, 0.4)';
ctx.shadowBlur = baseScale * 0.03;
ctx.shadowOffsetX = baseScale * 0.01;
ctx.shadowOffsetY = baseScale * 0.015;
ctx.drawImage(originalImg, marginX, marginYTop, originalImg.width, originalImg.height);
ctx.restore();
// 4. Inner border overlapping the image for a framed look
ctx.strokeStyle = '#ffffff';
ctx.lineWidth = Math.max(5, baseScale * 0.01);
ctx.strokeRect(marginX, marginYTop, originalImg.width, originalImg.height);
// Tiny gold accent line on the inner border
ctx.strokeStyle = '#d4af37';
ctx.lineWidth = Math.max(1, baseScale * 0.003);
ctx.strokeRect(
marginX + ctx.lineWidth * 2,
marginYTop + ctx.lineWidth * 2,
originalImg.width - ctx.lineWidth * 4,
originalImg.height - ctx.lineWidth * 4
);
// Helper function to draw hearts
function drawHeart(x, y, size, color) {
ctx.save();
ctx.translate(x, y);
ctx.beginPath();
const topCurveHeight = size * 0.3;
ctx.moveTo(0, topCurveHeight);
// Top Left
ctx.bezierCurveTo(0, 0, -size/2, 0, -size/2, topCurveHeight);
// Bottom Left
ctx.bezierCurveTo(-size/2, (size + topCurveHeight)/2, 0, (size + topCurveHeight)/2, 0, size);
// Bottom Right
ctx.bezierCurveTo(0, (size + topCurveHeight)/2, size/2, (size + topCurveHeight)/2, size/2, topCurveHeight);
// Top Right
ctx.bezierCurveTo(size/2, 0, 0, 0, 0, topCurveHeight);
ctx.closePath();
// Fill and slight stroke for popping effect
ctx.shadowColor = 'rgba(0,0,0,0.2)';
ctx.shadowBlur = size * 0.1;
ctx.fillStyle = color;
ctx.fill();
ctx.lineWidth = Math.max(1, size * 0.03);
ctx.strokeStyle = 'white';
ctx.stroke();
ctx.restore();
}
// 5. Add love/family themed corner decorations (Hearts)
const cornerSize = baseScale * 0.09;
const cornerColors = ['#ff4d4d', '#ff9999']; // Red, light pink
drawHeart(marginX * 0.6, marginYTop * 0.6, cornerSize, cornerColors[0]);
drawHeart(canvas.width - marginX * 0.6, marginYTop * 0.6, cornerSize, cornerColors[1]);
drawHeart(marginX * 0.6, canvas.height - marginYBottom + baseScale * 0.05, cornerSize, cornerColors[1]);
drawHeart(canvas.width - marginX * 0.6, canvas.height - marginYBottom + baseScale * 0.05, cornerSize, cornerColors[0]);
// 6. Add Custom Text at the bottom
if (customText) {
let fontSize = marginYBottom * 0.35;
ctx.font = `700 ${fontSize}px "${fontName}", cursive, sans-serif`;
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
// Scale down text if it's too long to fit horizontally
const maxTextWidth = canvas.width - (marginX * 1.5);
while (ctx.measureText(customText).width > maxTextWidth && fontSize > 10) {
fontSize -= 2;
ctx.font = `700 ${fontSize}px "${fontName}", cursive, sans-serif`;
}
const textX = canvas.width / 2;
const textY = canvas.height - (marginYBottom / 2);
// Highlight text shadow
ctx.shadowColor = 'rgba(255, 255, 255, 0.9)';
ctx.shadowBlur = 8;
ctx.shadowOffsetX = 2;
ctx.shadowOffsetY = 2;
ctx.fillStyle = textColor;
ctx.fillText(customText, textX, textY);
// Reset shadow for next text-flanking items
ctx.shadowColor = 'transparent';
// Little hearts flanking the text
const textWidth = ctx.measureText(customText).width;
const miniHeartSize = fontSize * 0.5;
drawHeart(textX - textWidth / 2 - miniHeartSize * 1.5, textY - miniHeartSize * 0.4, miniHeartSize, cornerColors[0]);
drawHeart(textX + textWidth / 2 + miniHeartSize * 1.5, textY - miniHeartSize * 0.4, miniHeartSize, cornerColors[0]);
}
return canvas;
}
Apply Changes