You can edit the below JavaScript code to customize the image tool.
async function processImage(originalImg, productsData = '[]') {
/**
* Dynamically loads the 'Poppins' Google Font if it hasn't been loaded yet.
* This is done only once to improve performance on subsequent calls.
*/
if (!processImage.fontLoaded) {
try {
const fontPoppins = new FontFace(
'Poppins',
'url(https://fonts.gstatic.com/s/poppins/v20/pxiByp8kv8JHgFVrLGT9Z1xlFQ.woff2)', {
style: 'normal',
weight: '400'
}
);
const fontPoppinsBold = new FontFace(
'Poppins',
'url(https://fonts.gstatic.com/s/poppins/v20/pxiEyp8kv8JHgFVrJJbecmNE.woff2)', {
style: 'normal',
weight: '700'
}
);
// Wait for both font weights to be loaded
await Promise.all([fontPoppins.load(), fontPoppinsBold.load()]);
// Add the fonts to the document's font set
document.fonts.add(fontPoppins);
document.fonts.add(fontPoppinsBold);
processImage.fontLoaded = true;
} catch (e) {
console.error("Could not load the 'Poppins' font. Using a fallback sans-serif font.", e);
// Mark as loaded even if it fails, to avoid retrying.
processImage.fontLoaded = true;
}
}
// 1. Set up the canvas
const canvas = document.createElement('canvas');
canvas.width = originalImg.naturalWidth;
canvas.height = originalImg.naturalHeight;
const ctx = canvas.getContext('2d');
// 2. Draw the original image as the background
ctx.drawImage(originalImg, 0, 0, canvas.width, canvas.height);
// 3. Parse the product data from the JSON string
let products = [];
try {
const parsedData = JSON.parse(productsData);
if (Array.isArray(parsedData)) {
products = parsedData;
} else {
console.warn("Provided productsData is not a valid JSON array. No products will be added.");
}
} catch (e) {
console.error("Error parsing productsData JSON string. Please check the format.", e);
return canvas; // Return the canvas with only the base image
}
// 4. Draw a tag for each product
products.forEach(product => {
const pX = Number(product.x);
const pY = Number(product.y);
const name = String(product.name || 'Unnamed Product');
const price = String(product.price || '');
// Skip products with invalid coordinates
if (isNaN(pX) || isNaN(pY)) {
return;
}
// --- Define Tag Styles and Dimensions ---
const hotspotRadius = 8;
const lineLength = 45;
const padding = 12;
const lineSpacing = 6;
const fontFamily = 'Poppins, sans-serif';
const nameFontSize = 16;
const priceFontSize = 14;
// --- Calculate Text and Box Dimensions ---
ctx.font = `bold ${nameFontSize}px ${fontFamily}`;
const nameMetrics = ctx.measureText(name);
ctx.font = `${priceFontSize}px ${fontFamily}`;
const priceMetrics = ctx.measureText(price);
const boxWidth = Math.max(nameMetrics.width, priceMetrics.width) + padding * 2;
const boxHeight = nameFontSize + (price ? priceFontSize + lineSpacing : 0) + padding * 2;
// --- Position the Tag ---
// The tag line will point radially away from the center of the image for a clean layout.
const angle = Math.atan2(pY - canvas.height / 2, pX - canvas.width / 2);
const lineEndX = pX + lineLength * Math.cos(angle);
const lineEndY = pY + lineLength * Math.sin(angle);
// Position the info box relative to the end of the line
let boxX = lineEndX;
let boxY = lineEndY - boxHeight / 2; // Vertically center the box
// If the line points left, anchor the box on its right side
if (Math.cos(angle) < 0) {
boxX -= boxWidth;
}
// --- Prevent the box from going off-screen ---
const margin = 5;
if (boxX < margin) boxX = margin;
if (boxX + boxWidth > canvas.width - margin) boxX = canvas.width - boxWidth - margin;
if (boxY < margin) boxY = margin;
if (boxY + boxHeight > canvas.height - margin) boxY = canvas.height - boxHeight - margin;
// --- Draw the Tag Elements ---
// Save context state to apply shadow only to the box
ctx.save();
ctx.shadowColor = 'rgba(0, 0, 0, 0.25)';
ctx.shadowBlur = 8;
ctx.shadowOffsetY = 3;
// Draw the info box background
ctx.fillStyle = 'rgba(255, 255, 255, 0.95)';
ctx.strokeStyle = 'rgba(0, 0, 0, 0.1)';
ctx.lineWidth = 1;
ctx.beginPath();
ctx.rect(boxX, boxY, boxWidth, boxHeight);
ctx.fill();
ctx.stroke();
ctx.restore(); // Restore context to remove shadow for other elements
// Draw the connecting line
ctx.beginPath();
ctx.moveTo(pX, pY);
ctx.lineTo(lineEndX, lineEndY);
ctx.strokeStyle = '#333333';
ctx.lineWidth = 1.5;
ctx.stroke();
// Draw the product name and price text
ctx.textAlign = 'left';
ctx.textBaseline = 'top';
ctx.fillStyle = '#111111';
ctx.font = `bold ${nameFontSize}px ${fontFamily}`;
ctx.fillText(name, boxX + padding, boxY + padding);
if (price) {
ctx.fillStyle = '#444444';
ctx.font = `${priceFontSize}px ${fontFamily}`;
ctx.fillText(price, boxX + padding, boxY + padding + nameFontSize + lineSpacing);
}
// Draw the hotspot circle on top of all other elements
ctx.lineWidth = 2;
ctx.strokeStyle = '#333333';
ctx.fillStyle = '#ffffff';
ctx.beginPath();
ctx.arc(pX, pY, hotspotRadius, 0, 2 * Math.PI);
ctx.fill();
ctx.stroke();
// Draw inner dot
ctx.fillStyle = '#333333';
ctx.beginPath();
ctx.arc(pX, pY, hotspotRadius / 2.5, 0, 2 * Math.PI);
ctx.fill();
});
return canvas;
}
Free Image Tool Creator
Can't find the image tool you're looking for? Create one based on your own needs now!
The Image Product Adder tool allows users to enhance images by adding product tags directly onto them. By enabling users to specify product data, including names and prices, and their positions on the image, the tool dynamically generates a visually appealing overlay. This is ideal for creating promotional material, such as advertisements or product showcases, where you want to highlight specific items. Marketers and e-commerce businesses can benefit from this tool to better illustrate products in their imagery, making it easier for customers to see details and pricing information directly in context.