Spaces:
Runtime error
Runtime error
<script lang="ts"> | |
import { afterUpdate, onMount } from 'svelte'; | |
import { fade } from 'svelte/transition'; | |
import { audioBlob, notesImage, style } from './stores'; | |
import { styles } from './config.json'; | |
let section: HTMLElement; | |
let currentTime: number; | |
let duration: number; | |
let paused = true; | |
let player: HTMLDivElement; | |
let visualisation: HTMLImageElement; | |
let imageWidth: number; | |
let imageHeight: number; | |
const updateDimensions = (): void => { | |
imageWidth = visualisation && visualisation.clientWidth; | |
imageHeight = visualisation && visualisation.clientHeight; | |
}; | |
onMount(() => { | |
updateDimensions(); | |
if ('mediaSession' in navigator) { | |
navigator.mediaSession.setActionHandler('play', () => (paused = false)); | |
navigator.mediaSession.setActionHandler('pause', () => (paused = true)); | |
navigator.mediaSession.setActionHandler('stop', () => { | |
paused = true; | |
currentTime = 0; | |
}); | |
} | |
window.scrollTo({ top: section.offsetTop, behavior: 'smooth' }); | |
}); | |
afterUpdate((): void => { | |
updateDimensions(); | |
}); | |
const mouseMove = (event: MouseEvent): void => { | |
if (!duration) { | |
return; | |
} | |
if (!event.buttons) { | |
return; | |
} | |
const { left, right } = player.getBoundingClientRect(); | |
currentTime = (duration * (event.clientX - left)) / (right - left); | |
}; | |
const touchMove = (event: TouchEvent): void => { | |
if (!duration) { | |
return; | |
} | |
const { left, right } = player.getBoundingClientRect(); | |
currentTime = (duration * (event.touches[0].clientX - left)) / (right - left); | |
}; | |
const keyDown = (event: KeyboardEvent): void => { | |
event.preventDefault(); | |
if (event.code === 'Space') { | |
paused = !paused; | |
} | |
if (event.code === 'ArrowLeft') { | |
currentTime = currentTime >= 1 ? currentTime - 1 : 0; | |
} | |
if (event.code === 'ArrowRight') { | |
currentTime = currentTime <= duration - 1 ? currentTime + 1 : duration; | |
} | |
}; | |
</script> | |
<section bind:this={section} transition:fade> | |
<img class="notes" src={$notesImage} alt="" bind:this={visualisation} /> | |
<div | |
bind:this={player} | |
class="player" | |
style:width={imageWidth + 'px'} | |
style:height={imageHeight + 'px'} | |
on:mousemove={mouseMove} | |
on:touchmove|preventDefault={touchMove} | |
on:keydown={keyDown} | |
on:click={() => (paused = !paused)} | |
tabindex="0" | |
> | |
<audio bind:currentTime bind:duration bind:paused src={$audioBlob} /> | |
<div | |
class="handle" | |
style:transform="translate({Math.min(imageWidth * (currentTime / (duration - 0.9)), imageWidth)}px, -2%)" | |
/> | |
{#if paused} | |
<img | |
class="play-button" | |
src="static/play.svg" | |
alt="Play button" | |
draggable="false" | |
transition:fade | |
style:width={imageHeight > 100 ? '20%' : '7.5%'} | |
/> | |
{/if} | |
</div> | |
<a href={$audioBlob} download={`${styles[$style]} Composition - AI Guru ft. Hugging Face.wav`} class="download" | |
>Download</a | |
> | |
</section> | |
<style> | |
section { | |
display: flex; | |
flex-direction: column; | |
position: relative; | |
border: 2px solid hsl(0 0% 80%); | |
border-radius: 0.375rem; | |
padding: 1rem; | |
} | |
.player { | |
position: absolute; | |
left: 50%; | |
transform: translateX(-50%); | |
cursor: pointer; | |
} | |
.notes { | |
width: min(100%, 512px); | |
margin: auto; | |
box-shadow: 0 0 5px 0.1px hsl(210, 10%, 20%); | |
} | |
audio { | |
width: 100%; | |
margin: 1rem auto; | |
} | |
.play-button { | |
position: absolute; | |
left: 50%; | |
top: 50%; | |
width: 20%; | |
aspect-ratio: 1 / 1; | |
transform: translate(-50%, -50%); | |
filter: drop-shadow(0 0 5px black); | |
pointer-events: none; | |
cursor: pointer; | |
} | |
.handle { | |
position: absolute; | |
left: 0; | |
top: 0; | |
height: 104%; | |
width: 0.2rem; | |
border-radius: 0.1rem; | |
background-color: white; | |
cursor: pointer; | |
transform: translate(0, -2%); | |
} | |
a.download { | |
display: block; | |
font-size: 1.2rem; | |
font-family: 'Lato', sans-serif; | |
font-weight: 700; | |
color: hsl(0 0% 97%); | |
background: transparent; | |
border: 3px solid hsl(0 0% 97%); | |
border-radius: 0.375rem; | |
padding: 0.5rem 1rem; | |
cursor: pointer; | |
margin: 1rem auto auto; | |
} | |
@media (min-width: 600px) { | |
section { | |
padding: 2rem; | |
} | |
} | |
</style> | |