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> | |