Papure/src/lib/components/PdfBackground.svelte
2026-09-26 19:13:02 +02:00

71 lines
1.7 KiB
Svelte

<script lang="ts">
import { loadSource, renderPage } from '$lib/pdf/render';
import { vault } from '$lib/storage/vault.svelte';
import type { Origin } from '$lib/model/types';
interface Props {
origin: Origin;
width: number;
height: number;
/** Current zoom (screen px per PDF point). */
scale: number;
}
let { origin, width, height, scale }: Props = $props();
let canvas: HTMLCanvasElement;
let failed = $state(false);
// Render at a stepped resolution so zooming doesn't re-render constantly.
const STEPS = [0.25, 0.5, 1, 1.5, 2, 3, 4];
const MAX_PIXELS = 12_000_000;
const resolution = $derived.by(() => {
const want = scale * (window.devicePixelRatio || 1);
const step = STEPS.find((s) => s >= want) ?? STEPS[STEPS.length - 1];
const cap = Math.sqrt(MAX_PIXELS / (width * height));
return Math.min(step, cap);
});
$effect(() => {
const res = resolution;
const { sourceId, pageIndex } = origin;
let job: ReturnType<typeof renderPage> | null = null;
let cancelled = false;
const timer = setTimeout(async () => {
try {
const doc = await loadSource(sourceId, () => vault.getSource(sourceId));
if (cancelled) return;
job = renderPage(doc, pageIndex, canvas, res);
failed = false;
} catch {
failed = true;
}
}, 120);
return () => {
cancelled = true;
clearTimeout(timer);
job?.cancel();
};
});
</script>
<canvas bind:this={canvas} class="bg" class:failed></canvas>
{#if failed}
<div class="missing">Source PDF “{origin.name}” is not available</div>
{/if}
<style>
.bg {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
}
.missing {
position: absolute;
inset: 0;
display: grid;
place-items: center;
color: #999;
font-size: 12px;
}
</style>