This repository has no description
1<!DOCTYPE html>
2<html lang="en">
3 <head>
4 <meta charset="UTF-8" />
5 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6 <title>Shapemaker preview</title>
7 <script type="importmap">
8 {
9 "imports": {
10 "debounce": "https://unpkg.com/throttle-debounce@5.0.2/esm/index.js"
11 }
12 }
13 </script>
14 </head>
15 <body>
16 <div id="frame_monitor"></div>
17 <div class="controls">
18 <button style="font-family: monospace" id="play_pause">|></button>
19 <input type="range" value="0" id="requested_frame" min="1" max="%frames_count%" />
20 <code id="requested_frame_number"></code>
21 </div>
22 <script type="module">
23 import { debounce } from "debounce"
24
25 const FPS =
26 parseInt(new URLSearchParams(window.location.search).get("fps")) || 30
27 const cache = new Map()
28
29 let playLoop
30 play_pause.onclick = () => {
31 if (playLoop) {
32 clearInterval(playLoop)
33 playLoop = null
34 play_pause.innerText = "|>"
35 } else {
36 play_pause.innerText = "||"
37 playLoop = setInterval(() => {
38 requested_frame.value = requested_frame.valueAsNumber + 1
39 render(requested_frame.valueAsNumber)
40 }, 1000 / FPS)
41 }
42 }
43
44 requested_frame.oninput = debounce(200, ({ target }) => {
45 render(target.valueAsNumber)
46 })
47
48 function render(frameNo) {
49 requested_frame_number.innerText = `(${frameNo})`
50
51 if (cache.has(frameNo)) {
52 requested_frame_number.innerText = frameNo
53 requested_frame_number.style.color = "magenta"
54 frame_monitor.innerHTML = cache.get(frameNo)
55 frame_monitor.style.color = "initial"
56 frame_monitor.style.opacity = "1"
57 return
58 }
59
60 frame_monitor.style.opacity = "0.5"
61
62 const start = performance.now()
63
64 fetch(`/frame/${frameNo}.svg`)
65 .then((response) =>
66 response.text().then((text) => ({
67 renderTime: Math.round(performance.now() - start),
68 ok: response.ok,
69 text,
70 }))
71 )
72 .then(({ ok, text, renderTime }) => {
73 if (ok) cache.set(frameNo, text)
74
75 if (frameNo !== requested_frame.valueAsNumber) return
76
77 requested_frame_number.innerText = `${frameNo} (in ${renderTime}ms)`
78 requested_frame_number.style.color = "initial"
79 frame_monitor.style.opacity = "1"
80
81 if (ok) {
82 frame_monitor.innerHTML = text
83 frame_monitor.style.color = "initial"
84 } else {
85 frame_monitor.innerText = text
86 frame_monitor.style.color = "red"
87 }
88 })
89 }
90 </script>
91 <style>
92 #frame_monitor {
93 width: 100vw;
94 height: calc(9 / 16 * 100vw);
95
96 svg {
97 width: 100%;
98 height: 100%;
99 }
100 }
101
102 #requested_frame_number {
103 width: 20ch;
104 }
105
106 .controls {
107 display: flex;
108 align-items: center;
109 justify-content: center;
110 gap: 1em;
111 margin-top: 3rem;
112 }
113
114 body {
115 margin: 0;
116 }
117 </style>
118 </body>
119</html>