Skip to content

Commit 91630df

Browse files
author
Oussama
committed
perf: defer narrative scene loading
1 parent 3f74ad8 commit 91630df

1 file changed

Lines changed: 89 additions & 29 deletions

File tree

website/src/App.svelte

Lines changed: 89 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,6 @@
44
import FilterStoreDebug from './lib/components/FilterStoreDebug.svelte';
55
import Tooltip from './lib/components/Tooltip.svelte';
66
import Scene0Hero from './lib/scenes/Scene0Hero.svelte';
7-
import Scene1Mediums from './lib/scenes/Scene1Mediums.svelte';
8-
import Scene2Globe from './lib/scenes/Scene2Globe.svelte';
9-
import Scene3Gender from './lib/scenes/Scene3Gender.svelte';
10-
import Scene4Explorer from './lib/scenes/Scene4Explorer.svelte';
11-
import Scene5Quiz from './lib/scenes/Scene5Quiz.svelte';
12-
import Scene6Footer from './lib/scenes/Scene6Footer.svelte';
137
import { initFilterHashSync } from './lib/stores/filters.js';
148
159
let summary = $state(null);
@@ -21,16 +15,36 @@
2115
let countrySummary = $state([]);
2216
let genderByCountry = $state([]);
2317
let mediumBreakdown = $state([]);
18+
let sceneModules = $state(null);
2419
let showFilterDebug = $state(false);
2520
let showComponentDemo = $state(false);
2621
22+
function deferStoryLoad(callback) {
23+
let done = false;
24+
const events = ['wheel', 'touchstart', 'keydown', 'pointerdown'];
25+
const cleanup = () => events.forEach((event) => window.removeEventListener(event, run));
26+
const run = () => {
27+
if (done) return;
28+
done = true;
29+
window.clearTimeout(timeoutId);
30+
cleanup();
31+
window.setTimeout(callback, 0);
32+
};
33+
const timeoutId = window.setTimeout(run, 5500);
34+
events.forEach((event) => window.addEventListener(event, run, { once: true, passive: true }));
35+
return () => {
36+
done = true;
37+
window.clearTimeout(timeoutId);
38+
cleanup();
39+
};
40+
}
41+
2742
onMount(() => {
2843
const stopHashSync = initFilterHashSync();
2944
showFilterDebug = import.meta.env.DEV && window.location.pathname.endsWith('/filters');
3045
showComponentDemo = import.meta.env.DEV && window.location.pathname.endsWith('/components');
3146
let cancelled = false;
3247
const files = [
33-
'summary',
3448
'gender_by_decade',
3549
'timeline',
3650
'medium_totals',
@@ -41,24 +55,47 @@
4155
'medium_breakdown',
4256
];
4357
44-
Promise.all(files.map((file) => fetch(`./data/${file}.json`).then((response) => response.json()))).then(
45-
(results) => {
46-
if (cancelled) return;
47-
[
48-
summary,
49-
genderByDecade,
50-
timelineData,
51-
mediumTotals,
52-
countryByDecade,
53-
countrySummary,
54-
genderByCountry,
55-
genderByDepartment,
56-
mediumBreakdown,
57-
] = results;
58-
},
59-
);
58+
fetch('./data/summary.json')
59+
.then((response) => response.json())
60+
.then((result) => {
61+
if (!cancelled) summary = result;
62+
});
63+
64+
const cancelSceneLoad = deferStoryLoad(async () => {
65+
const [modules, results] = await Promise.all([
66+
Promise.all([
67+
import('./lib/scenes/Scene1Mediums.svelte'),
68+
import('./lib/scenes/Scene2Globe.svelte'),
69+
import('./lib/scenes/Scene3Gender.svelte'),
70+
import('./lib/scenes/Scene4Explorer.svelte'),
71+
import('./lib/scenes/Scene5Quiz.svelte'),
72+
import('./lib/scenes/Scene6Footer.svelte'),
73+
]),
74+
Promise.all(files.map((file) => fetch(`./data/${file}.json`).then((response) => response.json()))),
75+
]);
76+
if (cancelled) return;
77+
sceneModules = {
78+
Scene1Mediums: modules[0].default,
79+
Scene2Globe: modules[1].default,
80+
Scene3Gender: modules[2].default,
81+
Scene4Explorer: modules[3].default,
82+
Scene5Quiz: modules[4].default,
83+
Scene6Footer: modules[5].default,
84+
};
85+
[
86+
genderByDecade,
87+
timelineData,
88+
mediumTotals,
89+
countryByDecade,
90+
countrySummary,
91+
genderByCountry,
92+
genderByDepartment,
93+
mediumBreakdown,
94+
] = results;
95+
});
6096
6197
return () => {
98+
cancelSceneLoad();
6299
cancelled = true;
63100
stopHashSync();
64101
};
@@ -76,13 +113,27 @@
76113
{:else if summary}
77114
<main>
78115
<Scene0Hero {summary} />
79-
<Scene1Mediums data={mediumTotals} />
80-
<Scene2Globe {countryByDecade} {countrySummary} timeline={timelineData} />
81-
<Scene3Gender {genderByDecade} {genderByDepartment} {genderByCountry} {countrySummary} timeline={timelineData} />
82-
<Scene4Explorer data={mediumBreakdown} {countrySummary} timeline={timelineData} />
83-
<Scene5Quiz />
116+
{#if sceneModules}
117+
{@const Scene1 = sceneModules.Scene1Mediums}
118+
{@const Scene2 = sceneModules.Scene2Globe}
119+
{@const Scene3 = sceneModules.Scene3Gender}
120+
{@const Scene4 = sceneModules.Scene4Explorer}
121+
{@const Scene5 = sceneModules.Scene5Quiz}
122+
<Scene1 data={mediumTotals} />
123+
<Scene2 {countryByDecade} {countrySummary} timeline={timelineData} />
124+
<Scene3 {genderByDecade} {genderByDepartment} {genderByCountry} {countrySummary} timeline={timelineData} />
125+
<Scene4 data={mediumBreakdown} {countrySummary} timeline={timelineData} />
126+
<Scene5 />
127+
{:else}
128+
<section class="story-loading" aria-live="polite">
129+
<p>Preparing the story...</p>
130+
</section>
131+
{/if}
84132
</main>
85-
<Scene6Footer />
133+
{#if sceneModules}
134+
{@const Scene6 = sceneModules.Scene6Footer}
135+
<Scene6 />
136+
{/if}
86137
{:else}
87138
<div class="loading">
88139
<div class="loading-spinner"></div>
@@ -109,6 +160,15 @@
109160
animation: spin 800ms linear infinite;
110161
}
111162
163+
.story-loading {
164+
min-height: 40vh;
165+
display: grid;
166+
place-items: center;
167+
color: var(--fg-on-light-mute);
168+
background: var(--bg-light);
169+
font-family: var(--font-ui);
170+
}
171+
112172
@keyframes spin {
113173
to {
114174
rotate: 360deg;

0 commit comments

Comments
 (0)