You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(originalImg, name = "MONKEY D. LUFFY", bounty = "3000000000", titleText = "WANTED", subtitleText = "DEAD OR ALIVE", marineText = "MARINE") {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
// --- Configuration ---
const canvasWidth = 600;
const canvasHeight = 850;
const posterBackgroundColor = '#F5E8C7'; // Parchment like
const posterBorderColor = '#654321'; // Dark brown
const textPrimaryColor = '#3A241D'; // Very dark brown for titles
const textSecondaryColor = 'black'; // For name, bounty
const imageBorderColor = '#3A241D';
// --- Setup Canvas ---
canvas.width = canvasWidth;
canvas.height = canvasHeight;
// --- Helper to load Google Fonts ---
const loadGoogleFont = async (fontFamily, fontWeight = '400') => {
const fontCss = fontFamily.replace(/\s+/g, '+');
const fontUrl = `https://fonts.googleapis.com/css2?family=${fontCss}:wght@${fontWeight}&display=swap`;
let linkExists = false;
document.querySelectorAll('link[rel="stylesheet"]').forEach(link => {
if (link.href === fontUrl) {
linkExists = true;
}
});
if (!linkExists) {
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = fontUrl;
document.head.appendChild(link);
await new Promise((resolve, reject) => {
link.onload = resolve;
link.onerror = (err) => {
console.error(`Failed to load stylesheet: ${fontUrl}`, err);
reject(new Error(`Stylesheet load error for ${fontFamily}`));
};
});
}
try {
await document.fonts.load(`${fontWeight} 1em "${fontFamily}"`);
} catch (err) {
console.error(`Failed to load font: ${fontFamily} (weight ${fontWeight})`, err);
throw new Error(`Font load error for ${fontFamily}`);
}
};
// --- Load Fonts ---
try {
await Promise.all([
loadGoogleFont('Luckiest Guy', '400'),
loadGoogleFont('Pirata One', '400')
]);
} catch (error) {
console.error("Font loading failed:", error);
// Basic fallback: a message on canvas or using system fonts (though this might not look good)
ctx.fillStyle = 'red';
ctx.font = '20px sans-serif';
ctx.textAlign = 'center';
ctx.fillText('Error: Fonts could not be loaded.', canvasWidth / 2, canvasHeight / 2);
return canvas; // Return canvas with error message
}
// --- Ensure originalImg is loaded ---
if (!originalImg.complete || originalImg.naturalWidth === 0) {
try {
await new Promise((resolve, reject) => {
// If src is already set and image is loading/failed, these handlers should catch it.
// If src not set, this won't help. Caller must provide a valid Image object with src.
if (originalImg.src) { // Only if src is set
const oldOnload = originalImg.onload;
const oldOnerror = originalImg.onerror;
originalImg.onload = () => { oldOnload?.(); resolve(); };
originalImg.onerror = (e) => { oldOnerror?.(); reject(new Error("Original image failed to load")); };
// If already failed and onerror was called, above might not re-trigger.
// If src is set and it fails after this point, it will be caught.
// This relies on the Image object being properly handled before passing.
// A common case is just checking .complete again if it wasn't at first.
if(originalImg.complete && originalImg.naturalWidth !== 0) resolve();
} else {
reject(new Error("Original image has no src."));
}
});
} catch (e) {
console.error("Error with input image:", e.message);
ctx.fillStyle = 'red';
ctx.font = '20px sans-serif';
ctx.textAlign = 'center';
ctx.fillText('Error: Input image invalid.', canvasWidth / 2, canvasHeight / 2 + 30);
return canvas;
}
}
// --- Draw Background ---
ctx.fillStyle = posterBackgroundColor;
ctx.fillRect(0, 0, canvasWidth, canvasHeight);
// --- Draw Main Border ---
ctx.strokeStyle = posterBorderColor;
ctx.lineWidth = 10; // Defines the thickness of the border
ctx.strokeRect(ctx.lineWidth / 2, ctx.lineWidth / 2, canvasWidth - ctx.lineWidth, canvasHeight - ctx.lineWidth);
// --- Draw "WANTED" Text ---
ctx.font = '90px "Luckiest Guy"';
ctx.fillStyle = textPrimaryColor;
ctx.textAlign = 'center';
ctx.shadowColor = 'rgba(0,0,0,0.4)';
ctx.shadowBlur = 4;
ctx.shadowOffsetX = 3;
ctx.shadowOffsetY = 3;
ctx.fillText(titleText.toUpperCase(), canvasWidth / 2, 120);
ctx.shadowColor = 'transparent'; // Reset shadow
// --- Draw "DEAD OR ALIVE" Text ---
ctx.font = '35px "Pirata One"';
ctx.fillStyle = textPrimaryColor;
ctx.fillText(subtitleText.toUpperCase(), canvasWidth / 2, 195);
// --- Draw Image ---
const imgContainerX = 60;
const imgContainerY = 240;
const imgContainerWidth = canvasWidth - 2 * imgContainerX; // 480px
const imgContainerHeight = 300;
let drawWidth = originalImg.width;
let drawHeight = originalImg.height;
const imgAspect = originalImg.width / originalImg.height;
const containerAspect = imgContainerWidth / imgContainerHeight;
if (drawWidth > imgContainerWidth || drawHeight > imgContainerHeight) {
if (imgAspect > containerAspect) {
drawWidth = imgContainerWidth;
drawHeight = drawWidth / imgAspect;
} else {
drawHeight = imgContainerHeight;
drawWidth = drawHeight * imgAspect;
}
}
const dx = imgContainerX + (imgContainerWidth - drawWidth) / 2;
const dy = imgContainerY + (imgContainerHeight - drawHeight) / 2;
// Image border
ctx.strokeStyle = imageBorderColor;
ctx.lineWidth = 6;
// Draw border slightly larger than image to frame it
ctx.strokeRect(dx - (ctx.lineWidth/2), dy - (ctx.lineWidth/2), drawWidth + ctx.lineWidth, drawHeight + ctx.lineWidth);
ctx.drawImage(originalImg, dx, dy, drawWidth, drawHeight);
const imageBottomY = dy + drawHeight + (ctx.lineWidth/2); // Account for border visually pushing content down
// --- Draw Name Text ---
const nameY = imageBottomY + 70;
ctx.font = '60px "Pirata One"';
ctx.fillStyle = textSecondaryColor;
ctx.shadowColor = 'rgba(0,0,0,0.3)';
ctx.shadowBlur = 3;
ctx.shadowOffsetX = 2;
ctx.shadowOffsetY = 2;
ctx.fillText(name.toUpperCase(), canvasWidth / 2, nameY);
ctx.shadowColor = 'transparent'; // Reset shadow
// --- Draw Bounty Text ---
const bountyY = nameY + 70;
const numericBounty = Number(String(bounty).replace(/,/g, ''));
const bountyString = isNaN(numericBounty) ? String(bounty) : numericBounty.toLocaleString('en-US');
const formattedBounty = '฿' + bountyString + '-';
ctx.font = '50px "Pirata One"';
ctx.fillStyle = textSecondaryColor;
ctx.fillText(formattedBounty, canvasWidth / 2, bountyY);
// --- Draw "MARINE" Text ---
const marineY = canvasHeight - 50;
ctx.font = '30px "Pirata One"'; // Could use a different font if desired
ctx.fillStyle = textSecondaryColor;
ctx.fillText(marineText.toUpperCase(), canvasWidth / 2, marineY);
return canvas;
}
Apply Changes