wear-fun / meme.html
dodey917's picture
good use the OpenAI鈥檚 DALL路E / gpt-image-1
6159e33 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Meme Generator - Wear.Fun</title>
<link rel="icon" type="image/x-icon" href="https://huggingface.co/dodey917/wear-fun/resolve/main/images/KZr0gBp.png">
<link rel="stylesheet" href="style.css">
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://unpkg.com/feather-icons"></script>
<script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script>
</head>
<body class="bg-black text-white">
<custom-header></custom-header>
<main class="py-12 px-6">
<div class="max-w-4xl mx-auto">
<h1 class="text-3xl md:text-4xl font-bold text-center mb-2">Meme Generator</h1>
<p class="text-gray-400 text-center mb-12">Turn your memes into wearable art</p>
<div class="bg-gray-900 rounded-2xl p-6 md:p-8 border border-gray-800">
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
<div>
<h2 class="text-xl font-bold mb-4">Upload Your Image</h2>
<div id="dropZone" class="border-2 border-dashed border-gray-700 rounded-xl p-8 text-center mb-6 transition hover:border-red-500 cursor-pointer">
<i data-feather="upload" class="w-12 h-12 mx-auto text-gray-500 mb-4"></i>
<p class="mb-2">Drag & drop your image here</p>
<p class="text-sm text-gray-500 mb-4">or</p>
<button type="button" id="browseBtn" class="bg-red-600 hover:bg-red-700 px-6 py-2 rounded-lg font-medium">Browse Files</button>
<input type="file" id="fileInput" class="hidden" accept="image/*">
</div>
<div id="imagePreview" class="hidden mb-6">
<img id="previewImg" class="w-full rounded-lg" alt="Preview">
</div>
<div class="mb-6">
<label class="block text-gray-400 mb-2">Style Hint (Optional)</label>
<input type="text" placeholder="e.g., crypto meme, sarcastic" class="w-full bg-gray-800 border border-gray-700 rounded-lg px-4 py-3 focus:outline-none focus:ring-2 focus:ring-red-500">
</div>
<button id="generateBtn" class="w-full bg-red-600 hover:bg-red-700 text-white py-3 rounded-lg font-semibold transition disabled:bg-gray-600 disabled:cursor-not-allowed">
Generate Captions
</button>
</div>
<div>
<h2 class="text-xl font-bold mb-4">Generated Captions</h2>
<div id="captionsContainer" class="space-y-4 hidden">
<!-- Captions will be dynamically inserted here -->
</div>
<div id="loadingCaptions" class="hidden text-center py-8">
<div class="animate-spin rounded-full h-8 w-8 border-b-2 border-red-600 mx-auto"></div>
<p class="mt-4 text-gray-400">Generating hilarious captions...</p>
</div>
<div id="noCaptions" class="text-center py-8 text-gray-500">
<i data-feather="message-circle" class="w-12 h-12 mx-auto mb-4 opacity-50"></i>
<p>Upload an image and generate captions to see them here</p>
</div>
</div>
</div>
<div class="mt-12">
<h2 class="text-xl font-bold mb-4">T-Shirt Preview</h2>
<div id="mockupContainer" class="bg-gray-800 rounded-xl p-8 flex items-center justify-center min-h-[500px] border border-gray-700">
<div id="mockupPlaceholder" class="text-center">
<i data-feather="t-shirt" class="w-24 h-24 mx-auto text-gray-600 mb-4"></i>
<p class="text-gray-500">Your t-shirt mockup will appear here</p>
</div>
<div id="mockupPreview" class="hidden relative">
<!-- T-Shirt SVG Mockup -->
<svg width="300" height="350" viewBox="0 0 300 350" class="relative">
<!-- T-Shirt Base -->
<path d="M75 80 L75 40 L100 20 L120 30 L150 25 L180 30 L200 20 L225 40 L225 80 L200 100 L200 320 L100 320 L100 100 Z"
fill="#1a1a1a" stroke="#333" stroke-width="2"/>
<!-- Neckline -->
<ellipse cx="150" cy="40" rx="25" ry="20" fill="none" stroke="#333" stroke-width="2"/>
<!-- Sleeves -->
<path d="M75 80 L50 120 L40 180 L70 180 L100 100" fill="#1a1a1a" stroke="#333" stroke-width="2"/>
<path d="M225 80 L250 120 L260 180 L230 180 L200 100" fill="#1a1a1a" stroke="#333" stroke-width="2"/>
<!-- Design Area -->
<defs>
<clipPath id="designArea">
<rect x="90" y="100" width="120" height="120" rx="5"/>
</clipPath>
</defs>
<rect x="90" y="100" width="120" height="120" rx="5" fill="white" opacity="0.1"/>
<g clip-path="url(#designArea)">
<image id="tshirtImage" x="90" y="100" width="120" height="120" preserveAspectRatio="xMidYMid meet"/>
</g>
<!-- Caption Text -->
<text id="tshirtCaption" x="150" y="240" text-anchor="middle" fill="white" font-size="14" font-weight="bold" font-family="Arial, sans-serif"></text>
</svg>
</div>
</div>
</div>
</div>
</div>
</main>
<custom-footer></custom-footer>
<script src="components/header.js"></script>
<script src="components/footer.js"></script>
<script src="script.js"></script>
<script>
feather.replace();
let uploadedImage = null;
let currentCaption = '';
const dropZone = document.getElementById('dropZone');
const fileInput = document.getElementById('fileInput');
const browseBtn = document.getElementById('browseBtn');
const generateBtn = document.getElementById('generateBtn');
const imagePreview = document.getElementById('imagePreview');
const previewImg = document.getElementById('previewImg');
const captionsContainer = document.getElementById('captionsContainer');
const loadingCaptions = document.getElementById('loadingCaptions');
const noCaptions = document.getElementById('noCaptions');
const mockupContainer = document.getElementById('mockupContainer');
const mockupPlaceholder = document.getElementById('mockupPlaceholder');
const mockupPreview = document.getElementById('mockupPreview');
const mockupImg = document.getElementById('mockupImg');
const mockupCaption = document.getElementById('mockupCaption');
// File upload handlers
browseBtn.addEventListener('click', () => fileInput.click());
fileInput.addEventListener('change', (e) => {
const file = e.target.files[0];
if (file && file.type.startsWith('image/')) {
handleImageUpload(file);
}
});
dropZone.addEventListener('dragover', (e) => {
e.preventDefault();
dropZone.classList.add('border-red-500', 'bg-red-900/10');
});
dropZone.addEventListener('dragleave', () => {
dropZone.classList.remove('border-red-500', 'bg-red-900/10');
});
dropZone.addEventListener('drop', (e) => {
e.preventDefault();
dropZone.classList.remove('border-red-500', 'bg-red-900/10');
const file = e.dataTransfer.files[0];
if (file && file.type.startsWith('image/')) {
handleImageUpload(file);
}
});
function handleImageUpload(file) {
const reader = new FileReader();
reader.onload = (e) => {
uploadedImage = e.target.result;
previewImg.src = uploadedImage;
imagePreview.classList.remove('hidden');
generateBtn.disabled = false;
resetCaptions();
};
reader.readAsDataURL(file);
}
// Generate captions
generateBtn.addEventListener('click', async () => {
if (!uploadedImage) return;
loadingCaptions.classList.remove('hidden');
captionsContainer.classList.add('hidden');
noCaptions.classList.add('hidden');
generateBtn.disabled = true;
// Simulate API call with mock captions
setTimeout(() => {
const captions = generateMockCaptions();
displayCaptions(captions);
loadingCaptions.classList.add('hidden');
generateBtn.disabled = false;
}, 1500);
});
function generateMockCaptions() {
const captionTemplates = [
"When you finally understand that meme",
"Send this to your group chat immediately",
"My therapist: 'And how does that make you feel?'",
"Me explaining why this is the funniest thing ever",
"POV: You get the reference",
"IYKYK (If you know, you know)",
"This is my emotional support meme",
"When the meme hits different at 3 AM",
"Tell me you have internet without telling me you have internet",
"Chef's kiss: 馃"
];
return Array.from({length: 3}, () =>
captionTemplates[Math.floor(Math.random() * captionTemplates.length)]
);
}
function displayCaptions(captions) {
captionsContainer.innerHTML = '';
captions.forEach((caption, index) => {
const captionDiv = document.createElement('div');
captionDiv.className = 'bg-gray-800 p-4 rounded-lg border border-gray-700 hover:border-red-500 transition';
captionDiv.innerHTML = `
<p class="font-medium mb-3">${caption}</p>
<div class="flex gap-2">
<button class="preview-btn flex-1 bg-gray-700 hover:bg-gray-600 py-2 rounded-lg text-sm transition" data-caption="${caption}">
Preview Mockup
</button>
<button class="add-to-shirt-btn flex-1 bg-red-600 hover:bg-red-700 py-2 rounded-lg text-sm transition" data-caption="${caption}">
Add to T-Shirt
</button>
</div>
`;
captionsContainer.appendChild(captionDiv);
});
// Add event listeners to the new buttons
document.querySelectorAll('.preview-btn').forEach(btn => {
btn.addEventListener('click', (e) => {
const caption = e.target.dataset.caption;
showMockup(caption);
});
});
document.querySelectorAll('.add-to-shirt-btn').forEach(btn => {
btn.addEventListener('click', (e) => {
const caption = e.target.dataset.caption;
currentCaption = caption;
showMockup(caption);
// Create success notification
const notification = document.createElement('div');
notification.className = 'fixed top-20 right-4 bg-green-600 text-white px-6 py-3 rounded-lg shadow-lg z-50 transform translate-x-full transition-transform duration-300';
notification.innerHTML = `
<div class="flex items-center">
<i data-feather="check-circle" class="w-5 h-5 mr-2"></i>
<span>Design added to T-Shirt! Scroll down to preview.</span>
</div>
`;
document.body.appendChild(notification);
feather.replace();
// Animate in
setTimeout(() => {
notification.style.transform = 'translateX(0)';
}, 100);
// Remove after 3 seconds
setTimeout(() => {
notification.style.transform = 'translateX(100%)';
setTimeout(() => {
document.body.removeChild(notification);
}, 300);
}, 3000);
});
});
captionsContainer.classList.remove('hidden');
}
async function showMockup(caption) {
if (!uploadedImage) return;
mockupPlaceholder.classList.add('hidden');
mockupPreview.classList.remove('hidden');
// Show loading state
mockupPreview.innerHTML = `
<div class="text-center py-8">
<div class="animate-spin rounded-full h-12 w-12 border-b-2 border-red-600 mx-auto mb-4"></div>
<p class="text-gray-400 mb-2">Generating realistic T-shirt mockup...</p>
<p class="text-gray-500 text-sm">Using AI to create a photorealistic design</p>
</div>
`;
try {
const shirtColor = window.selectedShirtColor || 'white';
// Call backend API for DALL-E generation
const response = await fetch('/api/generate-mockup', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
image: uploadedImage,
caption: caption,
shirtColor: shirtColor
})
});
if (!response.ok) {
throw new Error('Failed to generate mockup');
}
const data = await response.json();
if (data.success && data.imageUrl) {
// Display the generated realistic mockup
mockupPreview.innerHTML = `
<div class="space-y-4">
<div class="relative rounded-lg overflow-hidden shadow-2xl">
<img src="${data.imageUrl}" alt="T-shirt Mockup" class="w-full h-auto">
<div class="absolute bottom-0 left-0 right-0 bg-gradient-to-t from-black/80 to-transparent p-4">
<p class="text-white text-sm font-medium">${caption}</p>
</div>
</div>
<div class="flex gap-2 justify-center">
<button onclick="regenerateMockup('${caption}')" class="bg-gray-700 hover:bg-gray-600 px-4 py-2 rounded-lg text-sm transition">
<i data-feather="refresh-cw" class="w-4 h-4 inline mr-1"></i>
Regenerate
</button>
<button onclick="downloadMockup('${data.imageUrl}')" class="bg-red-600 hover:bg-red-700 px-4 py-2 rounded-lg text-sm transition">
<i data-feather="download" class="w-4 h-4 inline mr-1"></i>
Download
</button>
</div>
</div>
`;
feather.replace();
} else {
throw new Error(data.error || 'Generation failed');
}
} catch (error) {
console.error('Mockup generation error:', error);
showNotification('Failed to generate realistic mockup. Using fallback preview.', 'warning');
// Fallback to SVG mockup if API fails
showFallbackMockup(caption);
}
}
function regenerateMockup(caption) {
showMockup(caption);
}
function downloadMockup(imageUrl) {
const link = document.createElement('a');
link.href = imageUrl;
link.download = 'tshirt-mockup.png';
link.target = '_blank';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
function showNotification(message, type = 'info') {
const notification = document.createElement('div');
const bgColor = type === 'warning' ? 'bg-yellow-600' : type === 'error' ? 'bg-red-600' : 'bg-green-600';
notification.className = `fixed top-20 right-4 ${bgColor} text-white px-6 py-3 rounded-lg shadow-lg z-50 transform translate-x-full transition-transform duration-300`;
notification.innerHTML = `
<div class="flex items-center">
<i data-feather="${type === 'warning' ? 'alert-triangle' : type === 'error' ? 'x-circle' : 'check-circle'}" class="w-5 h-5 mr-2"></i>
<span>${message}</span>
</div>
`;
document.body.appendChild(notification);
feather.replace();
setTimeout(() => {
notification.style.transform = 'translateX(0)';
}, 100);
setTimeout(() => {
notification.style.transform = 'translateX(100%)';
setTimeout(() => {
document.body.removeChild(notification);
}, 300);
}, 5000);
}
function showFallbackMockup(caption) {
const tshirtImage = document.getElementById('tshirtImage');
const tshirtCaption = document.getElementById('tshirtCaption');
// Create the SVG fallback structure
mockupPreview.innerHTML = `
<svg width="300" height="350" viewBox="0 0 300 350" class="relative">
<!-- T-Shirt Base -->
<path d="M75 80 L75 40 L100 20 L120 30 L150 25 L180 30 L200 20 L225 40 L225 80 L200 100 L200 320 L100 320 L100 100 Z"
fill="#1a1a1a" stroke="#333" stroke-width="2"/>
<!-- Neckline -->
<ellipse cx="150" cy="40" rx="25" ry="20" fill="none" stroke="#333" stroke-width="2"/>
<!-- Sleeves -->
<path d="M75 80 L50 120 L40 180 L70 180 L100 100" fill="#1a1a1a" stroke="#333" stroke-width="2"/>
<path d="M225 80 L250 120 L260 180 L230 180 L200 100" fill="#1a1a1a" stroke="#333" stroke-width="2"/>
<!-- Design Area -->
<defs>
<clipPath id="designArea">
<rect x="90" y="100" width="120" height="120" rx="5"/>
</clipPath>
</defs>
<rect x="90" y="100" width="120" height="120" rx="5" fill="white" opacity="0.1"/>
<g clip-path="url(#designArea)">
<image id="tshirtImage" x="90" y="100" width="120" height="120" preserveAspectRatio="xMidYMid meet"/>
</g>
</svg>
<div class="mt-4 p-4 bg-gray-800 rounded-lg">
<p class="text-white text-center font-medium">${caption}</p>
</div>
`;
// Set the image on the t-shirt
const tshirtImg = document.getElementById('tshirtImage');
if (tshirtImg) {
tshirtImg.setAttributeNS('http://www.w3.org/1999/xlink', 'href', uploadedImage);
}
}
function resetCaptions() {
captionsContainer.classList.add('hidden');
noCaptions.classList.remove('hidden');
mockupPlaceholder.classList.remove('hidden');
mockupPreview.classList.add('hidden');
generateBtn.disabled = !uploadedImage;
}
// Style hint input
const styleHint = document.querySelector('input[placeholder="e.g., crypto meme, sarcastic"]');
if (styleHint) {
styleHint.addEventListener('input', (e) => {
// You could use this to influence caption generation
console.log('Style hint:', e.target.value);
});
}
</script>
</body>
</html>