You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, position = 'bottom-right', sizeRatio = '0.3', mainText = 'ПЕРВОЕ МЕСТО', numberText = '1') {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.width = originalImg.width;
canvas.height = originalImg.height;
// Draw the original base image
ctx.drawImage(originalImg, 0, 0);
const ratio = parseFloat(sizeRatio) || 0.3;
const minDim = Math.min(canvas.width, canvas.height);
const r = (minDim * ratio) / 2;
// Safety padding ensures that ribbons/badges are not cut off the edge
const padding = r * 2.2;
let cx, cy;
switch (position) {
case 'bottom-left':
cx = padding;
cy = canvas.height - padding;
break;
case 'top-right':
cx = canvas.width - padding;
cy = padding;
break;
case 'top-left':
cx = padding;
cy = padding;
break;
case 'center':
cx = canvas.width / 2;
cy = canvas.height / 2;
break;
case 'bottom-right':
default:
cx = canvas.width - padding;
cy = canvas.height - padding;
break;
}
// Creating an offscreen overlay canvas to construct the full marker.
// This allows applying a clean, unified drop shadow to the entire marker overlapping shapes.
const overlay = document.createElement('canvas');
overlay.width = canvas.width;
overlay.height = canvas.height;
const octx = overlay.getContext('2d');
// Theme Colors
const redMain = '#e74c3c';
const redDark = '#c0392b';
const goldOuter = '#f1c40f';
const goldMid = '#f39c12';
const goldDark = '#d35400';
// 1. Draw Hanging Swallowtail Ribbons
octx.fillStyle = redDark;
// Left Falling Ribbon
octx.beginPath();
octx.moveTo(cx - r * 0.1, cy);
octx.lineTo(cx - r * 0.9, cy + r * 2.3);
octx.lineTo(cx - r * 0.4, cy + r * 1.9);
octx.lineTo(cx - r * 0.1, cy + r * 2.3);
octx.closePath();
octx.fill();
// Right Falling Ribbon
octx.beginPath();
octx.moveTo(cx + r * 0.1, cy);
octx.lineTo(cx + r * 0.9, cy + r * 2.3);
octx.lineTo(cx + r * 0.4, cy + r * 1.9);
octx.lineTo(cx + r * 0.1, cy + r * 2.3);
octx.closePath();
octx.fill();
// 2. Draw Rosette Frills (Star/Sunburst pattern)
octx.fillStyle = redMain;
const spikes = 24;
const outerR = r * 1.35;
const innerR = r * 1.1;
octx.beginPath();
for (let i = 0; i < spikes * 2; i++) {
const radius = i % 2 === 0 ? outerR : innerR;
const angle = (i * Math.PI) / spikes;
const x = cx + Math.cos(angle) * radius;
const y = cy + Math.sin(angle) * radius;
if (i === 0) octx.moveTo(x, y);
else octx.lineTo(x, y);
}
octx.closePath();
octx.fill();
// Inner gap/stroke for rosette depth
octx.strokeStyle = redDark;
octx.lineWidth = r * 0.05;
octx.stroke();
// 3. Draw Gold Medal Core
// Outer Gold Border
octx.beginPath();
octx.arc(cx, cy, r, 0, 2 * Math.PI);
const gradOuter = octx.createLinearGradient(cx - r, cy - r, cx + r, cy + r);
gradOuter.addColorStop(0, goldOuter);
gradOuter.addColorStop(0.5, goldMid);
gradOuter.addColorStop(1, goldDark);
octx.fillStyle = gradOuter;
octx.fill();
// Inner Gold Recessed Face
octx.beginPath();
octx.arc(cx, cy, r * 0.85, 0, 2 * Math.PI);
const gradInner = octx.createLinearGradient(cx - r, cy + r, cx + r, cy - r);
gradInner.addColorStop(0, goldOuter);
gradInner.addColorStop(1, '#e67e22');
octx.fillStyle = gradInner;
octx.fill();
octx.strokeStyle = goldMid;
octx.lineWidth = r * 0.02;
octx.stroke();
// 4. Draw Number in the Center
octx.fillStyle = '#ffffff';
octx.shadowColor = 'rgba(0,0,0,0.3)';
octx.shadowBlur = r * 0.08;
octx.shadowOffsetX = r * 0.03;
octx.shadowOffsetY = r * 0.05;
octx.font = `bold ${r * 1.1}px "Impact", "Arial Black", sans-serif`;
octx.textAlign = 'center';
octx.textBaseline = 'middle';
// Add tiny vertical offset to strictly visually center bold capital letters
octx.fillText(numberText, cx, cy + r * 0.04);
octx.shadowColor = 'transparent'; // Cleanup shadow for next elements
// 5. Draw Primary Text Banner Overlapping Everything
const bannerW = r * 3.0;
const bannerH = r * 0.6;
const bannerY = cy + r * 0.65;
// Draw the tucked ends (tails) of the banner first
octx.fillStyle = redDark;
// Left Tail
octx.beginPath();
octx.moveTo(cx - bannerW/2 + r*0.3, bannerY + bannerH);
octx.lineTo(cx - bannerW/2 - r*0.4, bannerY + bannerH);
octx.lineTo(cx - bannerW/2 - r*0.2, bannerY + bannerH/2);
octx.lineTo(cx - bannerW/2 - r*0.4, bannerY);
octx.lineTo(cx - bannerW/2 + r*0.3, bannerY);
octx.fill();
// Right Tail
octx.beginPath();
octx.moveTo(cx + bannerW/2 - r*0.3, bannerY + bannerH);
octx.lineTo(cx + bannerW/2 + r*0.4, bannerY + bannerH);
octx.lineTo(cx + bannerW/2 + r*0.2, bannerY + bannerH/2);
octx.lineTo(cx + bannerW/2 + r*0.4, bannerY);
octx.lineTo(cx + bannerW/2 - r*0.3, bannerY);
octx.fill();
// Main Center Banner
octx.fillStyle = redMain;
octx.fillRect(cx - bannerW/2, bannerY, bannerW, bannerH);
// Delicate Gold Stroke on Banner
octx.strokeStyle = goldOuter;
octx.lineWidth = Math.max(1, r * 0.04);
octx.strokeRect(cx - bannerW/2, bannerY, bannerW, bannerH);
// Render Foreground Text
octx.fillStyle = '#ffffff';
octx.shadowColor = 'rgba(0,0,0,0.5)';
octx.shadowBlur = Math.max(2, r * 0.05);
octx.shadowOffsetX = 1;
octx.shadowOffsetY = 1;
octx.font = `bold ${bannerH * 0.6}px Arial, sans-serif`;
octx.fillText(mainText, cx, bannerY + bannerH / 2 + bannerH * 0.04, bannerW * 0.9);
// Apply structured overlay object to the final canvas with unified drop shadow layout
ctx.shadowColor = 'rgba(0,0,0,0.7)';
ctx.shadowBlur = r * 0.25;
ctx.shadowOffsetX = r * 0.1;
ctx.shadowOffsetY = r * 0.15;
ctx.drawImage(overlay, 0, 0);
return canvas;
}
Apply Changes