haircut-advisor / index.html
Rogerjs's picture
Add 3 files
df67c6a verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Haircut Advisor - Find Your Perfect Style</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
.face-outline {
position: relative;
width: 300px;
height: 400px;
border-radius: 50% 50% 50% 50% / 60% 60% 40% 40%;
background: #f3e7d3;
margin: 0 auto;
box-shadow: 0 10px 25px rgba(0,0,0,0.2);
}
.face-landmark {
position: absolute;
background: rgba(255,0,0,0.3);
border-radius: 50%;
}
.face-shape-indicator {
position: absolute;
border: 2px dashed #4f46e5;
border-radius: 50%;
}
.hair-preview {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
z-index: 10;
pointer-events: none;
}
.result-card {
transition: all 0.3s ease;
}
.result-card:hover {
transform: translateY(-5px);
box-shadow: 0 15px 30px rgba(0,0,0,0.15);
}
#previewCanvas {
display: none;
}
.upload-area {
border: 2px dashed #cbd5e1;
transition: all 0.3s ease;
}
.upload-area:hover {
border-color: #818cf8;
background-color: #f8fafc;
}
.upload-area.dragover {
border-color: #4f46e5;
background-color: #eef2ff;
}
</style>
</head>
<body class="bg-gray-50 min-h-screen">
<div class="container mx-auto px-4 py-12">
<!-- Header -->
<header class="text-center mb-12">
<h1 class="text-4xl md:text-5xl font-bold text-indigo-800 mb-3">Haircut Advisor</h1>
<p class="text-lg text-gray-600 max-w-2xl mx-auto">Upload your photo and discover the perfect hairstyle that complements your facial features and proportions.</p>
</header>
<!-- Main Content -->
<div class="flex flex-col lg:flex-row gap-8">
<!-- Upload Section -->
<div class="lg:w-1/2 bg-white rounded-xl shadow-md p-6">
<h2 class="text-2xl font-semibold text-gray-800 mb-6">Analyze Your Face</h2>
<div id="uploadArea" class="upload-area rounded-lg p-8 text-center cursor-pointer mb-6">
<div class="flex flex-col items-center justify-center">
<i class="fas fa-cloud-upload-alt text-4xl text-indigo-500 mb-3"></i>
<p class="text-gray-600 mb-2">Drag & drop your photo here</p>
<p class="text-sm text-gray-500 mb-4">or</p>
<label for="fileInput" class="bg-indigo-600 hover:bg-indigo-700 text-white font-medium py-2 px-6 rounded-full cursor-pointer transition">
Browse Files
</label>
<input id="fileInput" type="file" accept="image/*" class="hidden">
</div>
</div>
<div id="facePreview" class="hidden">
<div class="flex flex-col md:flex-row gap-6 items-center">
<div class="relative">
<div class="face-outline">
<canvas id="previewCanvas" width="300" height="400"></canvas>
<div id="faceLandmarks" class="hair-preview"></div>
</div>
</div>
<div class="flex-1">
<h3 class="text-xl font-medium text-gray-800 mb-3">Facial Analysis</h3>
<div id="faceStats" class="space-y-3">
<!-- Will be populated by JS -->
</div>
</div>
</div>
<button id="analyzeBtn" class="mt-6 w-full bg-indigo-600 hover:bg-indigo-700 text-white font-medium py-3 px-6 rounded-lg transition">
<i class="fas fa-magic mr-2"></i> Suggest Hairstyles
</button>
</div>
</div>
<!-- Results Section -->
<div class="lg:w-1/2">
<div id="resultsContainer" class="hidden bg-white rounded-xl shadow-md p-6">
<h2 class="text-2xl font-semibold text-gray-800 mb-6">Recommended Hairstyles</h2>
<div id="faceShapeInfo" class="mb-6 p-4 bg-indigo-50 rounded-lg">
<h3 class="text-lg font-medium text-indigo-800 mb-2">Your Face Shape: <span id="faceShapeLabel" class="font-bold">Oval</span></h3>
<p id="faceShapeDesc" class="text-gray-700">Based on your facial proportions, these hairstyles will best complement your features.</p>
</div>
<div id="resultsGrid" class="grid grid-cols-1 md:grid-cols-2 gap-4">
<!-- Will be populated by JS -->
</div>
</div>
<div id="instructions" class="bg-white rounded-xl shadow-md p-6">
<h2 class="text-2xl font-semibold text-gray-800 mb-4">How It Works</h2>
<ol class="space-y-4">
<li class="flex items-start">
<span class="flex items-center justify-center bg-indigo-100 text-indigo-800 rounded-full w-6 h-6 mr-3 font-medium">1</span>
<span>Upload a clear front-facing photo with good lighting</span>
</li>
<li class="flex items-start">
<span class="flex items-center justify-center bg-indigo-100 text-indigo-800 rounded-full w-6 h-6 mr-3 font-medium">2</span>
<span>Our system analyzes your facial proportions and shape</span>
</li>
<li class="flex items-start">
<span class="flex items-center justify-center bg-indigo-100 text-indigo-800 rounded-full w-6 h-6 mr-3 font-medium">3</span>
<span>Get personalized hairstyle recommendations based on your features</span>
</li>
</ol>
<div class="mt-8 p-4 bg-yellow-50 rounded-lg border border-yellow-200">
<h3 class="text-lg font-medium text-yellow-800 mb-2"><i class="fas fa-lightbulb mr-2"></i> Pro Tip</h3>
<p class="text-yellow-700">For best results, remove glasses and pull hair back to clearly show your facial structure.</p>
</div>
</div>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
// DOM Elements
const fileInput = document.getElementById('fileInput');
const uploadArea = document.getElementById('uploadArea');
const facePreview = document.getElementById('facePreview');
const previewCanvas = document.getElementById('previewCanvas');
const faceLandmarks = document.getElementById('faceLandmarks');
const faceStats = document.getElementById('faceStats');
const analyzeBtn = document.getElementById('analyzeBtn');
const resultsContainer = document.getElementById('resultsContainer');
const resultsGrid = document.getElementById('resultsGrid');
const faceShapeLabel = document.getElementById('faceShapeLabel');
const faceShapeDesc = document.getElementById('faceShapeDesc');
const instructions = document.getElementById('instructions');
// Drag and drop functionality
uploadArea.addEventListener('dragover', (e) => {
e.preventDefault();
uploadArea.classList.add('dragover');
});
uploadArea.addEventListener('dragleave', () => {
uploadArea.classList.remove('dragover');
});
uploadArea.addEventListener('drop', (e) => {
e.preventDefault();
uploadArea.classList.remove('dragover');
if (e.dataTransfer.files.length) {
fileInput.files = e.dataTransfer.files;
handleFileUpload(e.dataTransfer.files[0]);
}
});
// File input change handler
fileInput.addEventListener('change', () => {
if (fileInput.files.length) {
handleFileUpload(fileInput.files[0]);
}
});
// Analyze button click handler
analyzeBtn.addEventListener('click', analyzeFaceAndSuggestHairstyles);
// Handle file upload
function handleFileUpload(file) {
if (!file.type.match('image.*')) {
alert('Please upload an image file.');
return;
}
const reader = new FileReader();
reader.onload = function(e) {
// Show preview
const img = new Image();
img.onload = function() {
const ctx = previewCanvas.getContext('2d');
// Calculate dimensions to fit in canvas while maintaining aspect ratio
const hRatio = previewCanvas.width / img.width;
const vRatio = previewCanvas.height / img.height;
const ratio = Math.min(hRatio, vRatio);
const centerShiftX = (previewCanvas.width - img.width * ratio) / 2;
const centerShiftY = (previewCanvas.height - img.height * ratio) / 2;
ctx.clearRect(0, 0, previewCanvas.width, previewCanvas.height);
ctx.drawImage(img, 0, 0, img.width, img.height,
centerShiftX, centerShiftY, img.width * ratio, img.height * ratio);
// Show the preview section
facePreview.classList.remove('hidden');
uploadArea.classList.add('hidden');
// Simulate face detection (in a real app, you'd use a face detection API)
simulateFaceDetection();
};
img.src = e.target.result;
};
reader.readAsDataURL(file);
}
// Simulate face detection (in a real app, replace with actual face detection)
function simulateFaceDetection() {
// Generate random facial proportions for demo purposes
const faceWidth = 0.7 + Math.random() * 0.1;
const faceLength = 1.0 + Math.random() * 0.2;
const foreheadHeight = 0.3 + Math.random() * 0.1;
const cheekboneWidth = 0.6 + Math.random() * 0.1;
const jawWidth = 0.5 + Math.random() * 0.15;
// Calculate ratios
const lengthToWidthRatio = faceLength / faceWidth;
const jawToCheekRatio = jawWidth / cheekboneWidth;
// Display facial analysis
faceStats.innerHTML = `
<div class="flex justify-between">
<span class="text-gray-600">Face Length/Width Ratio:</span>
<span class="font-medium">${lengthToWidthRatio.toFixed(2)}</span>
</div>
<div class="flex justify-between">
<span class="text-gray-600">Jaw/Cheekbone Ratio:</span>
<span class="font-medium">${jawToCheekRatio.toFixed(2)}</span>
</div>
<div class="flex justify-between">
<span class="text-gray-600">Forehead Height:</span>
<span class="font-medium">${(foreheadHeight * 100).toFixed(0)}%</span>
</div>
<div class="flex justify-between">
<span class="text-gray-600">Cheekbone Prominence:</span>
<span class="font-medium">${(cheekboneWidth * 100).toFixed(0)}%</span>
</div>
`;
// Add simulated face landmarks
faceLandmarks.innerHTML = '';
// Forehead
addLandmark(150, 80, 60, 30, 'Forehead');
// Eyes
addLandmark(110, 150, 40, 20, 'Left Eye');
addLandmark(190, 150, 40, 20, 'Right Eye');
// Nose
addLandmark(150, 200, 20, 40, 'Nose');
// Mouth
addLandmark(150, 250, 50, 15, 'Mouth');
// Jawline
addLandmark(100, 300, 20, 20, 'Jaw');
addLandmark(200, 300, 20, 20, 'Jaw');
// Face shape indicator
const shapeIndicator = document.createElement('div');
shapeIndicator.className = 'face-shape-indicator';
shapeIndicator.style.width = `${faceWidth * 200}px`;
shapeIndicator.style.height = `${faceLength * 300}px`;
shapeIndicator.style.left = `${150 - (faceWidth * 200)/2}px`;
shapeIndicator.style.top = `${100 - (faceLength * 300)/4}px`;
faceLandmarks.appendChild(shapeIndicator);
}
function addLandmark(left, top, width, height, label) {
const landmark = document.createElement('div');
landmark.className = 'face-landmark';
landmark.style.width = `${width}px`;
landmark.style.height = `${height}px`;
landmark.style.left = `${left - width/2}px`;
landmark.style.top = `${top - height/2}px`;
landmark.title = label;
faceLandmarks.appendChild(landmark);
}
// Analyze face and suggest hairstyles
function analyzeFaceAndSuggestHairstyles() {
// Show loading state
analyzeBtn.innerHTML = '<i class="fas fa-spinner fa-spin mr-2"></i> Analyzing...';
analyzeBtn.disabled = true;
// Simulate API call delay
setTimeout(() => {
// Determine face shape (simplified for demo)
const faceShapes = [
{ name: 'Oval', desc: 'Oval faces are well-balanced and versatile. Most hairstyles work well with this shape.' },
{ name: 'Round', desc: 'Round faces benefit from hairstyles that add height and length to create the illusion of a more oval shape.' },
{ name: 'Square', desc: 'Square faces look great with styles that soften the angular jawline.' },
{ name: 'Heart', desc: 'Heart-shaped faces suit styles that balance the wider forehead with the narrower chin.' },
{ name: 'Long', desc: 'Long faces benefit from styles that add width and volume to the sides.' }
];
const randomShape = faceShapes[Math.floor(Math.random() * faceShapes.length)];
faceShapeLabel.textContent = randomShape.name;
faceShapeDesc.textContent = randomShape.desc;
// Generate hairstyle suggestions
const hairstyles = getHairstyleSuggestions(randomShape.name);
// Display results
resultsGrid.innerHTML = '';
hairstyles.forEach(style => {
const card = document.createElement('div');
card.className = 'result-card bg-white rounded-lg overflow-hidden shadow-md border border-gray-100';
card.innerHTML = `
<div class="h-48 bg-gray-200 relative overflow-hidden">
<img src="${style.image}" alt="${style.name}" class="w-full h-full object-cover">
<div class="absolute bottom-0 left-0 right-0 bg-gradient-to-t from-black/70 to-transparent p-3">
<h3 class="text-white font-semibold">${style.name}</h3>
</div>
</div>
<div class="p-4">
<p class="text-gray-600 text-sm mb-3">${style.description}</p>
<div class="flex justify-between items-center">
<span class="text-xs px-2 py-1 rounded-full ${style.difficulty === 'Easy' ? 'bg-green-100 text-green-800' :
style.difficulty === 'Medium' ? 'bg-yellow-100 text-yellow-800' : 'bg-purple-100 text-purple-800'}">
${style.difficulty}
</span>
<button class="text-xs text-indigo-600 hover:text-indigo-800 font-medium">
<i class="far fa-save mr-1"></i> Save
</button>
</div>
</div>
`;
resultsGrid.appendChild(card);
});
// Show results
resultsContainer.classList.remove('hidden');
instructions.classList.add('hidden');
// Reset button
analyzeBtn.innerHTML = '<i class="fas fa-magic mr-2"></i> Suggest Hairstyles';
analyzeBtn.disabled = false;
}, 1500);
}
// Get hairstyle suggestions based on face shape
function getHairstyleSuggestions(faceShape) {
// Base hairstyles
const allHairstyles = [
{
name: "Classic Layers",
description: "Face-framing layers that add movement and texture while maintaining a polished look.",
difficulty: "Medium",
suits: ["Oval", "Round", "Heart"],
image: "https://images.unsplash.com/photo-1595476108010-b4d1f102b1b1?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=500&q=80"
},
{
name: "Blunt Bob",
description: "A sharp, clean-cut bob that emphasizes jawlines and works well with straight hair.",
difficulty: "Easy",
suits: ["Square", "Oval"],
image: "https://images.unsplash.com/photo-1534528741775-53994a69daeb?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=500&q=80"
},
{
name: "Long with Side Bangs",
description: "Soft side bangs that gracefully frame the face and draw attention to the eyes.",
difficulty: "Easy",
suits: ["Square", "Long", "Heart"],
image: "https://images.unsplash.com/photo-1529626455594-4ff0802cfb7e?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=500&q=80"
},
{
name: "Pixie Cut",
description: "A short, chic cut that highlights facial features and works well with delicate bone structure.",
difficulty: "Medium",
suits: ["Heart", "Oval", "Round"],
image: "https://images.unsplash.com/photo-1519699047748-de8e457a634e?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=500&q=80"
},
{
name: "Shag with Curtain Bangs",
description: "A textured shag cut with face-framing curtain bangs for a modern, effortless look.",
difficulty: "Medium",
suits: ["Oval", "Round", "Square"],
image: "https://images.unsplash.com/photo-1515886657613-9f3515b0c78f?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=500&q=80"
},
{
name: "Asymmetrical Lob",
description: "A longer bob cut with asymmetrical lines that add interest and balance to facial features.",
difficulty: "Hard",
suits: ["Square", "Heart", "Oval"],
image: "https://images.unsplash.com/photo-1544005313-94ddf0286df2?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=500&q=80"
},
{
name: "Voluminous Curls",
description: "Full, bouncy curls that add width and balance to longer face shapes.",
difficulty: "Medium",
suits: ["Long", "Oval", "Square"],
image: "https://images.unsplash.com/photo-1492106087820-71f1a00d2b11?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=500&q=80"
},
{
name: "Textured Crop",
description: "A short, textured cut with piece-y layers for a modern, edgy look.",
difficulty: "Hard",
suits: ["Round", "Oval", "Heart"],
image: "https://images.unsplash.com/photo-1517841905240-472988babdf9?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=500&q=80"
}
];
// Filter hairstyles that suit the face shape
return allHairstyles.filter(style => style.suits.includes(faceShape));
}
});
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=Rogerjs/haircut-advisor" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>