improve: experience for mobile

This commit is contained in:
alikia2x (寒寒) 2024-11-23 07:20:07 +08:00
parent 2742ba43f2
commit f1ecabd523
Signed by: alikia2x
GPG Key ID: 56209E0CCD8420C6
9 changed files with 95 additions and 27 deletions

View File

@ -1,6 +1,6 @@
{
"name": "aquavox",
"version": "2.9.2",
"version": "2.9.3",
"private": false,
"module": "index.ts",
"type": "module",

View File

@ -11,7 +11,8 @@
{#if hasLyrics}
<img
class="absolute shadow-md select-none z-10 object-cover rounded-lg md:rounded-2xl max-md:h-20 max-xl:h-32 max-xl:top-6 md:max-h-[calc(94vh-20rem)] xl:w-auto max-w-[90%] xl:max-w-[37vw]
class="absolute shadow-md select-none z-10 object-cover rounded-lg md:rounded-2xl max-md:h-20 max-xl:h-32 max-xl:top-6
md:max-h-[calc(94vh-20rem)] max-md:w-20 xl:w-auto max-w-[90%] xl:max-w-[37vw]
md:bottom-[21rem] left-6 md:left-[calc(7vw-1rem)] lg:left-[calc(12vw-1rem)] xl:translate-x-[-50%] xl:left-[25vw]"
src={path}
width="1200"
@ -19,7 +20,8 @@
/>
{:else}
<img
class="absolute shadow-md select-none z-10 object-cover rounded-2xl max-h-[calc(94vh-18rem)] md:max-h-[calc(94vh-20rem)] xl:w-auto max-w-[90%] md:max-w-[75%] xl:max-w-[37vw]
class="absolute shadow-md select-none z-10 object-cover rounded-2xl max-h-[calc(94vh-18rem)]
md:max-h-[calc(94vh-20rem)] xl:w-auto max-w-[90%] md:max-w-[75%] xl:max-w-[37vw]
bottom-72 md:bottom-80 left-1/2 translate-x-[-50%]"
src={path}
alt="封面"

View File

@ -18,6 +18,9 @@
export let hasLyrics: boolean;
export let showInteractiveBox: boolean;
export let setShowingInteractiveBox: Function;
let progressBar: HTMLDivElement;
let volumeBar: HTMLDivElement;
let showInfoTop: boolean = false;
@ -25,6 +28,13 @@
let songInfoTopContainer: HTMLDivElement;
let songInfoTopContent: HTMLSpanElement;
let userAdjustingVolume = false;
let lastTouchClientX = 0;
setTimeout(() => {
if (screen.width < 728) {
setShowingInteractiveBox(false);
}
}, 3000);
const mql = window.matchMedia('(max-width: 1280px)');
@ -68,6 +78,30 @@
$: {
showInfoTop = mql.matches && hasLyrics;
}
window.addEventListener("mousemove", (event) => {
if ($userAdjustingProgress) {
adjustDisplayProgress(event.offsetX / progressBar.getBoundingClientRect().width);
}
});
window.addEventListener("mouseup", (event) => {
if ($userAdjustingProgress) {
userAdjustingProgress.set(false);
adjustProgress(event.offsetX / progressBar.getBoundingClientRect().width);
}
});
window.addEventListener("touchmove", (event) => {
if ($userAdjustingProgress) {
adjustDisplayProgress((event.touches[0].clientX - progressBar.getBoundingClientRect().left) / progressBar.getBoundingClientRect().width);
lastTouchClientX = event.touches[0].clientX;
}
});
window.addEventListener("touchend", (event) => {
if ($userAdjustingProgress) {
adjustProgress((lastTouchClientX - progressBar.getBoundingClientRect().left) / progressBar.getBoundingClientRect().width);
userAdjustingProgress.set(false);
}
});
</script>
{#if showInfoTop}
@ -78,11 +112,14 @@
{/if}
<div
class={'absolute select-none bottom-12 h-60 w-[86vw] left-[7vw] z-10 ' +
class={'absolute select-none bottom-12 h-60 w-[86vw] left-[7vw] duration-500 z-10 ' +
(hasLyrics
? 'lg:w-[76vw] lg:left-[12vw] xl:w-[37vw] xl:left-[7vw]'
: 'lg:w-[76vw] lg:left-[12vw] xl:w-[37vw] xl:left-[31.5vw]')}
: 'lg:w-[76vw] lg:left-[12vw] xl:w-[37vw] xl:left-[31.5vw]') + ' ' +
(showInteractiveBox ? 'opacity-100' : 'opacity-0')}
style={`z-index: ${showInteractiveBox ? "0" : "50"}`}
>
{#if !showInfoTop}
<div class="song-info">
<div class="song-info-regular {isInfoTopOverflowing ? 'animate' : ''}" bind:this={songInfoTopContainer}>
@ -95,6 +132,13 @@
</div>
{/if}
<div class="absolute w-full h-2/3 bottom-0" style={`z-index: ${showInteractiveBox ? "0" : "50"}`} on:click={() => {
setShowingInteractiveBox(true);
setTimeout(() => {
setShowingInteractiveBox(false);
}, 5000);
}}></div>
<div class="progress top-16">
<div class="time-indicator text-shadow-md time-current">
{formatDuration(progress)}
@ -107,23 +151,14 @@
class="progress-bar shadow-md"
on:keydown
on:keyup
on:click={(e) => {
progressBarOnClick(e);
}}
on:mousedown={() => {
userAdjustingProgress.set(true);
}}
on:mousemove={(e) => {
if ($userAdjustingProgress) {
adjustDisplayProgress(e.offsetX / progressBar.getBoundingClientRect().width);
}
}}
on:mouseup={(e) => {
const offsetX = e.offsetX;
progressBarOnClick(e);
// Q: why it needs delay?
// A: I do not know.
setTimeout(()=> {
userAdjustingProgress.set(false);
progressBarMouseUp(offsetX);
}, 50);
on:touchstart={() => {
userAdjustingProgress.set(true);
}}
role="slider"
tabindex="0"

View File

@ -16,10 +16,11 @@
document.body.style.overflow = 'hidden';
// Props
let { originalLyrics, progress, player } : {
let { originalLyrics, progress, player, showInteractiveBox } : {
originalLyrics: LyricData,
progress: number,
player: HTMLAudioElement | null
player: HTMLAudioElement | null,
showInteractiveBox: boolean
} = $props();
// States
@ -195,7 +196,7 @@
lastEventLyricIndex = currentLyricIndex;
lastEventProgress = progress;
if (!lyricChanged) return;
if (!lyricChanged || scrolling) return;
if (!lyricIndexDeltaTooBig && deltaInRange) {
console.log("Event: regular move");
console.log(new Date().getTime(), lastSeekForward);
@ -257,8 +258,11 @@
{#if originalLyrics && originalLyrics.scripts}
<div
class="absolute top-[6.5rem] md:top-36 xl:top-0 w-screen xl:w-[52vw] px-6 md:px-12
lg:px-[7.5rem] xl:left-[46vw] xl:px-[3vw] h-[calc(100vh-17rem)] xl:h-screen font-sans
lg:px-[7.5rem] xl:left-[46vw] xl:px-[3vw] xl:h-screen font-sans
text-left no-scrollbar z-[1] pt-16 overflow-hidden"
style={`mask: linear-gradient(0deg, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 1) 7%, rgba(0, 0, 0, 1) 95%,
rgba(0, 0, 0, 0) 100%);
height: ${showInteractiveBox ? "calc(100vh - 21rem)" : "calc(100vh - 7rem)"}`}
bind:this={lyricsContainer}
>
{#each lyricLines as lyric, i}

View File

@ -2,8 +2,12 @@
<html lang="en">
<head>
<meta charset="utf-8" />
<!-- <link rel="icon" href="%sveltekit.assets%/favicon.png" /> -->
<link rel="icon" href="/icon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="manifest" href="/manifest.json" />
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<link rel="apple-touch-icon" href="/icon.png">
<title>AquaVox</title>
%sveltekit.head%
</head>

View File

@ -62,7 +62,7 @@
{#each idList as id}
<!-- svelte-ignore a11y-click-events-have-key-events -->
<!-- svelte-ignore a11y-no-static-element-interactions -->
<div class="!no-underline !text-black dark:!text-white" onclick={() => goto(`/play/${id}`)}>
<div class="!no-underline !text-black dark:!text-white" onclick={() => location.href = (`/play/${id}`)}>
<li
class="relative my-4 p-4 duration-150 bg-zinc-200 hover:bg-zinc-300 dark:bg-zinc-700 dark:hover:bg-zinc-600 rounded-lg"
>

View File

@ -31,6 +31,7 @@
let hasLyrics: boolean;
const coverPath = writable('');
let mainInterval: ReturnType<typeof setInterval>;
let showInteractiveBox = true;
function setMediaSession() {
if ('mediaSession' in navigator === false) return;
@ -196,6 +197,10 @@
}
}
function setShowingInteractiveBox(showing: boolean) {
showInteractiveBox = showing;
}
$: {
clearInterval(mainInterval);
mainInterval = setInterval(() => {
@ -240,9 +245,11 @@
{adjustVolume}
{adjustDisplayProgress}
{hasLyrics}
{showInteractiveBox}
{setShowingInteractiveBox}
/>
<NewLyrics {originalLyrics} progress={currentProgress} player={audioPlayer}/>
<NewLyrics {originalLyrics} progress={currentProgress} player={audioPlayer} {showInteractiveBox} />
<audio
bind:this={audioPlayer}

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

View File

@ -0,0 +1,16 @@
{
"orientation": "portrait",
"name": "AquaVox",
"short_name": "AquaVox",
"start_url": "/",
"display": "standalone",
"background_color": "#000000",
"description": "A readable Hacker News app",
"icons": [
{
"src": "/icon.png",
"sizes": "192x192",
"type": "image/png"
}
]
}