aboutsummaryrefslogtreecommitdiffhomepage
path: root/services/nuldoc/public/slides/slide.js
blob: a90fda7af1138293a2f63db3c0593a42da394eec (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
import { getDocument, GlobalWorkerOptions } from "./pdf.min.mjs";

async function init() {
  GlobalWorkerOptions.workerSrc = "/pdf.worker.min.mjs";

  const canvas = document.getElementById("slide");
  const ctx = canvas.getContext("2d");
  const url = canvas.dataset.slideLink;

  let pageNum = 1;
  let doc = null;
  let renderTask = null;
  let renderToken = 0;

  const backCanvas = document.createElement("canvas");
  const backCtx = backCanvas.getContext("2d");

  const renderPage = async (num) => {
    const token = ++renderToken;
    if (renderTask !== null) {
      renderTask.cancel();
      renderTask = null;
    }

    const page = await doc.getPage(num);
    if (token !== renderToken) return;

    const baseViewport = page.getViewport({ scale: 1.0 });
    const containerWidth = canvas.parentElement.clientWidth;
    const scale = containerWidth / baseViewport.width;
    const viewport = page.getViewport({ scale });

    const outputScale = globalThis.devicePixelRatio || 1;
    const cssWidth = Math.floor(viewport.width);
    const cssHeight = Math.floor(viewport.height);
    backCanvas.width = Math.floor(viewport.width * outputScale);
    backCanvas.height = Math.floor(viewport.height * outputScale);

    const transform = outputScale !== 1
      ? [outputScale, 0, 0, outputScale, 0, 0]
      : null;

    // TODO: error handling
    const task = page.render({
      canvasContext: backCtx,
      viewport,
      transform,
    });
    renderTask = task;
    try {
      await task.promise;
    } catch (e) {
      if (e?.name === "RenderingCancelledException") return;
      throw e;
    } finally {
      if (renderTask === task) renderTask = null;
    }
    if (token !== renderToken) return;

    if (canvas.width !== backCanvas.width || canvas.height !== backCanvas.height) {
      canvas.width = backCanvas.width;
      canvas.height = backCanvas.height;
      canvas.style.width = cssWidth + "px";
      canvas.style.height = cssHeight + "px";
    }
    ctx.drawImage(backCanvas, 0, 0);
  };

  const prev = document.getElementById("prev");
  prev.addEventListener("click", () => {
    if (pageNum <= 1) return;
    pageNum--;
    renderPage(pageNum);
  });
  const next = document.getElementById("next");
  next.addEventListener("click", () => {
    if (pageNum >= doc.numPages) return;
    pageNum++;
    renderPage(pageNum);
  });

  document.addEventListener("keydown", (e) => {
    if (e.key === "ArrowLeft" || e.key === "h") {
      if (pageNum <= 1) return;
      pageNum--;
      renderPage(pageNum);
    } else if (e.key === "ArrowRight" || e.key === "l") {
      if (pageNum >= doc.numPages) return;
      pageNum++;
      renderPage(pageNum);
    }
  });

  // TODO: error handling
  doc = await getDocument(url).promise;
  renderPage(pageNum);
}

document.addEventListener("DOMContentLoaded", init);