Please bookmark this page to avoid losing your image tool!

Image Of Real Life Action Figure Toy In Package

(Free & Supports Bulk Upload)

Drag & drop your images here or

The result will appear here...
You can edit the below JavaScript code to customize the image tool.
async function processImage(
    originalImg,
    titleText = "ACTION HERO",
    brandName = "EPIC TOYS",
    ageRating = "AGES 4+",
    cardBaseColor = "#0077CC", // Main color of the packaging card
    cardAccentColor = "#FFD700", // Color for titles, highlights
    fontFamily = "Bangers", // Font for text elements. 'Bangers' is loaded by default.
    newFeatureText = "NEW!", // Text for the "NEW!" burst, e.g., "NEW!", "EXCLUSIVE!"
    newFeatureTextColor = "#FFFFFF", // Color of the text in the burst
    newFeatureBurstColor = "#FF0000" // Background color of the burst
) {

    // Helper: Load the 'Bangers' web font (used as a default cool font)
    async function loadDefaultFont() {
        const bangersFontFamily = "Bangers"; // The specific font family name
        const bangersFontUrl = "https://fonts.gstatic.com/s/bangers/v24/FeVQS0BTqb0h60ACL5k.woff2";
        
        let fontAlreadyAvailable = false;
        if (document.fonts) {
            try {
                // Check if the font is already loaded or available
                if (document.fonts.check(`1em ${bangersFontFamily}`)) {
                    fontAlreadyAvailable = true;
                }
            } catch (e) {
                // Some browsers (older Firefox) might throw an error on `check()`
                console.warn("Font checking might not be fully supported, attempting to load font if needed.");
            }
        } else {
            // console.log("document.fonts API not available. Cannot check/load web fonts dynamically in this environment.");
            return; // Cannot proceed with font loading
        }

        if (!fontAlreadyAvailable) {
            const font = new FontFace(bangersFontFamily, `url(${bangersFontUrl})`);
            try {
                await font.load();
                document.fonts.add(font);
                // console.log(`${bangersFontFamily} font loaded and added.`);
            } catch (e) {
                console.error(`Font ${bangersFontFamily} from ${bangersFontUrl} failed to load:`, e);
                // If Bangers fails to load, the canvas will use the specified `fontFamily` with system fallbacks.
            }
        } else {
            // console.log(`${bangersFontFamily} font is already available.`);
        }
    }

    // Helper: Draw Rounded Rectangle
    function roundRect(ctx, x, y, width, height, radius, fill, stroke) {
        if (typeof radius === 'number') {
            radius = {tl: radius, tr: radius, br: radius, bl: radius};
        } else {
            const defaultRadiusValues = {tl: 0, tr: 0, br: 0, bl: 0};
            for (const side in defaultRadiusValues) {
                radius[side] = radius[side] || defaultRadiusValues[side];
            }
        }
        ctx.beginPath();
        ctx.moveTo(x + radius.tl, y);
        ctx.lineTo(x + width - radius.tr, y);
        ctx.quadraticCurveTo(x + width, y, x + width, y + radius.tr);
        ctx.lineTo(x + width, y + height - radius.br);
        ctx.quadraticCurveTo(x + width, y + height, x + width - radius.br, y + height);
        ctx.lineTo(x + radius.bl, y + height);
        ctx.quadraticCurveTo(x, y + height, x, y + height - radius.bl);
        ctx.lineTo(x, y + radius.tl);
        ctx.quadraticCurveTo(x, y, x + radius.tl, y);
        ctx.closePath();
        if (fill) {
            ctx.fill();
        }
        if (stroke) {
            ctx.stroke();
        }
    }

    // Helper: Draw Star/Burst
    function drawStar(ctx, cx, cy, spikes, outerRadius, innerRadius, fillColor, strokeColor, lineWidth = 2) {
        let rotation = Math.PI / 2 * 3;
        let currentX = cx;
        let currentY = cy;
        const angleStep = Math.PI / spikes;

        ctx.beginPath();
        ctx.moveTo(cx, cy - outerRadius); // Start at the top point
        for (let i = 0; i < spikes; i++) {
            currentX = cx + Math.cos(rotation) * outerRadius;
            currentY = cy + Math.sin(rotation) * outerRadius;
            ctx.lineTo(currentX, currentY);
            rotation += angleStep;

            currentX = cx + Math.cos(rotation) * innerRadius;
            currentY = cy + Math.sin(rotation) * innerRadius;
            ctx.lineTo(currentX, currentY);
            rotation += angleStep;
        }
        ctx.lineTo(cx, cy - outerRadius); // Close path back to start
        ctx.closePath();
        
        if (fillColor) {
            ctx.fillStyle = fillColor;
            ctx.fill();
        }
        if (strokeColor) {
            ctx.strokeStyle = strokeColor;
            ctx.lineWidth = lineWidth;
            ctx.stroke();
        }
    }

    await loadDefaultFont(); // Ensure 'Bangers' font (or specified default) is loaded

    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d');

    // --- Layout Calculations ---
    const cardMinWidth = 320; // Minimum width for the packaging card
    const cardMinHeight = 480; // Minimum height for the packaging card
    
    let canvasWidth = Math.max(cardMinWidth, originalImg.width * 1.4);
     // Ensure proportion for typical figure packaging if image is very narrow
    if (originalImg.width * 1.8 < cardMinWidth) canvasWidth = cardMinWidth * 1.1;


    let canvasHeight = canvasWidth * 1.6; // Aspect ratio similar to 6x9 inch packaging
    if (canvasHeight < cardMinHeight) {
        canvasHeight = cardMinHeight;
        canvasWidth = Math.max(cardMinWidth, canvasHeight / 1.6); 
    }
    
    // Cap maximum dimensions to prevent overly large canvases from huge images
    const maxCanvasDimension = 2000; 
    if (canvasWidth > maxCanvasDimension || canvasHeight > maxCanvasDimension) {
        const currentRatio = canvasWidth / canvasHeight;
        if (canvasWidth > canvasHeight) {
            canvasWidth = maxCanvasDimension;
            canvasHeight = maxCanvasDimension / currentRatio;
        } else {
            canvasHeight = maxCanvasDimension;
            canvasWidth = maxCanvasDimension * currentRatio;
        }
    }

    canvas.width = Math.round(canvasWidth);
    canvas.height = Math.round(canvasHeight);

    const cardEdgePadding = canvasWidth * 0.04;

    const headerAreaHeight = canvasHeight * 0.20;
    const blisterDisplayAreaY = headerAreaHeight;
    const blisterDisplayAreaHeight = canvasHeight * 0.65;
    const footerAreaHeight = canvasHeight * 0.15;

    const imgAspectRatio = originalImg.width / originalImg.height;
    const maxImgDisplayWidth = canvasWidth - 2 * cardEdgePadding - (canvasWidth * 0.05); // Space for blister plastic edges
    const maxImgDisplayHeight = blisterDisplayAreaHeight - 2 * cardEdgePadding - (canvasWidth * 0.05);

    let scaledImgWidth, scaledImgHeight;
    if ((maxImgDisplayWidth / imgAspectRatio) <= maxImgDisplayHeight) {
        scaledImgWidth = maxImgDisplayWidth;
        scaledImgHeight = scaledImgWidth / imgAspectRatio;
    } else {
        scaledImgHeight = maxImgDisplayHeight;
        scaledImgWidth = scaledImgHeight * imgAspectRatio;
    }

    const imgDisplayX = (canvasWidth - scaledImgWidth) / 2;
    const imgDisplayY = blisterDisplayAreaY + (blisterDisplayAreaHeight - scaledImgHeight) / 2;

    const blisterEdgeWidth = Math.max(15, canvasWidth * 0.03); // How much blister plastic extends around image
    const blisterX = imgDisplayX - blisterEdgeWidth;
    const blisterY = imgDisplayY - blisterEdgeWidth;
    const blisterWidth = scaledImgWidth + 2 * blisterEdgeWidth;
    const blisterHeight = scaledImgHeight + 2 * blisterEdgeWidth;
    const blisterCornerRadius = Math.max(10, canvasWidth * 0.05);

    // --- Drawing Operations ---

    // 1. Cardboard Background
    ctx.fillStyle = cardBaseColor;
    ctx.fillRect(0, 0, canvasWidth, canvasHeight);
    ctx.strokeStyle = "rgba(0,0,0,0.15)"; // Subtle edge for the card itself
    ctx.lineWidth = 1;
    ctx.strokeRect(0.5, 0.5, canvasWidth - 1, canvasHeight - 1);


    // 2. Peg Hook Hole (simulating a punched-out hole)
    const hookHoleWidth = canvasWidth * 0.18;
    const hookHoleHeight = headerAreaHeight * 0.22;
    const hookHoleX = (canvasWidth - hookHoleWidth) / 2;
    const hookHoleY = headerAreaHeight * 0.12; // Positioned in upper part of header
    const hookHoleCornerRadius = hookHoleHeight / 3;
    ctx.fillStyle = "rgba(0,0,0,0.08)"; // Darker shade to imply depth
    roundRect(ctx, hookHoleX, hookHoleY, hookHoleWidth, hookHoleHeight, hookHoleCornerRadius, true, false);
    ctx.strokeStyle = "rgba(0,0,0,0.15)";
    ctx.lineWidth = 1;
    roundRect(ctx, hookHoleX, hookHoleY, hookHoleWidth, hookHoleHeight, hookHoleCornerRadius, false, true);


    // 3. Brand Name Text
    let brandFontSize = canvasHeight * 0.032;
    ctx.font = `italic bold ${brandFontSize}px "${fontFamily}", sans-serif`;
    ctx.fillStyle = cardAccentColor;
    ctx.textAlign = 'center';
    ctx.textBaseline = 'middle';
    // Basic check to shrink font if brandName is too wide
    while (ctx.measureText(brandName).width > canvasWidth * 0.7 && brandFontSize > 10) {
        brandFontSize -= 1;
        ctx.font = `italic bold ${brandFontSize}px "${fontFamily}", sans-serif`;
    }
    ctx.fillText(brandName, canvasWidth / 2, headerAreaHeight * 0.45);


    // 4. Main Title Text
    let titleFontSize = canvasHeight * 0.065;
    ctx.font = `bold ${titleFontSize}px "${fontFamily}", sans-serif`;
    const maxTitleWidth = canvasWidth * 0.90;
    
    const titleTextMetrics = ctx.measureText(titleText);
    let titleLines = [titleText];

    if (titleTextMetrics.width > maxTitleWidth) {
        // Attempt to split into two lines if a space exists appropriately
        let potentialSplitPoint = -1;
        const words = titleText.split(' ');
        if (words.length > 1) {
            let currentLine = "";
            for(let i=0; i<words.length; i++){
                const testLine = currentLine + (currentLine ? " " : "") + words[i];
                if(ctx.measureText(testLine).width > maxTitleWidth && currentLine){
                    potentialSplitPoint = currentLine.length;
                    break;
                }
                currentLine = testLine;
            }
        }
        if (potentialSplitPoint > 0) {
             titleLines = [titleText.substring(0, potentialSplitPoint), titleText.substring(potentialSplitPoint + 1)];
        } else { // If no good split, or single very long word, shrink font
            while (ctx.measureText(titleText).width > maxTitleWidth && titleFontSize > 15) {
                 titleFontSize -=1;
                 ctx.font = `bold ${titleFontSize}px "${fontFamily}", sans-serif`;
            }
            titleLines = [titleText];
        }
    }
    ctx.font = `bold ${titleFontSize}px "${fontFamily}", sans-serif`; // Set final font for drawing title

    ctx.fillStyle = cardAccentColor;
    ctx.strokeStyle = 'rgba(0,0,0,0.6)';
    ctx.lineWidth = Math.max(1, titleFontSize * 0.08);
    ctx.lineJoin = 'round';
    ctx.textAlign = 'center';
    
    const titleLineHeight = titleFontSize * (titleLines.length > 1 ? 0.9 : 1.1); // Tighter spacing for multi-line
    const totalTitleHeight = titleLineHeight * titleLines.length;
    const titleStartY = headerAreaHeight * 0.82 - (totalTitleHeight / 2) + (titleLineHeight/2) - (titleLines.length > 1 ? titleFontSize*0.1 : 0);

    titleLines.forEach((line, index) => {
        const lineY = titleStartY + index * titleLineHeight;
        ctx.strokeText(line, canvasWidth / 2, lineY);
        ctx.fillText(line, canvasWidth / 2, lineY);
    });


    // 5. Age Rating Text
    let ageFontSize = canvasHeight * 0.028;
    ctx.font = `bold ${ageFontSize}px "${fontFamily}", sans-serif`;
    ctx.fillStyle = "#FFFFFF"; 
    ctx.strokeStyle = "rgba(0,0,0,0.7)";
    ctx.lineWidth = Math.max(1, ageFontSize * 0.1);
    ctx.textAlign = 'right';
    ctx.textBaseline = 'alphabetic'; // Common baseline for text at bottom
    const ageRatingX = canvasWidth - cardEdgePadding;
    const ageRatingY = canvasHeight - footerAreaHeight * 0.25;
    ctx.strokeText(ageRating, ageRatingX, ageRatingY);
    ctx.fillText(ageRating, ageRatingX, ageRatingY);


    // 6. Draw the Original Image (as the "toy")
    ctx.drawImage(originalImg, imgDisplayX, imgDisplayY, scaledImgWidth, scaledImgHeight);


    // 7. Blister Plastic Bubble Effect
    // Base semi-transparent fill for the bubble shape
    ctx.fillStyle = 'rgba(230, 230, 255, 0.1)'; // A very light, cool, transparent fill
    roundRect(ctx, blisterX, blisterY, blisterWidth, blisterHeight, blisterCornerRadius, true, false);

    // "Sealed" edge effect for the blister plastic
    ctx.strokeStyle = 'rgba(180, 180, 200, 0.3)'; // Color for the main sealed edge
    ctx.lineWidth = Math.max(2, blisterEdgeWidth * 0.3); // Width of the seal
    roundRect(ctx, blisterX, blisterY, blisterWidth, blisterHeight, blisterCornerRadius, false, true);

    // Subtle highlight on the inner part of the seal
    ctx.strokeStyle = 'rgba(255, 255, 255, 0.4)';
    ctx.lineWidth = Math.max(1, blisterEdgeWidth * 0.08); // Thin highlight line
    const inset = ctx.lineWidth * 2;
    roundRect(ctx, 
        blisterX + inset, blisterY + inset,
        blisterWidth - 2 * inset, blisterHeight - 2 * inset, 
        Math.max(5, blisterCornerRadius - inset), 
        false, true);
    

    // 8. "NEW!" or Feature Burst (if text is provided)
    if (newFeatureText && newFeatureText.trim() !== "") {
        const numSpikes = 16;
        const outerBurstRadius = canvasWidth * 0.075;
        const innerBurstRadius = outerBurstRadius * 0.60;
        
        // Position burst, e.g., top-left of blister area or card corner
        const burstCenterX = blisterX + outerBurstRadius * 0.8; 
        const burstCenterY = blisterY + outerBurstRadius * 0.8;

        drawStar(ctx, burstCenterX, burstCenterY, numSpikes, outerBurstRadius, innerBurstRadius, newFeatureBurstColor, 'rgba(0,0,0,0.25)', 2);

        // Text inside the burst
        let burstTextFontSize = outerBurstRadius * 0.40;
        // Adjust font size roughly based on text length to prevent overflow
        const textLengthFactor = Math.max(1, newFeatureText.length / 4); // Crude factor
        burstTextFontSize /= textLengthFactor;
        burstTextFontSize = Math.max(8, burstTextFontSize); // Minimum font size for readability

        ctx.font = `bold ${burstTextFontSize}px "${fontFamily}", sans-serif`;
        ctx.fillStyle = newFeatureTextColor;
        ctx.textAlign = 'center';
        ctx.textBaseline = 'middle';
        
        ctx.save(); // Save context for rotation
        ctx.translate(burstCenterX, burstCenterY);
        ctx.rotate(-12 * Math.PI / 180); // Slight tilt for dynamism
        
        ctx.strokeStyle = 'rgba(0,0,0,0.5)'; // Stroke for better text visibility
        ctx.lineWidth = Math.max(1, burstTextFontSize * 0.08);
        ctx.strokeText(newFeatureText, 0, 0);
        ctx.fillText(newFeatureText, 0, 0);
        
        ctx.restore(); // Restore context after rotation
    }
    
    return canvas;
}

Free Image Tool Creator

Can't find the image tool you're looking for?
Create one based on your own needs now!

Description

The ‘Image of Real Life Action Figure Toy in Package’ tool allows users to create a visually appealing packaging design for an action figure toy. Users can upload an image of the toy, and the tool will generate a mock packaging card that includes customizable elements such as the toy’s title, brand name, age rating, and special feature highlights. This tool is ideal for toy designers, collectors, or marketers who want to showcase action figures in a professional packaging format, complete with vibrant colors and appealing typography.

Leave a Reply

Your email address will not be published. Required fields are marked *