feat: asynchronous album covers

This commit is contained in:
not-nullptr 2024-11-14 19:59:00 +00:00
parent 1328cdcb05
commit aac176816b
5 changed files with 87 additions and 47 deletions

View File

@ -47,7 +47,7 @@
class="absolute w-full h-full" class="absolute w-full h-full"
style=" style="
z-index: {index + 2}; z-index: {index + 2};
backdrop-filter: blur({blurIntensity}px); backdrop-filter: blur( calc({blurIntensity}px * var(--blur-amount, 1)) );
mask: {mask}; mask: {mask};
" "
></div> ></div>
@ -63,6 +63,6 @@
></div> ></div>
<div <div
class="absolute top-0 left-0 w-full h-full z-50" class="absolute top-0 left-0 w-full h-full z-50"
style="background: linear-gradient({getGradientDirection()}, transparent 0%, {fadeTo} 100%);" style="background: linear-gradient({getGradientDirection()}, transparent 0%, {fadeTo} 100%); opacity: var(--blur-amount, 1);"
></div> ></div>
</div> </div>

View File

@ -16,18 +16,21 @@ export class VertFile {
public to = $state(""); public to = $state("");
public blobUrl = $state<string>();
public converter: Converter | null = null; public converter: Converter | null = null;
constructor( constructor(
public readonly file: File, public readonly file: File,
to: string, to: string,
converter?: Converter, converter?: Converter,
public readonly blobUrl?: string, blobUrl?: string,
) { ) {
this.to = to; this.to = to;
this.converter = converter ?? null; this.converter = converter ?? null;
this.convert = this.convert.bind(this); this.convert = this.convert.bind(this);
this.download = this.download.bind(this); this.download = this.download.bind(this);
this.blobUrl = blobUrl;
} }
public async convert() { public async convert() {

View File

@ -11,8 +11,10 @@ export const load = ({ url, request, cookies }) => {
const { pathname } = url; const { pathname } = url;
const ua = request.headers.get("user-agent"); const ua = request.headers.get("user-agent");
const isMobile = /mobile/i.test(ua || ""); const isMobile = /mobile/i.test(ua || "");
const isFirefox = /firefox/i.test(ua || "");
return { return {
pathname, pathname,
isMobile, isMobile,
isFirefox,
}; };
}; };

View File

@ -63,49 +63,35 @@
img.onerror = async () => { img.onerror = async () => {
// resolve(new VertFile(f, to, converter)); // resolve(new VertFile(f, to, converter));
const reader = new FileReader(); const reader = new FileReader();
const file = new VertFile(f, to, converter);
resolve(file);
reader.onload = async (e) => { reader.onload = async (e) => {
try { const tags = await new Promise<TagType>(
const tags = await new Promise<TagType>( (resolve, reject) => {
(resolve, reject) => { jsmediatags.read(
jsmediatags.read( new Blob([
new Blob([ new Uint8Array(
new Uint8Array( e.target?.result as ArrayBuffer,
e.target ),
?.result as ArrayBuffer, ]),
), {
]), onSuccess: (tag) => resolve(tag),
{ onError: (error) => reject(error),
onSuccess: (tag) => },
resolve(tag), );
onError: (error) => },
reject(error), );
}, const picture = tags.tags.picture;
); if (!picture) return;
},
); const blob = new Blob(
console.log(tags); [new Uint8Array(picture.data)],
const picture = tags.tags.picture; {
if (!picture) { type: picture.format,
resolve(new VertFile(f, to, converter)); },
return; );
} const url = URL.createObjectURL(blob);
const blob = new Blob( file.blobUrl = url;
[new Uint8Array(picture.data)],
{
type: picture.format,
},
);
resolve(
new VertFile(
f,
to,
converter,
URL.createObjectURL(blob),
),
);
} catch {
resolve(new VertFile(f, to, converter));
}
}; };
reader.readAsArrayBuffer(f); reader.readAsArrayBuffer(f);
}; };

View File

@ -13,6 +13,13 @@
import { ArrowRight, Disc2Icon, FileAudioIcon, XIcon } from "lucide-svelte"; import { ArrowRight, Disc2Icon, FileAudioIcon, XIcon } from "lucide-svelte";
import { onMount } from "svelte"; import { onMount } from "svelte";
import { quintOut } from "svelte/easing"; import { quintOut } from "svelte/easing";
import {
fade,
type EasingFunction,
type TransitionConfig,
} from "svelte/transition";
const { data } = $props();
const reversedFiles = $derived(files.files.slice().reverse()); const reversedFiles = $derived(files.files.slice().reverse());
@ -131,6 +138,33 @@
files.files = []; files.files = [];
goto("/"); goto("/");
}; };
export const progBlur = (
_: HTMLElement,
config:
| Partial<{
duration: number;
easing: EasingFunction;
}>
| undefined,
dir: {
direction: "in" | "out" | "both";
},
): TransitionConfig => {
const prefersReducedMotion = window.matchMedia(
"(prefers-reduced-motion: reduce)",
).matches;
if (!config) config = {};
if (!config.duration) config.duration = 300;
if (!config.easing) config.easing = quintOut;
return {
duration: prefersReducedMotion ? 0 : config?.duration || 300,
css: (t) => {
return "--blur-amount: " + (dir.direction !== "in" ? t : 1 - t);
},
easing: config?.easing,
};
};
</script> </script>
<svelte:head> <svelte:head>
@ -279,7 +313,9 @@
)} )}
style="background-color: color-mix(in srgb, var(--{file.result style="background-color: color-mix(in srgb, var(--{file.result
? 'accent-bg' ? 'accent-bg'
: 'bg'}), transparent var(--transparency)); backdrop-filter: blur(18px);" : 'bg'}), transparent var(--transparency)); backdrop-filter: blur({data.isFirefox
? 0
: 18}px);"
> >
<div <div
class="w-full grid grid-cols-1 grid-rows-1" class="w-full grid grid-cols-1 grid-rows-1"
@ -418,10 +454,23 @@
{#if file.blobUrl} {#if file.blobUrl}
<div <div
class="bg-cover bg-center w-full h-full" class="bg-cover bg-center w-full h-full"
style="background-image: url({file.blobUrl});" style="background-image: url({file.blobUrl})"
in:blur={{
blurMultiplier: 24,
scale: {
start: 1.1,
end: 1,
},
duration,
easing: quintOut,
}}
></div> ></div>
<div <div
class="absolute left-0 top-0 pt-[50px] h-full w-full" class="absolute left-0 top-0 pt-[50px] h-full w-full"
transition:progBlur={{
duration,
easing: quintOut,
}}
> >
<ProgressiveBlur <ProgressiveBlur
direction="bottom" direction="bottom"