wallgen / frontend /build /script.js
Ankit
application files
c476ef5
document.addEventListener('DOMContentLoaded', () => {
const elements = {
generateBtn: document.getElementById('generate-btn'),
surpriseBtn: document.getElementById('surprise-btn'),
randomSeedBtn: document.getElementById('random-seed-btn'),
resultImage: document.getElementById('result-image'),
paletteContainer: document.getElementById('palette-container'),
loader: document.getElementById('loader'),
backgroundContainer: document.getElementById('background-container'),
statusDisplay: document.getElementById('status-display'),
styleSelect: document.getElementById('style-select'),
seedInput: document.getElementById('seed-input'),
colorInput: document.getElementById('color-input'),
postGenerationTools: document.getElementById('post-generation-tools'),
copySeedBtn: document.getElementById('copy-seed-btn'),
downloadBtn: document.getElementById('download-btn'),
copySeedFeedback: document.getElementById('copy-seed-feedback'),
tagline: document.getElementById('tagline'),
recentCreationsSection: document.getElementById('recent-creations'),
recentGallery: document.getElementById('recent-gallery'),
particlesContainer: document.getElementById('particles'),
};
// API URL for your Python backend
const API_URL = '';
// const API_URL = 'http://127.0.0.1:8000';
// State Variables
let backgroundImages = [];
let recentCreations = [];
let statusInterval;
const MAX_BG_IMAGES = 6;
const MAX_RECENTS = 4;
// Preloaded example images for floating background
const EXAMPLE_IMAGES = [
'examples/img1.png',
'examples/img2.png',
'examples/img3.png',
'examples/img4.png',
'examples/img5.png',
'examples/img6.png',
];
// Enhanced Particles System
function createParticles() {
const colors = ['#4D6473', '#A58A73', '#B59B67', '#F4C2C2', '#E6CFC1'];
for (let i = 0; i < 20; i++) {
const particle = document.createElement('div');
particle.className = 'particle';
particle.style.width = Math.random() * 8 + 4 + 'px';
particle.style.height = particle.style.width;
particle.style.left = Math.random() * 100 + '%';
particle.style.top = Math.random() * 100 + '%';
particle.style.backgroundColor = colors[Math.floor(Math.random() * colors.length)];
particle.style.animationDelay = Math.random() * 15 + 's';
particle.style.animationDuration = (Math.random() * 10 + 10) + 's';
elements.particlesContainer.appendChild(particle);
}
}
// Enhanced Typing Animation
function typeText(element, text, speed = 100) {
let i = 0;
element.innerHTML = "";
const typing = setInterval(() => {
if (i < text.length) {
element.innerHTML += text.charAt(i);
i++;
} else {
clearInterval(typing);
setTimeout(() => {
element.style.textShadow = '0 0 20px rgba(77, 100, 115, 0.5)';
setTimeout(() => {
element.style.textShadow = 'none';
}, 1000);
}, 500);
}
}, speed);
}
// Enhanced Floating Background Logic
function addFloatingImage(imageUrl) {
const img = document.createElement('img');
img.src = imageUrl;
img.className = 'floating-bg';
if (backgroundImages.length >= MAX_BG_IMAGES) {
const oldImg = backgroundImages.shift();
oldImg.style.opacity = '0';
oldImg.style.transform = 'scale(0.8)';
setTimeout(() => oldImg.remove(), 1000);
}
const size = Math.random() * 180 + 120;
img.style.width = `${size}px`;
img.style.height = 'auto';
img.style.top = `${Math.random() * 80 + 10}%`;
img.style.left = `${Math.random() * 80 + 10}%`;
img.style.animationDuration = `${Math.random() * 15 + 20}s`;
img.style.zIndex = Math.floor(Math.random() * 3) + 1;
elements.backgroundContainer.appendChild(img);
backgroundImages.push(img);
}
function addFloatingImageFromExamples() {
const imageUrl = EXAMPLE_IMAGES[Math.floor(Math.random() * EXAMPLE_IMAGES.length)];
addFloatingImage(imageUrl);
}
function addFloatingImageAt(imageUrl, topPercent, leftPercent, cellWPercent) {
const img = document.createElement('img');
img.src = imageUrl;
img.className = 'floating-bg';
if (backgroundImages.length >= MAX_BG_IMAGES) {
const oldImg = backgroundImages.shift();
oldImg.style.opacity = '0';
oldImg.style.transform = 'scale(0.8)';
setTimeout(() => oldImg.remove(), 1000);
}
// Size scaled to cell width for better spacing
const cellWidthPx = window.innerWidth * (cellWPercent / 100);
const targetWidth = Math.max(120, Math.min(220, cellWidthPx * 0.6));
img.style.width = `${Math.round(targetWidth)}px`;
img.style.height = 'auto';
// track percent positions for later adjustment
img.dataset.topPercent = String(topPercent);
img.dataset.leftPercent = String(leftPercent);
img.style.top = `${topPercent}%`;
img.style.left = `${leftPercent}%`;
img.style.animationDuration = `${Math.random() * 15 + 20}s`;
img.style.zIndex = Math.floor(Math.random() * 3) + 1;
elements.backgroundContainer.appendChild(img);
// After the image has dimensions, nudge it so no more than 50% is under the controls card
if (img.complete) {
adjustToAvoidControls(img, 8);
} else {
img.addEventListener('load', () => adjustToAvoidControls(img, 8), { once: true });
}
backgroundImages.push(img);
}
function computeEvenPositions(count, margin = 8) {
const cols = Math.ceil(Math.sqrt(count));
const rows = Math.ceil(count / cols);
const availableW = 100 - 2 * margin;
const availableH = 100 - 2 * margin;
const cellW = availableW / cols;
const cellH = availableH / rows;
const positions = [];
for (let i = 0; i < count; i++) {
const r = Math.floor(i / cols);
const c = i % cols;
let top = margin + (r + 0.5) * cellH;
let left = margin + (c + 0.5) * cellW;
const jitterX = (Math.random() - 0.5) * cellW * 0.3;
const jitterY = (Math.random() - 0.5) * cellH * 0.3;
top += jitterY;
left += jitterX;
top = Math.max(margin, Math.min(100 - margin, top));
left = Math.max(margin, Math.min(100 - margin, left));
positions.push({ top, left });
}
return { positions, cellW, cellH, rows, cols };
}
function computeOverlapArea(r1, r2) {
const x = Math.max(0, Math.min(r1.right, r2.right) - Math.max(r1.left, r2.left));
const y = Math.max(0, Math.min(r1.bottom, r2.bottom) - Math.max(r1.top, r2.top));
return x * y;
}
function adjustToAvoidControls(img, margin = 8) {
const controls = document.querySelector('.controls');
if (!controls) return;
const clamp = (val, min, max) => Math.max(min, Math.min(max, val));
let topP = parseFloat(img.dataset.topPercent || '50');
let leftP = parseFloat(img.dataset.leftPercent || '50');
const step = 3; // percent per nudge
const maxTries = 25;
for (let i = 0; i < maxTries; i++) {
const imgRect = img.getBoundingClientRect();
const ctrlRect = controls.getBoundingClientRect();
const imgArea = imgRect.width * imgRect.height;
if (!imgArea) break;
const overlap = computeOverlapArea(imgRect, ctrlRect);
const ratio = overlap / imgArea;
if (ratio <= 0.5) break; // acceptable
const cx = ctrlRect.left + ctrlRect.width / 2;
const cy = ctrlRect.top + ctrlRect.height / 2;
const ix = imgRect.left + imgRect.width / 2;
const iy = imgRect.top + imgRect.height / 2;
const overlapW = Math.max(0, Math.min(imgRect.right, ctrlRect.right) - Math.max(imgRect.left, ctrlRect.left));
const overlapH = Math.max(0, Math.min(imgRect.bottom, ctrlRect.bottom) - Math.max(imgRect.top, ctrlRect.top));
if (overlapW >= overlapH) {
const dir = ix < cx ? -1 : 1; // move away horizontally
leftP = clamp(leftP + dir * step, margin, 100 - margin);
} else {
const dir = iy < cy ? -1 : 1; // move away vertically
topP = clamp(topP + dir * step, margin, 100 - margin);
}
img.dataset.topPercent = String(topP);
img.dataset.leftPercent = String(leftP);
img.style.top = `${topP}%`;
img.style.left = `${leftP}%`;
}
}
// Enhanced Status Messages
function startStatusUpdates() {
const messages = [
" Mixing digital paint...",
" Consulting the AI muse...",
" Channeling creative energy...",
" Weaving visual magic...",
" Launching imagination...",
" Almost there..."
];
let msgIndex = 0;
elements.statusDisplay.textContent = messages[msgIndex];
statusInterval = setInterval(() => {
msgIndex = (msgIndex + 1) % messages.length;
elements.statusDisplay.textContent = messages[msgIndex];
}, 2500);
}
function stopStatusUpdates(finalMessage) {
clearInterval(statusInterval);
elements.statusDisplay.textContent = finalMessage;
elements.statusDisplay.style.transform = 'scale(1.1)';
elements.statusDisplay.style.color = '#4D6473';
setTimeout(() => {
elements.statusDisplay.style.transform = 'scale(1)';
elements.statusDisplay.style.color = '';
}, 300);
}
// Enhanced Recent Creations
function renderRecents() {
if (recentCreations.length === 0) return;
elements.recentGallery.innerHTML = "";
recentCreations.forEach((creation, index) => {
const img = document.createElement('img');
img.src = creation.src;
img.title = `Style: ${creation.style}, Seed: ${creation.seed}`;
img.style.animationDelay = `${index * 0.1}s`;
img.addEventListener('click', () => {
elements.styleSelect.value = creation.style;
elements.seedInput.value = creation.seed;
elements.colorInput.value = creation.color || "";
window.scrollTo({
top: 0,
behavior: 'smooth'
});
elements.styleSelect.style.boxShadow = '0 0 20px rgba(77, 100, 115, 0.5)';
setTimeout(() => {
elements.styleSelect.style.boxShadow = '';
}, 2000);
});
elements.recentGallery.appendChild(img);
});
elements.recentCreationsSection.classList.remove('hidden');
}
// Enhanced Color Palette Display
function displayColorPalette(colors) {
elements.paletteContainer.innerHTML = '';
colors.forEach((color, index) => {
const wrapper = document.createElement('div');
wrapper.className = 'color-swatch-wrapper';
wrapper.style.animationDelay = `${index * 0.1}s`;
const swatch = document.createElement('div');
swatch.className = 'color-swatch';
swatch.style.backgroundColor = color;
const hexCode = document.createElement('span');
hexCode.className = 'hex-code';
hexCode.textContent = color;
swatch.addEventListener('click', () => {
navigator.clipboard.writeText(color).then(() => {
hexCode.textContent = 'Copied!';
swatch.style.transform = 'scale(1.3) rotate(10deg)';
setTimeout(() => {
hexCode.textContent = color;
swatch.style.transform = '';
}, 1500);
});
});
wrapper.appendChild(swatch);
wrapper.appendChild(hexCode);
elements.paletteContainer.appendChild(wrapper);
});
}
// **UPDATED** Generation Function with actual API call
async function performGeneration() {
elements.loader.classList.remove('hidden');
elements.resultImage.classList.add('hidden');
elements.paletteContainer.innerHTML = '';
elements.generateBtn.disabled = true;
elements.surpriseBtn.disabled = true;
elements.postGenerationTools.classList.add('hidden');
startStatusUpdates();
const generationData = {
style: elements.styleSelect.value,
resolution: document.getElementById('resolution-select').value,
seed: parseInt(elements.seedInput.value, 10),
color: elements.colorInput.value,
steps: 4,
};
try {
const response = await fetch(`${API_URL}/generate-wallpaper/`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(generationData),
});
if (!response.ok) {
throw new Error(`Server error! Status: ${response.status}`);
}
const data = await response.json();
// Display result
elements.resultImage.src = data.image;
elements.resultImage.classList.remove('hidden');
// Show tools and palette
elements.postGenerationTools.classList.remove('hidden');
displayColorPalette(data.palette);
// Add to recents
recentCreations.unshift({ ...generationData,
src: data.image
});
if (recentCreations.length > MAX_RECENTS) {
recentCreations.pop();
}
renderRecents();
stopStatusUpdates(' Masterpiece created!');
} catch (error) {
console.error('Generation failed:', error);
stopStatusUpdates(`Error: ${error.message}`);
} finally {
elements.loader.classList.add('hidden');
elements.generateBtn.disabled = false;
elements.surpriseBtn.disabled = false;
}
}
// **UPDATED** Random Seed Generator with actual API call
async function updateRandomSeed() {
try {
const response = await fetch(`${API_URL}/random-seed/`);
if (!response.ok) {
throw new Error('Failed to fetch seed from server.');
}
const data = await response.json();
elements.seedInput.value = data.seed;
// Visual feedback
elements.seedInput.style.background = 'rgba(77, 100, 115, 0.2)';
setTimeout(() => {
elements.seedInput.style.background = '';
}, 500);
} catch (error) {
console.error('Failed to get random seed:', error);
elements.seedInput.value = Math.floor(Math.random() * 999999);
}
}
elements.generateBtn.addEventListener('click', performGeneration);
elements.randomSeedBtn.addEventListener('click', updateRandomSeed);
elements.surpriseBtn.addEventListener('click', async () => {
const options = elements.styleSelect.options;
const randomIndex = Math.floor(Math.random() * options.length);
elements.styleSelect.value = options[randomIndex].value;
elements.colorInput.value = "";
await updateRandomSeed();
elements.surpriseBtn.style.transform = 'rotate(360deg)';
setTimeout(() => {
elements.surpriseBtn.style.transform = '';
performGeneration();
}, 500);
});
elements.copySeedBtn.addEventListener('click', () => {
const seedValue = elements.seedInput.value;
navigator.clipboard.writeText(seedValue).then(() => {
elements.copySeedFeedback.textContent = 'Copied!';
elements.copySeedBtn.style.background = 'rgba(77, 100, 115, 0.3)';
setTimeout(() => {
elements.copySeedFeedback.textContent = 'Copy Seed';
elements.copySeedBtn.style.background = '';
}, 2000);
});
});
elements.downloadBtn.addEventListener('click', () => {
if (!elements.resultImage.src || elements.resultImage.src.startsWith('http')) return;
const link = document.createElement('a');
link.href = elements.resultImage.src;
const style = elements.styleSelect.value.replace(/\s+/g, '-').toLowerCase();
const seed = elements.seedInput.value;
link.download = `wallpaper-${style}-${seed}.png`;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
elements.downloadBtn.style.transform = 'scale(1.1)';
setTimeout(() => {
elements.downloadBtn.style.transform = '';
}, 200);
});
// Enhanced Input Interactions
[elements.styleSelect, elements.colorInput, elements.seedInput].forEach(input => {
input.addEventListener('focus', (e) => {
e.target.style.transform = 'translateY(-2px)';
});
input.addEventListener('blur', (e) => {
e.target.style.transform = '';
});
});
// Initialize Everything
createParticles();
typeText(elements.tagline, "Create unique, high-resolution wallpapers with AI magic ");
if (typeof initThemeSelector === 'function') {
initThemeSelector();
}
// Compute an even spread layout and seed images
const layout = computeEvenPositions(EXAMPLE_IMAGES.length, 8);
EXAMPLE_IMAGES.forEach((src, i) => {
const pos = layout.positions[i];
setTimeout(() => addFloatingImageAt(src, pos.top, pos.left, layout.cellW), i * 400);
});
// Keep floating background anchored during scroll (no parallax)
window.addEventListener('scroll', () => {
elements.backgroundContainer.style.transform = 'translateY(0)';
});
// Add keyboard shortcuts
document.addEventListener('keydown', (e) => {
if (e.ctrlKey || e.metaKey) {
switch (e.key) {
case 'Enter':
e.preventDefault();
performGeneration();
break;
case 'r':
e.preventDefault();
updateRandomSeed();
break;
case 's':
e.preventDefault();
elements.surpriseBtn.click();
break;
}
}
});
console.log('AI Wallpaper Generator initialized successfully!');
});