musica / source /ui /src /lib /Notes.svelte
MMVos's picture
try
4a9e8e5
<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>