You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(originalImg, text = "Лучший друг", textColor = "#333333", frameColor = "#F9F6F0", heartColor = "#E63946") {
// Inject and load a clean handwritten font (Caveat supports Cyrillic chars gracefully)
const fontName = 'Caveat';
if (!document.getElementById('font-caveat')) {
const link = document.createElement('link');
link.id = 'font-caveat';
link.rel = 'stylesheet';
link.href = `https://fonts.googleapis.com/css2?family=${fontName}:wght@700&display=swap`;
document.head.appendChild(link);
}
try {
await document.fonts.ready;
await document.fonts.load(`700 20px "${fontName}"`);
} catch (e) {
console.warn("Font loading failed, falling back to safe alternatives.");
}
// Determine dimensions recursively based on the actual image sizes
const w = originalImg.naturalWidth;
const h = originalImg.naturalHeight;
const maxDim = Math.max(w, h);
const padding = Math.max(maxDim * 0.05, 20);
const bottomPadding = Math.max(maxDim * 0.20, 80);
const cw = w + padding * 2;
const ch = h + padding + bottomPadding;
const canvas = document.createElement("canvas");
canvas.width = cw;
canvas.height = ch;
const ctx = canvas.getContext("2d");
// 1. Draw polaroid frame wrapper
ctx.fillStyle = frameColor;
ctx.fillRect(0, 0, cw, ch);
// Subtle edge framing outline
ctx.strokeStyle = "#e8e5df";
ctx.lineWidth = Math.max(maxDim * 0.005, 2);
ctx.strokeRect(ctx.lineWidth / 2, ctx.lineWidth / 2, cw - ctx.lineWidth, ch - ctx.lineWidth);
// 2. Draw slight dark inset behind image
ctx.fillStyle = "#d0d0d0";
ctx.fillRect(padding - 2, padding - 2, w + 4, h + 4);
// 3. Draw the original image
ctx.drawImage(originalImg, padding, padding, w, h);
// 4. Draw a scrapbook adhesive tape at the top
ctx.save();
ctx.translate(cw / 2, padding);
ctx.rotate(-0.02); // Playful tilt
ctx.fillStyle = "rgba(255, 255, 255, 0.45)"; // Semi-transparent, cloudy white
ctx.shadowColor = "rgba(0, 0, 0, 0.15)";
ctx.shadowBlur = 5;
ctx.shadowOffsetY = 2;
const tapeW = Math.max(maxDim * 0.25, 80);
const tapeH = Math.max(padding * 0.5, 15);
ctx.fillRect(-tapeW / 2, -tapeH / 2, tapeW, tapeH);
// Draw little tape 'creases'
ctx.shadowColor = "transparent";
ctx.fillStyle = "rgba(255, 255, 255, 0.55)";
ctx.fillRect(-tapeW / 2 + tapeW * 0.05, -tapeH / 2, tapeW * 0.02, tapeH);
ctx.fillRect(tapeW / 2 - tapeW * 0.08, -tapeH / 2, tapeW * 0.02, tapeH);
ctx.restore();
// 5. Setup text properties
let fontSize = Math.max(bottomPadding * 0.35, 16);
ctx.font = `700 ${fontSize}px "${fontName}", "Comic Sans MS", cursive`;
// Evaluate combined Text + Heart footprint to fit it nicely with shrink-to-fit logic
const heartSize = fontSize * 0.75;
const heartWidth = heartSize * 2;
const maxAllowedWidth = cw - padding * 2;
let textMetrics = ctx.measureText(text);
let textWidth = textMetrics.width;
let gap = textWidth > 0 ? fontSize * 0.3 : 0;
let totalWidth = textWidth + gap + heartWidth;
// Shrink if needed (useful for extremely long names / inputs)
if (totalWidth > maxAllowedWidth) {
const scaleFact = maxAllowedWidth / totalWidth;
fontSize *= scaleFact;
ctx.font = `700 ${fontSize}px "${fontName}", "Comic Sans MS", cursive`;
textMetrics = ctx.measureText(text);
textWidth = textMetrics.width;
gap = textWidth > 0 ? fontSize * 0.3 : 0;
totalWidth = textWidth + gap + (fontSize * 0.75 * 2);
}
// 6. Draw handwritten text + Heart
const textY = h + padding + bottomPadding * 0.45;
ctx.save();
ctx.translate(cw / 2, textY);
ctx.rotate(-0.015); // Authentic handwritten slight slant applied globally to text + heart
const startX = -totalWidth / 2;
const textCenterX = startX + textWidth / 2;
const finalHeartSize = fontSize * 0.75;
const heartCenterX = startX + textWidth + gap + finalHeartSize;
// Soft ink pooling / shadow memory effect for visual charm
ctx.shadowColor = "rgba(0, 0, 0, 0.15)";
ctx.shadowBlur = 2;
ctx.shadowOffsetX = 1;
ctx.shadowOffsetY = 1;
// Draw text
if (textWidth > 0) {
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillStyle = textColor;
ctx.fillText(text, textCenterX, 0);
}
// Draw heart
ctx.translate(heartCenterX, -fontSize * 0.05); // Vertically align the heart with text
ctx.beginPath();
ctx.moveTo(0, finalHeartSize * 0.25);
ctx.bezierCurveTo(-finalHeartSize / 2, -finalHeartSize * 0.25, -finalHeartSize, finalHeartSize * 0.4, 0, finalHeartSize);
ctx.bezierCurveTo(finalHeartSize, finalHeartSize * 0.4, finalHeartSize / 2, -finalHeartSize * 0.25, 0, finalHeartSize * 0.25);
ctx.fillStyle = heartColor;
ctx.fill();
ctx.restore();
return canvas;
}
Apply Changes