Created
June 10, 2025 08:25
-
-
Save gestisch2025/e35c634c78bb8d17cfffbd6b131e2b64 to your computer and use it in GitHub Desktop.
Untitled
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<html lang="en"> | |
<head> | |
<meta charset="UTF-8" /> | |
<meta name="viewport" content="width=device-width, initial-scale=1" /> | |
<title>Audio Player</title> | |
<script src="https://cdn.tailwindcss.com"></script> | |
<link | |
rel="stylesheet" | |
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css" | |
/> | |
</head> | |
<body class="bg-gray-100 flex items-center justify-center min-h-screen"> | |
<div class="w-[320px] h-[320px] rounded-lg border-2 border-[#4a2f3a] shadow-lg bg-white flex flex-col"> | |
<!-- Top bar --> | |
<div class="flex items-center justify-between bg-[#c18db0] rounded-t-lg border-b-2 border-[#4a2f3a] h-10 px-3"> | |
<span class="text-white font-bold text-sm">Audio Player</span> | |
<button class="w-6 h-6 flex items-center justify-center border-2 border-white rounded-full text-white" aria-label="Close">×</button> | |
</div> | |
<!-- Main content --> | |
<div class="p-2 flex flex-col h-full justify-between"> | |
<!-- File input for audio selection --> | |
<input type="file" id="audio-upload" accept="audio/*" class="mb-2 w-full border-2 border-[#4a2f3a] rounded-md p-1 text-sm" /> | |
<!-- Progress bar with star as progress indicator --> | |
<div class="relative h-6 w-full bg-gray-200 border-2 border-[#4a2f3a] rounded-md mb-2"> | |
<div id="progress-fill" class="absolute top-0 left-0 h-full bg-[#f9d9a7] rounded-md" style="width: 0%;"></div> | |
<button id="progress-star" class="absolute top-0 left-0 w-6 h-6 bg-[#f9d9a7] rounded-md border-2 border-[#4a2f3a] flex items-center justify-center cursor-pointer" aria-label="Seek"> | |
<svg xmlns="http://www.w3.org/2000/svg" class="w-3 h-3 text-[#4a2f3a]" fill="none" viewBox="0 0 24 24" stroke="#4a2f3a" stroke-width="1.5" stroke-linejoin="round" stroke-linecap="round"> | |
<path stroke-linejoin="round" d="M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z" /> | |
</svg> | |
</button> | |
</div> | |
<!-- Controls --> | |
<div class="flex justify-between text-[#4a2f3a] mt-auto mb-4"> | |
<button id="prev-btn" aria-label="Previous" class="w-8 h-10 flex items-center justify-center border-2 border-[#4a2f3a] rounded-md"> | |
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="#4a2f3a" stroke-width="2" stroke-linejoin="round" stroke-linecap="round"> | |
<polygon points="15 19 8 12 15 5 15 19" /> | |
</svg> | |
</button> | |
<button id="play-pause-btn" aria-label="Play" class="w-10 h-10 flex items-center justify-center border-2 border-[#4a2f3a] rounded-full bg-[#c18db0]"> | |
<svg id="play-icon" xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-[#4a2f3a]" fill="none" viewBox="0 0 24 24" stroke="#4a2f3a" stroke-width="2" stroke-linejoin="round" stroke-linecap="round"> | |
<polygon points="5 3 19 12 5 21 5 3" /> | |
</svg> | |
<svg id="pause-icon" xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-[#4a2f3a] hidden" fill="none" viewBox="0 0 24 24" stroke="#4a2f3a" stroke-width="2" stroke-linejoin="round" stroke-linecap="round"> | |
<rect x="6" y="5" width="4" height="14" rx="1" /> | |
<rect x="14" y="5" width="4" height="14" rx="1" /> | |
</svg> | |
</button> | |
<button id="next-btn" aria-label="Next" class="w-8 h-10 flex items-center justify-center border-2 border-[#4a2f3a] rounded-md"> | |
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="#4a2f3a" stroke-width="2" stroke-linejoin="round" stroke-linecap="round"> | |
<polygon points="9 5 16 12 9 19 9 5" /> | |
</svg> | |
</button> | |
</div> | |
<!-- Platz unter den Steuerelementen --> | |
<div class="flex-grow"></div> | |
</div> | |
</div> | |
<audio id="audio" src=""></audio> | |
<script> | |
const audio = document.getElementById('audio'); | |
const playPauseBtn = document.getElementById('play-pause-btn'); | |
const playIcon = document.getElementById('play-icon'); | |
const pauseIcon = document.getElementById('pause-icon'); | |
const progressFill = document.getElementById('progress-fill'); | |
const progressStar = document.getElementById('progress-star'); | |
const prevBtn = document.getElementById('prev-btn'); | |
const nextBtn = document.getElementById('next-btn'); | |
const audioUpload = document.getElementById('audio-upload'); | |
// Load selected audio from file input | |
audioUpload.addEventListener('change', (event) => { | |
const file = event.target.files[0]; | |
if (file) { | |
const fileURL = URL.createObjectURL(file); | |
audio.src = fileURL; | |
audio.currentTime = 0; // Reset to start | |
audio.play(); // Play the new track if a file is selected | |
} | |
}); | |
// Play/pause toggle | |
playPauseBtn.addEventListener('click', () => { | |
if (audio.paused) { | |
audio.play(); | |
} else { | |
audio.pause(); | |
} | |
}); | |
audio.addEventListener('play', () => { | |
playIcon.classList.add('hidden'); | |
pauseIcon.classList.remove('hidden'); | |
playPauseBtn.setAttribute('aria-label', 'Pause'); | |
}); | |
audio.addEventListener('pause', () => { | |
playIcon.classList.remove('hidden'); | |
pauseIcon.classList.add('hidden'); | |
playPauseBtn.setAttribute('aria-label', 'Play'); | |
}); | |
// Update progress bar and star position | |
audio.addEventListener('timeupdate', () => { | |
if (!audio.duration) return; | |
const progressPercent = (audio.currentTime / audio.duration) * 100; | |
progressFill.style.width = progressPercent + '%'; | |
const containerWidth = progressFill.parentElement.clientWidth; | |
const starWidth = progressStar.clientWidth; | |
let leftPos = (progressPercent / 100) * containerWidth - starWidth / 2; | |
if (leftPos < 0) leftPos = 0; | |
if (leftPos > containerWidth - starWidth) leftPos = containerWidth - starWidth; | |
progressStar.style.left = leftPos + 'px'; | |
}); | |
// Seek audio by clicking or dragging star | |
let isDragging = false; | |
function seekAudio(clientX) { | |
const rect = progressFill.parentElement.getBoundingClientRect(); | |
let offsetX = clientX - rect.left; | |
if (offsetX < 0) offsetX = 0; | |
if (offsetX > rect.width) offsetX = rect.width; | |
const seekTime = (offsetX / rect.width) * audio.duration; | |
audio.currentTime = seekTime; | |
} | |
progressStar.addEventListener('mousedown', (e) => { | |
isDragging = true; | |
e.preventDefault(); | |
}); | |
window.addEventListener('mouseup', () => { | |
if (isDragging) isDragging = false; | |
}); | |
window.addEventListener('mousemove', (e) => { | |
if (isDragging) { | |
seekAudio(e.clientX); | |
} | |
}); | |
// Also allow clicking on progress bar to seek | |
progressStar.parentElement.addEventListener('click', (e) => { | |
seekAudio(e.clientX); | |
}); | |
// Dummy prev/next buttons (no playlist) | |
prevBtn.addEventListener('click', () => { | |
audio.currentTime = 0; | |
}); | |
nextBtn.addEventListener('click', () => { | |
audio.currentTime = audio.duration || 0; | |
audio.pause(); | |
}); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment