feature: full play function

This commit is contained in:
Alikia2x 2024-05-11 14:08:51 +08:00
parent 10e7341a73
commit 6e50a7bdc3
4 changed files with 168 additions and 38 deletions

View File

@ -8,4 +8,24 @@ h1 {
h2 {
@apply text-3xl font-medium leading-[3rem];
}
}
.text-shadow {
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.text-shadow-md {
text-shadow:
0 4px 8px rgba(0, 0, 0, 0.12),
0 2px 4px rgba(0, 0, 0, 0.08);
}
.text-shadow-lg {
text-shadow:
0 15px 30px rgba(0, 0, 0, 0.11),
0 5px 15px rgba(0, 0, 0, 0.08);
}
.text-shadow-none {
text-shadow: none;
}

View File

@ -1,18 +1,15 @@
<script lang="ts">
import localforage from '$lib/storage';
export let coverId;
import type { Writable } from 'svelte/store';
export let coverPath: Writable<string>;
let path: string = "";
let coverPath = '';
localforage.getItem(`${coverId}-cover`, function (err, file) {
if (file) {
const path = URL.createObjectURL(file as File);
coverPath = path;
}
coverPath.subscribe((p) => {
if (p) path = p;
});
</script>
<img class="cover" src={coverPath} alt="封面" />
<img class="cover" src={path} alt="封面" />
<style>
.cover {

View File

@ -1,38 +1,39 @@
<script lang="ts">
import formatDuration from "$lib/formatDuration";
import formatDuration from '$lib/formatDuration';
export let name: string;
export let singer: string = "";
export let singer: string = '';
export let duration: number = 0;
export let progress: number = 0;
export let paused: boolean;
export let clickPlay: Function;
</script>
<div class="interactive-box">
<div class="song-info">
<span class="song-name">{name}</span><br/>
<span class="song-name text-shadow">{name}</span><br />
<span class="song-author">{singer}</span>
</div>
<div class="progress">
<div class="time-indicator time-current">{formatDuration(progress)}</div>
<div id="progress-bar" class="progress-bar">
<div class="bar" style={`width: ${progress/(duration+0.001)*100}%;`}></div>
<div class="time-indicator text-shadow-md time-current">{formatDuration(progress)}</div>
<div class="progress-bar shadow-md">
<div class="bar" style={`width: ${(progress / (duration + 0.001)) * 100}%;`}></div>
</div>
<div class="time-indicator time-total">{formatDuration(duration)}</div>
<div class="time-indicator text-shadow-md time-total">{formatDuration(duration)}</div>
</div>
<div class="controls">
<div id="previous" class="control-btn previous">
<div style="filter: drop-shadow( 0 4px 8px rgba(0, 0, 0, 0.12) );" class="control-btn previous">
<img class="control-img switch-song-img" src="/previous.svg" alt="上一曲" />
</div>
<div id="play" class="control-btn play-btn">
<img id="play-img" class="control-img" src="/play.svg" alt="暂停或播放" />
<div style="filter: drop-shadow( 0 4px 8px rgba(0, 0, 0, 0.12) );" class="control-btn play-btn" on:click={() => clickPlay()}>
<img class="control-img" src={paused ? '/play.svg' : '/pause.svg'} alt="暂停或播放" />
</div>
<div id="right" class="control-btn next">
<div style="filter: drop-shadow( 0 4px 8px rgba(0, 0, 0, 0.12) );" class="control-btn next">
<img class="control-img switch-song-img" src="/next.svg" alt="下一曲" />
</div>
</div>
</div>
<style>
.controls {
position: absolute;
@ -110,13 +111,13 @@
position: relative;
width: 100%;
height: 0.3rem;
background-color: rgba(255, 255, 255, 0.5);
background-color: rgba(255, 255, 255, .5);
border-radius: 0.5rem;
}
.bar {
background-color: white;
position: absolute;
content: "";
content: '';
height: 0.3rem;
display: inline-block;
border-radius: 1.5rem;
@ -127,7 +128,6 @@
font-size: 1rem;
line-height: 1rem;
color: rgba(255, 255, 255, 0.8);
font-family: sans-serif;
display: inline-block;
top: 0.2rem;
}

View File

@ -6,25 +6,138 @@
import InteractiveBox from '$lib/components/interactiveBox.svelte';
import extractFileName from '$lib/extractFileName';
import localforage from 'localforage';
import { writable } from 'svelte/store';
const audioId = $page.params.id;
let name = "";
let singer = "";
let audioPlayer: HTMLAudioElement;
let name = '';
let singer = '';
let duration = 0;
let currentProgress = 0;
let audioFile: File;
let paused: boolean = true;
let launched = false;
let prepared: string[] = [];
const coverPath = writable('');
let mainInterval: ReturnType<typeof setInterval>;
localforage.getItem(`${audioId}-file`, function (err, file) {
if (file) {
const f = file as File;
name = extractFileName(f.name);
function setMediaSession() {
if ('mediaSession' in navigator === false) return;
const ms = navigator.mediaSession;
ms.metadata = new MediaMetadata({
title: name,
artist: singer,
artwork: [
{
src: $coverPath
}
]
});
ms.setActionHandler('play', function () {
audioPlayer.play();
paused = false;
});
ms.setActionHandler('pause', function () {
audioPlayer.pause();
paused = true;
});
ms.setActionHandler('seekbackward', function () {
if (audioPlayer.currentTime > 4) {
audioPlayer.currentTime = 0;
}
});
ms.setActionHandler('previoustrack', function () {
if (audioPlayer.currentTime > 4) {
audioPlayer.currentTime = 0;
}
});
}
function readDB() {
getAudioIDMetadata(audioId, (metadata: any) => {
duration = metadata.format.duration ? metadata.format.duration : 0;
prepared.push('duration');
});
localforage.getItem(`${audioId}-cover`, function (err, file) {
if (file) {
const path = URL.createObjectURL(file as File);
coverPath.set(path);
}
prepared.push('cover');
});
localforage.getItem(`${audioId}-file`, function (err, file) {
if (file) {
const f = file as File;
audioFile = f;
audioPlayer.src = URL.createObjectURL(audioFile);
name = extractFileName(f.name);
prepared.push('name');
prepared.push('file');
}
});
}
function playAudio() {
if (audioPlayer.duration) {
duration = audioPlayer.duration;
}
});
getAudioIDMetadata(audioId, (metadata: any) => {
console.log(metadata);
duration = metadata.format.duration ? metadata.format.duration : 0;
})
audioPlayer.paused ? audioPlayer.play() : audioPlayer.pause();
paused = audioPlayer.paused;
setMediaSession();
}
$: {
if (!launched) {
console.log('launch?');
const requirements = ['name', 'file', 'cover'];
let flag = true;
for (const r of requirements) {
if (!prepared.includes(r)) {
flag = false;
}
}
console.log(prepared, flag);
if (flag) {
launched = true;
console.log('launch!');
setMediaSession();
audioPlayer.play();
}
}
}
$: {
clearInterval(mainInterval);
mainInterval = setInterval(() => {
if (audioPlayer !== null && audioPlayer.currentTime !== undefined) {
currentProgress = audioPlayer.currentTime;
}
}, 500);
}
$: {
if (audioPlayer) {
paused = audioPlayer.paused;
if (audioPlayer.ended) {
paused = true;
audioPlayer.pause();
}
}
}
readDB();
</script>
<Background coverId={audioId} />
<Cover coverId={audioId} />
<InteractiveBox name={name} singer={singer} duration={duration} progress={currentProgress}/>
<Cover {coverPath} />
<InteractiveBox
{name}
{singer}
{duration}
progress={currentProgress}
clickPlay={playAudio}
{paused}
/>
<audio bind:this={audioPlayer} controls style="display: none"></audio>