From 647628b4f8dd864002f35903183ebffef9f396c1 Mon Sep 17 00:00:00 2001 From: Chris Amico Date: Wed, 15 May 2024 14:29:20 -0400 Subject: [PATCH] Pagination mostly working --- src/lib/components/documents/PDFPage.svelte | 16 ++++- src/lib/components/documents/Page.svelte | 70 ++++++++++++++++++- src/lib/components/documents/TextPage.svelte | 2 +- src/routes/documents/[id]-[slug]/+layout.ts | 4 +- src/routes/documents/[id]-[slug]/+page.svelte | 41 +++++++---- 5 files changed, 112 insertions(+), 21 deletions(-) diff --git a/src/lib/components/documents/PDFPage.svelte b/src/lib/components/documents/PDFPage.svelte index ac6516efb..58404df1a 100644 --- a/src/lib/components/documents/PDFPage.svelte +++ b/src/lib/components/documents/PDFPage.svelte @@ -28,6 +28,9 @@ Selectable text can be rendered in one of two ways: // keep track of this to avoid overlapping renders let renderTask; + // visibility, for loading optimization + let visible: boolean = false; + $: aspect = height / width; $: orientation = height > width ? "vertical" : "horizontal"; $: numericScale = fitPage(width, height, container, scale); @@ -43,8 +46,10 @@ Selectable text can be rendered in one of two ways: /** * Return a numeric scale based on intrinsic page size and container size - * @param page + * @param width Original document width + * @param height Original document height * @param container + * @param scale */ function fitPage( width: number, @@ -136,10 +141,17 @@ Selectable text can be rendered in one of two ways: - +
import type { ViewerMode } from "@/lib/api/types"; + import { createEventDispatcher, getContext, onMount } from "svelte"; + import type { Writable } from "svelte/store"; import { _ } from "svelte-i18n"; import { pageHashUrl } from "$lib/api/documents"; @@ -9,19 +11,81 @@ export let mode: ViewerMode = "document"; export let wide = false; export let tall = false; + export let track: boolean | "once" = false; + + const currentPage: Writable = getContext("currentPage"); + const dispatch = createEventDispatcher(); + + let io: IntersectionObserver; + let container: HTMLElement; + let heading: HTMLElement; + let visible: boolean; $: id = pageHashUrl(page_number).replace("#", ""); $: href = `?mode=${mode}` + pageHashUrl(page_number); + + function watch(el: HTMLElement, once = false): IntersectionObserver { + const io = new IntersectionObserver( + (entries, observer) => { + entries.forEach((entry) => { + if (entry.isIntersecting) { + // update state + visible = true; + dispatch("visible"); + + if (once) { + observer.unobserve(el); + } + } else { + visible = false; + } + + const { boundingClientRect, rootBounds } = entry; + + if ( + boundingClientRect.top > rootBounds.top && + boundingClientRect.top < rootBounds.height / 2 + ) { + $currentPage = page_number; + } + }); + }, + { + // [0, 0.1, 0.2, ...] + threshold: Array(11) + .fill(undefined) + .map((n, i) => i / 10), + }, + ); + + io.observe(el); + + return io; + } + + function unwatch(io: IntersectionObserver, el: HTMLElement) { + io?.unobserve(el); + } + + onMount(() => { + if (track) { + io = watch(container, track === "once"); + } + + return () => { + unwatch(io, container); + }; + }); -
-

+