Please bookmark this page to avoid losing your image tool!

Squirrel Image Viewer

(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.
function processImage(originalImg, bgColor = "#2d2d2d", overlayText = "🐿️ Белка Image Viewer") {
    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d');

    // Set maximum dimensions for the viewer
    const MAX_WIDTH = 1024;
    const MAX_HEIGHT = 800;

    // Calculate canvas size based on original image, capped at max dimensions
    canvas.width = Math.min(originalImg.width, MAX_WIDTH);
    canvas.height = Math.min(originalImg.height, MAX_HEIGHT);

    // Ensure the initial view fits the entire image neatly within the canvas
    const initialScale = Math.min(canvas.width / originalImg.width, canvas.height / originalImg.height);
    let scale = initialScale;
    let posX = (canvas.width - originalImg.width * scale) / 2;
    let posY = (canvas.height - originalImg.height * scale) / 2;

    let isDragging = false;
    let startX = 0;
    let startY = 0;

    function draw() {
        // Clear and draw background
        ctx.fillStyle = bgColor;
        ctx.fillRect(0, 0, canvas.width, canvas.height);

        // Save context state, apply panning and zooming, draw image
        ctx.save();
        ctx.translate(posX, posY);
        ctx.scale(scale, scale);
        // Smoothing for when it's zoomed in or out
        ctx.imageSmoothingEnabled = true;
        ctx.imageSmoothingQuality = 'high';
        ctx.drawImage(originalImg, 0, 0);
        ctx.restore();

        // Draw Viewer UI Overlay
        ctx.fillStyle = "rgba(0, 0, 0, 0.6)";
        ctx.fillRect(0, 0, canvas.width, 60);

        ctx.fillStyle = "#ffffff";
        ctx.font = "bold 22px 'Segoe UI', Arial, sans-serif";
        ctx.fillText(overlayText, 15, 30);
        
        ctx.fillStyle = "#cccccc";
        ctx.font = "14px 'Segoe UI', Arial, sans-serif";
        ctx.fillText("Scroll to zoom • Click & Drag to pan • Double-click to reset", 15, 50);
        
        // Draw zoom percentage in top right
        const zoomPercent = Math.round((scale / initialScale) * 100) + "%";
        ctx.textAlign = "right";
        ctx.fillStyle = "#ffffff";
        ctx.font = "bold 16px 'Segoe UI', Arial, sans-serif";
        ctx.fillText(zoomPercent, canvas.width - 20, 35);
        ctx.textAlign = "left"; // reset alignment
    }

    // Zooming functionality using mouse wheel
    canvas.addEventListener('wheel', (e) => {
        e.preventDefault();
        const zoomIntensity = 0.1;
        const wheel = e.deltaY < 0 ? 1 : -1;
        
        // Calculate the zoom factor
        const zoomFactor = Math.exp(wheel * zoomIntensity);

        // Get mouse coordinates relative to the canvas
        const rect = canvas.getBoundingClientRect();
        const mouseX = e.clientX - rect.left;
        const mouseY = e.clientY - rect.top;

        // Adjust position so the zoom centers on the mouse cursor
        posX = mouseX - (mouseX - posX) * zoomFactor;
        posY = mouseY - (mouseY - posY) * zoomFactor;
        scale *= zoomFactor;

        // Prevent zooming out too far or zooming in too much
        const minScale = initialScale * 0.1;
        const maxScale = initialScale * 20;
        if (scale < minScale) {
            scale = minScale;
            // Recalculate position adjustment limit when hit boundary
            posX = mouseX - (mouseX - posX) * (minScale / (scale / zoomFactor));
            posY = mouseY - (mouseY - posY) * (minScale / (scale / zoomFactor));
        } else if (scale > maxScale) {
            scale = maxScale;
            posX = mouseX - (mouseX - posX) * (maxScale / (scale / zoomFactor));
            posY = mouseY - (mouseY - posY) * (maxScale / (scale / zoomFactor));
        }

        requestAnimationFrame(draw);
    }, { passive: false }); // passive: false required to allow e.preventDefault()

    // Panning functionality
    canvas.addEventListener('mousedown', (e) => {
        isDragging = true;
        
        // Get start coordinates relative to current pan position
        const rect = canvas.getBoundingClientRect();
        startX = (e.clientX - rect.left) - posX;
        startY = (e.clientY - rect.top) - posY;
        canvas.style.cursor = 'grabbing';
    });

    canvas.addEventListener('mousemove', (e) => {
        if (!isDragging) return;
        
        // Update pan position based on mouse movement
        const rect = canvas.getBoundingClientRect();
        posX = (e.clientX - rect.left) - startX;
        posY = (e.clientY - rect.top) - startY;
        
        requestAnimationFrame(draw);
    });

    const stopDragging = () => {
        isDragging = false;
        canvas.style.cursor = 'grab';
    };

    canvas.addEventListener('mouseup', stopDragging);
    canvas.addEventListener('mouseleave', stopDragging);
    
    // Reset view on double click
    canvas.addEventListener('dblclick', () => {
        scale = initialScale;
        posX = (canvas.width - originalImg.width * scale) / 2;
        posY = (canvas.height - originalImg.height * scale) / 2;
        requestAnimationFrame(draw);
    });

    // Set initial cursor style
    canvas.style.cursor = 'grab';
    
    // Disable text selection during drag interactions
    canvas.style.userSelect = 'none';

    // Perform initial draw
    draw();

    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 Squirrel Image Viewer is an interactive tool designed for detailed image inspection. It provides a user-friendly interface that allows you to zoom in and out using your mouse wheel, pan around the image by clicking and dragging, and quickly reset your view with a double-click. This tool is ideal for tasks that require close examination of image details, such as reviewing high-resolution photography, inspecting digital artwork, or checking fine textures and small elements within a picture.

Leave a Reply

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