document.addEventListener('DOMContentLoaded', function() { // DOM Elements const spinner = document.getElementById('spinner'); const animationContainer = document.getElementById('animation-container'); const playBtn = document.getElementById('play-btn'); const spinnerType = document.getElementById('spinner-type'); const spinnerColor = document.getElementById('spinner-color'); const spinnerColor2 = document.getElementById('spinner-color-2'); const spinnerSize = document.getElementById('spinner-size'); const sizeValue = document.getElementById('size-value'); const spinnerSpeed = document.getElementById('spinner-speed'); const speedValue = document.getElementById('speed-value'); const animationStyle = document.getElementById('animation-style'); const customSvgUpload = document.getElementById('custom-svg-upload'); const svgUpload = document.getElementById('svg-upload'); const particleControls = document.getElementById('particle-controls'); const particleCount = document.getElementById('particle-count'); const particleSize = document.getElementById('particle-size'); // Export buttons const exportCssBtn = document.getElementById('export-css'); const exportHtmlBtn = document.getElementById('export-html'); const exportJsBtn = document.getElementById('export-js'); const exportReactBtn = document.getElementById('export-react'); const exportVueBtn = document.getElementById('export-vue'); const exportGifBtn = document.getElementById('export-gif'); const exportSvgBtn = document.getElementById('export-svg'); const exportLottieBtn = document.getElementById('export-lottie'); const animationCards = document.querySelectorAll('.animation-card'); // Current animation state let currentAnimation = 'circle'; let isPlaying = true; let customSvgPath = null; let animationTimeline = null; // Initialize updateSizeDisplay(); updateSpeedDisplay(); feather.replace(); // Setup Konva stage for advanced animations const stage = new Konva.Stage({ container: animationContainer, width: animationContainer.clientWidth, height: animationContainer.clientHeight }); const layer = new Konva.Layer(); stage.add(layer); // Event Listeners spinnerType.addEventListener('change', function() { updateSpinnerType(); toggleAdvancedControls(); }); spinnerColor.addEventListener('input', updateSpinnerColor); spinnerColor2.addEventListener('input', updateSpinnerColor); spinnerSize.addEventListener('input', updateSpinnerSize); spinnerSpeed.addEventListener('input', updateSpinnerSpeed); animationStyle.addEventListener('change', updateAnimationStyle); svgUpload.addEventListener('change', handleSvgUpload); particleCount.addEventListener('input', updateParticles); particleSize.addEventListener('input', updateParticles); playBtn.addEventListener('click', toggleAnimation); exportCssBtn.addEventListener('click', exportCSS); exportHtmlBtn.addEventListener('click', exportHTML); exportJsBtn.addEventListener('click', exportJS); exportReactBtn.addEventListener('click', exportReact); exportVueBtn.addEventListener('click', exportVue); exportGifBtn.addEventListener('click', exportGIF); exportSvgBtn.addEventListener('click', exportSVG); exportLottieBtn.addEventListener('click', exportLottie); // Add click events to gallery items animationCards.forEach(card => { card.addEventListener('click', function() { const type = this.getAttribute('data-type'); spinnerType.value = type; updateSpinnerType(); }); }); // Toggle advanced controls based on selected animation type function toggleAdvancedControls() { customSvgUpload.classList.add('hidden'); particleControls.classList.add('hidden'); if (currentAnimation === 'custom') { customSvgUpload.classList.remove('hidden'); } else if (currentAnimation === 'particles') { particleControls.classList.remove('hidden'); } } // Handle SVG upload function handleSvgUpload(e) { const file = e.target.files[0]; if (!file) return; const reader = new FileReader(); reader.onload = function(event) { customSvgPath = event.target.result; updateSpinnerType(); }; reader.readAsText(file); } // Update particle animation function updateParticles() { if (currentAnimation === 'particles') { updateSpinnerType(); } } // Update animation easing style function updateAnimationStyle() { if (animationTimeline) { animationTimeline.easing(animationStyle.value); } } // Main function to update spinner type function updateSpinnerType() { currentAnimation = spinnerType.value; animationContainer.innerHTML = ''; let newSpinner; switch(currentAnimation) { case 'circle': newSpinner = document.createElement('div'); newSpinner.className = 'rounded-full border-4 border-t-transparent animate-spin'; newSpinner.style.borderColor = spinnerColor.value; newSpinner.style.width = `${spinnerSize.value}px`; newSpinner.style.height = `${spinnerSize.value}px`; newSpinner.style.animationDuration = `${spinnerSpeed.value}s`; break; case 'dots': newSpinner = document.createElement('div'); newSpinner.className = 'flex gap-2 items-center'; for (let i = 0; i < 3; i++) { const dot = document.createElement('div'); dot.className = 'rounded-full animate-bounce'; dot.style.backgroundColor = spinnerColor.value; dot.style.width = `${spinnerSize.value/5}px`; dot.style.height = `${spinnerSize.value/5}px`; dot.style.animationDuration = `${spinnerSpeed.value}s`; dot.style.animationDelay = `${i * 0.2}s`; newSpinner.appendChild(dot); } break; case 'bar': newSpinner = document.createElement('div'); newSpinner.className = 'w-3/4 h-2 bg-gray-200 rounded-full overflow-hidden'; const innerBar = document.createElement('div'); innerBar.className = 'h-full rounded-full animate-progress'; innerBar.style.backgroundColor = spinnerColor.value; innerBar.style.animationDuration = `${spinnerSpeed.value}s`; newSpinner.appendChild(innerBar); break; case 'pulse': newSpinner = document.createElement('div'); newSpinner.className = 'rounded-full animate-pulse'; newSpinner.style.backgroundColor = spinnerColor.value; newSpinner.style.width = `${spinnerSize.value}px`; newSpinner.style.height = `${spinnerSize.value}px`; newSpinner.style.animationDuration = `${spinnerSpeed.value}s`; break; case 'bounce': newSpinner = document.createElement('div'); newSpinner.className = 'rounded-full animate-custom-bounce'; newSpinner.style.backgroundColor = spinnerColor.value; newSpinner.style.width = `${spinnerSize.value}px`; newSpinner.style.height = `${spinnerSize.value}px`; newSpinner.style.animationDuration = `${spinnerSpeed.value}s`; break; } animationContainer.appendChild(newSpinner); spinner = newSpinner; } function updateSpinnerColor() { const elements = animationContainer.querySelectorAll('div'); elements.forEach(el => { if (el.style.borderColor) { el.style.borderColor = spinnerColor.value; } if (el.style.backgroundColor) { el.style.backgroundColor = spinnerColor.value; } }); } function updateSpinnerSize() { sizeValue.textContent = spinnerSize.value; const elements = animationContainer.querySelectorAll('div'); elements.forEach(el => { if (currentAnimation === 'dots') { el.style.width = `${spinnerSize.value/5}px`; el.style.height = `${spinnerSize.value/5}px`; } else { if (el.style.width) el.style.width = `${spinnerSize.value}px`; if (el.style.height) el.style.height = `${spinnerSize.value}px`; } }); } function updateSpinnerSpeed() { speedValue.textContent = spinnerSpeed.value; const elements = animationContainer.querySelectorAll('div'); elements.forEach(el => { if (el.style.animationDuration) { el.style.animationDuration = `${spinnerSpeed.value}s`; } }); } function updateSizeDisplay() { sizeValue.textContent = spinnerSize.value; } function updateSpeedDisplay() { speedValue.textContent = spinnerSpeed.value; } function toggleAnimation() { const elements = animationContainer.querySelectorAll('div'); if (isPlaying) { elements.forEach(el => { el.style.animationPlayState = 'paused'; }); playBtn.innerHTML = ' Play'; isPlaying = false; } else { elements.forEach(el => { el.style.animationPlayState = 'running'; }); playBtn.innerHTML = ' Pause'; isPlaying = true; } feather.replace(); } function createParticleAnimation() { layer.destroyChildren(); const count = parseInt(particleCount.value); const size = parseInt(particleSize.value); const centerX = stage.width() / 2; const centerY = stage.height() / 2; const radius = Math.min(stage.width(), stage.height()) * 0.3; const particles = []; for (let i = 0; i < count; i++) { const angle = (i / count) * Math.PI * 2; const x = centerX + Math.cos(angle) * radius; const y = centerY + Math.sin(angle) * radius; const particle = new Konva.Circle({ x: x, y: y, radius: size, fill: spinnerColor.value, opacity: 0.7 }); layer.add(particle); particles.push(particle); } animationTimeline = anime.timeline({ duration: parseInt(spinnerSpeed.value) * 1000, easing: animationStyle.value, loop: true }); particles.forEach((particle, i) => { const angle = (i / particles.length) * Math.PI * 2; animationTimeline.add({ targets: particle, x: centerX + Math.cos(angle + Math.PI) * radius, y: centerY + Math.sin(angle + Math.PI) * radius, fill: spinnerColor2.value, duration: parseInt(spinnerSpeed.value) * 1000, offset: 0 }); }); layer.draw(); } function exportCSS() { let cssCode = ''; switch(currentAnimation) { case 'circle': cssCode = `/* Circle Spinner */ .spinner { width: ${spinnerSize.value}px; height: ${spinnerSize.value}px; border: 4px solid ${spinnerColor.value}; border-top-color: transparent; border-radius: 50%; animation: spin ${spinnerSpeed.value}s linear infinite; } @keyframes spin { to { transform: rotate(360deg); } }`; break; case 'dots': cssCode = `/* Bouncing Dots */ .dots-container { display: flex; gap: 8px; align-items: center; } .dot { width: ${spinnerSize.value/5}px; height: ${spinnerSize.value/5}px; background-color: ${spinnerColor.value}; border-radius: 50%; animation: bounce ${spinnerSpeed.value}s infinite; } .dot:nth-child(2) { animation-delay: 0.2s; } .dot:nth-child(3) { animation-delay: 0.4s; } @keyframes bounce { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-15px); } }`; break; case 'bar': cssCode = `/* Progress Bar */ .progress-container { width: 75%; height: 8px; background-color: #e5e7eb; border-radius: 4px; overflow: hidden; } .progress-bar { height: 100%; background-color: ${spinnerColor.value}; border-radius: 4px; animation: progress ${spinnerSpeed.value}s ease-in-out infinite; } @keyframes progress { 0% { width: 0%; } 100% { width: 100%; } }`; break; case 'pulse': cssCode = `/* Pulse Animation */ .pulse { width: ${spinnerSize.value}px; height: ${spinnerSize.value}px; background-color: ${spinnerColor.value}; border-radius: 50%; animation: pulse ${spinnerSpeed.value}s cubic-bezier(0.4, 0, 0.6, 1) infinite; } @keyframes pulse { 0%, 100% { transform: scale(1); opacity: 1; } 50% { transform: scale(0.8); opacity: 0.5; } }`; break; case 'bounce': cssCode = `/* Bounce Animation */ .bounce { width: ${spinnerSize.value}px; height: ${spinnerSize.value}px; background-color: ${spinnerColor.value}; border-radius: 50%; animation: customBounce ${spinnerSpeed.value}s infinite; } @keyframes customBounce { 0%, 100% { transform: translateY(0) scale(1); } 25% { transform: translateY(-20px) scale(1.1); } 50% { transform: translateY(0) scale(1); } 75% { transform: translateY(-10px) scale(1.05); } }`; break; } // Create download link const blob = new Blob([cssCode], { type: 'text/css' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = 'spinner-animation.css'; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); } function exportHTML() { let htmlCode = ''; switch(currentAnimation) { case 'circle': htmlCode = `
`; break; case 'dots': htmlCode = `