Generator Public

Code #4957

Virtual Traditional Clothing Try-On (Client-Side)

This web application allows users to upload their photo and overlay various traditional clothing items. It features client-side image display, clothing selection, and manual positioning/resizing, simulating a virtual try-on experience.

Code
<!-- index.html -->
<!DOCTYPE html>
<html lang='en'>
<head>
    <meta charset='UTF-8'>
    <meta name='viewport' content='width=device-width, initial-scale=1.0'>
    <title>Traditional Clothing Try-On</title>
    <link rel='stylesheet' href='style.css'>
</head>
<body>
    <div class='container'>
        <h1>Virtual Traditional Clothing Try-On</h1>
        <p>Upload your photo and try on traditional clothes from around the world!</p>

        <!-- User Photo Upload Section -->
        <div class='upload-section'>
            <label for='userImageInput' class='button'>Upload Your Photo</label>
            <input type='file' id='userImageInput' accept='image/*' style='display: none;'>
            <div class='image-preview-area'>
                <img id='userPhotoPreview' src='' alt='Your Photo' class='hidden'>
                <p id='uploadPlaceholder'>Upload your photo here</p>
            </div>
        </div>

        <!-- Clothing Selection Section -->
        <div class='clothing-selection'>
            <h2>Select Traditional Clothing</h2>
            <div class='clothing-options' id='clothingOptions'>
                <!-- Example clothing items; replace with your own assets or dynamic loading -->
                <img src='https://via.placeholder.com/100x100?text=Kimono' data-clothing='https://upload.wikimedia.org/wikipedia/commons/thumb/1/12/Japanese_kimono_robe.jpg/250px-Japanese_kimono_robe.jpg' alt='Kimono' class='clothing-thumbnail'>
                <img src='https://via.placeholder.com/100x100?text=Sari' data-clothing='https://upload.wikimedia.org/wikipedia/commons/thumb/c/c5/Woman_in_Sari.jpg/220px-Woman_in_Sari.jpg' alt='Sari' class='clothing-thumbnail'>
                <img src='https://via.placeholder.com/100x100?text=Kilt' data-clothing='https://upload.wikimedia.org/wikipedia/commons/thumb/b/b5/Man_in_Kilt.jpg/220px-Man_in_Kilt.jpg' alt='Kilt' class='clothing-thumbnail'>
                <img src='https://via.placeholder.com/100x100?text=Hanbok' data-clothing='https://upload.wikimedia.org/wikipedia/commons/thumb/6/64/Korean_Hanbok_dress_2.jpg/220px-Korean_Hanbok_dress_2.jpg' alt='Hanbok' class='clothing-thumbnail'>
                <img src='https://via.placeholder.com/100x100?text=Dashiki' data-clothing='https://upload.wikimedia.org/wikipedia/commons/thumb/d/d7/Dashiki_Kente.jpg/220px-Dashiki_Kente.jpg' alt='Dashiki' class='clothing-thumbnail'>
            </div>
        </div>

        <!-- Try-On Area -->
        <div class='tryon-area'>
            <h2>Your Virtual Try-On</h2>
            <div id='canvasContainer' class='canvas-container'>
                <img id='baseImage' src='' alt='Your Base Photo' class='hidden'>
                <img id='overlayClothing' src='' alt='Overlay Clothing' class='hidden draggable resizable rotatable'>
                <p id='tryonPlaceholder'>Upload a photo and select clothing to begin!</p>
            </div>
            <div class='controls'>
                <button id='resetButton' class='button hidden'>Reset Overlay</button>
                <label for='scaleSlider' class='hidden'>Scale:</label>
                <input type='range' id='scaleSlider' min='0.1' max='3' step='0.1' value='1' class='hidden'>
                <label for='rotationSlider' class='hidden'>Rotate:</label>
                <input type='range' id='rotationSlider' min='0' max='360' step='1' value='0' class='hidden'>
            </div>
        </div>
    </div>

    <script src='script.js'></script>
</body>
</html>

/* style.css */
body {
    font-family: Arial, sans-serif;
    display: flex;
    justify-content: center;
    align-items: flex-start;
    min-height: 100vh;
    margin: 0;
    background-color: #f4f7f6;
    color: #333;
    padding: 20px;
    box-sizing: border-box;
}

.container {
    background-color: #fff;
    padding: 30px;
    border-radius: 10px;
    box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
    max-width: 900px;
    width: 100%;
    text-align: center;
}

h1, h2 {
    color: #0056b3;
}

p {
    margin-bottom: 20px;
    color: #555;
}

.button {
    display: inline-block;
    background-color: #007bff;
    color: white;
    padding: 10px 20px;
    border: none;
    border-radius: 5px;
    cursor: pointer;
    font-size: 16px;
    text-decoration: none;
    transition: background-color 0.3s ease;
}

.button:hover {
    background-color: #0056b3;
}

.upload-section, .clothing-selection, .tryon-area {
    margin-top: 30px;
    padding: 20px;
    border: 1px dashed #ccc;
    border-radius: 8px;
    background-color: #fafafa;
}

.image-preview-area {
    width: 100%;
    max-width: 300px;
    height: 200px;
    margin: 20px auto;
    border: 1px solid #ddd;
    display: flex;
    justify-content: center;
    align-items: center;
    overflow: hidden;
    background-color: #e9ecef;
    border-radius: 5px;
}

#userPhotoPreview {
    max-width: 100%;
    max-height: 100%;
    object-fit: contain;
}

.clothing-options {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 15px;
    margin-top: 20px;
}

.clothing-thumbnail {
    width: 100px;
    height: 100px;
    object-fit: cover;
    border: 2px solid #eee;
    border-radius: 5px;
    cursor: pointer;
    transition: transform 0.2s, border-color 0.2s;
}

.clothing-thumbnail:hover {
    transform: translateY(-3px);
    border-color: #007bff;
}

.clothing-thumbnail.selected {
    border-color: #007bff;
    box-shadow: 0 0 8px rgba(0, 123, 255, 0.5);
}

.canvas-container {
    position: relative;
    width: 400px;
    height: 400px;
    margin: 20px auto;
    border: 2px solid #007bff;
    overflow: hidden;
    background-color: #e9ecef;
    display: flex;
    justify-content: center;
    align-items: center;
    border-radius: 5px;
}

#baseImage {
    position: absolute;
    max-width: 100%;
    max-height: 100%;
    object-fit: contain;
    z-index: 1;
}

#overlayClothing {
    position: absolute;
    cursor: grab;
    z-index: 2;
    /* Initial transformation, will be overridden by JS */
    transform: translate(-50%, -50%) scale(1) rotate(0deg);
    transform-origin: top left;
    left: 50%; /* Center initially */
    top: 50%;  /* Center initially */
}

#overlayClothing.hidden, #userPhotoPreview.hidden, #baseImage.hidden,
#uploadPlaceholder.hidden, #tryonPlaceholder.hidden,
.button.hidden, input[type='range'].hidden, .controls label.hidden {
    display: none;
}

.controls {
    margin-top: 20px;
    display: flex;
    justify-content: center;
    align-items: center;
    gap: 15px;
    flex-wrap: wrap;
}

.controls label {
    font-weight: bold;
    color: #555;
}

input[type='range'] {
    width: 150px;
    -webkit-appearance: none;
    height: 8px;
    background: #d3d3d3;
    outline: none;
    opacity: 0.7;
    -webkit-transition: .2s;
    transition: opacity .2s;
    border-radius: 5px;
}

input[type='range']::-webkit-slider-thumb {
    -webkit-appearance: none;
    appearance: none;
    width: 20px;
    height: 20px;
    border-radius: 50%;
    background: #007bff;
    cursor: pointer;
}

input[type='range']::-moz-range-thumb {
    width: 20px;
    height: 20px;
    border-radius: 50%;
    background: #007bff;
    cursor: pointer;
}

/* script.js */

// --- DOM Elements --- //
const userImageInput = document.getElementById('userImageInput');
const userPhotoPreview = document.getElementById('userPhotoPreview');
const uploadPlaceholder = document.getElementById('uploadPlaceholder');
const clothingOptions = document.getElementById('clothingOptions');
const tryonPlaceholder = document.getElementById('tryonPlaceholder');
const canvasContainer = document.getElementById('canvasContainer');
const baseImage = document.getElementById('baseImage');
const overlayClothing = document.getElementById('overlayClothing');
const resetButton = document.getElementById('resetButton');
const scaleSlider = document.getElementById('scaleSlider');
const rotationSlider = document.getElementById('rotationSlider');
const scaleLabel = document.querySelector('label[for="scaleSlider"]');
const rotationLabel = document.querySelector('label[for="rotationSlider"]');

// --- Global State Variables --- //
// These variables store the current transformation state of the overlay clothing.
let currentScale = 1;
let currentRotation = 0;
// currentX and currentY represent the top-left corner's offset from the canvasContainer's top-left.
let currentX = 0;
let currentY = 0;
let isDragging = false;
let startX, startY; // Stores initial mouse click position relative to the element being dragged.

// --- Event Listeners --- //

/**
 * Handles the user's photo upload.
 * Reads the selected file and displays it in the preview area and as the base image for try-on.
 */
userImageInput.addEventListener('change', (event) => {
    const file = event.target.files[0];
    if (file) {
        const reader = new FileReader();
        reader.onload = (e) => {
            // Update photo preview in the upload section
            userPhotoPreview.src = e.target.result;
            userPhotoPreview.classList.remove('hidden');
            uploadPlaceholder.classList.add('hidden');

            // Set the base image for the try-on area
            baseImage.src = e.target.result;
            baseImage.classList.remove('hidden');
            tryonPlaceholder.classList.add('hidden');
            resetOverlay(); // Reset overlay position when a new base image is loaded
        };
        reader.readAsDataURL(file);
    }
});

/**
 * Handles the selection of a traditional clothing item.
 * Updates the overlay clothing image and enables controls if a base image is present.
 */
clothingOptions.addEventListener('click', (event) => {
    if (event.target.classList.contains('clothing-thumbnail')) {
        // Remove 'selected' class from previously selected item
        document.querySelectorAll('.clothing-thumbnail').forEach(img => {
            img.classList.remove('selected');
        });

        // Add 'selected' class to the clicked item
        event.target.classList.add('selected');

        const clothingSrc = event.target.dataset.clothing;
        if (baseImage.src) {
            // Update the overlay clothing image
            overlayClothing.src = clothingSrc;
            overlayClothing.classList.remove('hidden');

            // Show controls for manipulating the overlay
            resetButton.classList.remove('hidden');
            scaleSlider.classList.remove('hidden');
            rotationSlider.classList.remove('hidden');
            scaleLabel.classList.remove('hidden');
            rotationLabel.classList.remove('hidden');
            resetOverlay(); // Reset position and scale for the new clothing item
        } else {
            alert('Please upload your photo first!');
        }
    }
});

/**
 * Initiates dragging of the overlay clothing image on mouse down.
 * Calculates the initial offset to prevent the image from 'jumping'.
 */
overlayClothing.addEventListener('mousedown', (e) => {
    if (e.button === 0) { // Only respond to left mouse button
        isDragging = true;
        overlayClothing.style.cursor = 'grabbing';

        // Store the mouse position relative to the overlay image's current top-left corner.
        // We calculate this based on the element's actual position relative to the document
        // and the mouse's document position.
        const rect = overlayClothing.getBoundingClientRect();
        startX = e.clientX - rect.left;
        startY = e.clientY - rect.top;

        e.preventDefault(); // Prevent default browser drag behavior (e.g., image ghosting)
    }
});

/**
 * Updates the overlay clothing's position during a drag operation.
 * Calculates new currentX and currentY based on mouse movement.
 */
document.addEventListener('mousemove', (e) => {
    if (isDragging) {
        // Get the canvas container's position to correctly offset the overlay
        const containerRect = canvasContainer.getBoundingClientRect();

        // Calculate new top-left position of the overlay relative to the container
        currentX = e.clientX - containerRect.left - startX;
        currentY = e.clientY - containerRect.top - startY;

        applyTransform(); // Apply the new transform
    }
});

/**
 * Ends the drag operation on mouse up.
 */
document.addEventListener('mouseup', () => {
    if (isDragging) {
        isDragging = false;
        overlayClothing.style.cursor = 'grab';
    }
});

/**
 * Handles scaling the overlay clothing via the scale slider.
 */
scaleSlider.addEventListener('input', (event) => {
    currentScale = parseFloat(event.target.value);
    applyTransform();
});

/**
 * Handles rotating the overlay clothing via the rotation slider.
 */
rotationSlider.addEventListener('input', (event) => {
    currentRotation = parseFloat(event.target.value);
    applyTransform();
});

/**
 * Handles resetting the overlay clothing's position, scale, and rotation.
 */
resetButton.addEventListener('click', resetOverlay);

// --- Functions --- //

/**
 * Applies the current transformation (position, scale, rotation) to the overlay clothing image.
 * Uses CSS `transform` property for smooth and efficient manipulation.
 */
function applyTransform() {
    // The transform-origin is set to top left (0,0) in CSS for simplicity.
    // currentX and currentY define the pixel offset of the image's top-left corner
    // from the canvasContainer's top-left corner.
    overlayClothing.style.transform = `translate(${currentX}px, ${currentY}px) scale(${currentScale}) rotate(${currentRotation}deg)`;
}

/**
 * Resets the position, scale, and rotation of the overlay clothing image to its initial state.
 * This places the clothing image at the top-left of the container with default scale and no rotation.
 * Sliders are also reset to their default values.
 */
function resetOverlay() {
    currentScale = 1;
    currentRotation = 0;
    currentX = 0; // Reset to top-left of the canvas container
    currentY = 0;

    scaleSlider.value = currentScale;
    rotationSlider.value = currentRotation;
    applyTransform();
}

// --- Initial Setup --- //
// Apply initial transforms to ensure sliders reflect the state and overlay is positioned.
resetOverlay();

/**
 * MODERATION NOTE:
 * This application provides a client-side interface for users to manually overlay
 * clothing items onto their uploaded photos. It is NOT an AI-driven virtual try-on system.
 * The core logic for 'making the user wear' clothing (i.e., automatically fitting,
 * adjusting perspective, handling occlusions, or generating new images with clothing)
 * would typically involve advanced server-side image processing, computer vision (e.g., OpenCV),
 * or machine learning models (e.g., Generative Adversarial Networks - GANs, or diffusion models).
 *
 * To implement automated try-on, you would extend this application as follows:
 * 1.  Backend API: Create a server-side API (e.g., with Python/Flask/Django, Node.js/Express)
 *     that receives the user's photo and the selected clothing image (or its ID).
 * 2.  Image Processing/AI: Integrate a library or model in the backend to perform tasks like:
 *     a.  Human pose/landmark detection in the user's photo.
 *     b.  Segmentation of the user's body from the background.
 *     c.  Warping, resizing, and superimposing the clothing image onto the detected body region,
 *         adjusting for perspective and body shape.
 *     d.  Handling layering and potential occlusions (e.g., if the user's arm should be over the clothing).
 *     e.  Potentially using AI to generate a more realistic merged image.
 * 3.  Frontend Integration: The frontend would then send the user's photo and selected clothing
 *     to this backend API and display the processed image returned by the server, replacing the manual overlay system.
 *
 * This current code provides the interactive frontend scaffolding required for such a system,
 * focusing on user interaction and manual visual placement for demonstration purposes.
 */
Prompt: web app that able to make the user wear any worldwide traditional clothing