Lazy-load content images while preserving screenshot readiness
Some checks failed
Checks / checks (push) Has been cancelled

This commit is contained in:
Matt Isenhower
2026-09-13 13:31:43 -07:00
parent cf7d28028b
commit 51761711cc
10 changed files with 33 additions and 23 deletions

View File

@@ -19,6 +19,11 @@ export async function markScreenshotReady({
await nextTick();
await document.fonts?.ready;
// Screenshots capture the whole layout, including images outside the viewport.
for (let image of document.images)
image.loading = 'eager';
await Promise.allSettled([...document.images].map(image => image.decode()));
await new Promise(resolve => requestAnimationFrame(resolve));
await new Promise(resolve => requestAnimationFrame(resolve));

View File

@@ -507,12 +507,16 @@ body.has-modal #main {
}
.stage-image {
background-size: contain;
background-repeat: no-repeat;
border-radius: 10px;
position: relative;
> figure > img {
object-fit: contain;
object-position: top;
border-radius: 10px;
}
.stage-title {
position: absolute;
right: 3px;

View File

@@ -2,13 +2,13 @@
<div class="merchandise-box font-splatoon2" :class="merchandise.kind">
<div class="brand">
<div class="image is-32x32">
<img :src="merchandise.gear.brand.image | localSplatNetImageUrl" :title="brandName" />
<img loading="lazy" :src="merchandise.gear.brand.image | localSplatNetImageUrl" :title="brandName" />
</div>
</div>
<div class="skills">
<div class="main skill-img-bg" v-if="merchandise.skill">
<img :src="merchandise.skill.image | localSplatNetImageUrl" :title="skillName" />
<img loading="lazy" :src="merchandise.skill.image | localSplatNetImageUrl" :title="skillName" />
</div>
<div class="sub" v-for="i in merchandise.gear.rarity + 1" :key="i">
<img src="~@/web/assets/img/blank-skill-slot.png" />
@@ -21,7 +21,7 @@
<div class="gear-image">
<div class="image is-square">
<img :src="merchandise.gear.image | localSplatNetImageUrl" />
<img loading="lazy" :src="merchandise.gear.image | localSplatNetImageUrl" />
</div>
</div>
@@ -36,7 +36,7 @@
<div class="level-left">
<div class="level-item">
<div class="skill-img-bg strikethrough">
<img :src="merchandise.original_gear.skill.image | localSplatNetImageUrl" :title="originalSkillName" />
<img loading="lazy" :src="merchandise.original_gear.skill.image | localSplatNetImageUrl" :title="originalSkillName" />
</div>
<div class="sub" v-for="i in merchandise.original_gear.rarity + 1" :key="i">
<img src="~@/web/assets/img/blank-skill-slot.png" />
@@ -58,7 +58,7 @@
<div class="is-size-7">{{ $t('gear.frequent_ability') }}</div>
<div>
<div class="skill-img-bg">
<img :src="merchandise.gear.brand.frequent_skill.image | localSplatNetImageUrl" :title="frequentSkillName" />
<img loading="lazy" :src="merchandise.gear.brand.frequent_skill.image | localSplatNetImageUrl" :title="frequentSkillName" />
</div>
{{ brandName }}
</div>

View File

@@ -9,19 +9,19 @@
<div class="weapon-special-sub">
<div class="image-wrapper" :title="specialName">
<div class="image is-24x24">
<img :src="weapon.special.image_a | localSplatNetImageUrl" />
<img loading="lazy" :src="weapon.special.image_a | localSplatNetImageUrl" />
</div>
</div>
<div class="image-wrapper" :title="subName">
<div class="image is-24x24">
<img :src="weapon.sub.image_a | localSplatNetImageUrl" />
<img loading="lazy" :src="weapon.sub.image_a | localSplatNetImageUrl" />
</div>
</div>
</div>
<div class="image is-square">
<img :src="weapon.image | localSplatNetImageUrl" />
<img loading="lazy" :src="weapon.image | localSplatNetImageUrl" />
</div>
<div class="weapon-name has-text-centered is-size-5">

View File

@@ -27,7 +27,7 @@
</div>
<div class="salmon-run-gear-image hand" @click="gearDialogOpen = true" :title="rewardGearName">
<div class="image is-32x32">
<img :src="rewardGear.image | localSplatNetImageUrl" />
<img loading="lazy" :src="rewardGear.image | localSplatNetImageUrl" />
</div>
</div>
</template>

View File

@@ -17,6 +17,7 @@
<div class="image is-square weapon">
<template v-if="weapon.weapon">
<img
loading="lazy"
:src="weapon.weapon.image | localSplatNetImageUrl"
:title="$t(`splatnet.weapons.${weapon.weapon.id}.name`, weapon.weapon.name)"
/>
@@ -24,6 +25,7 @@
<template v-else-if="weapon.coop_special_weapon">
<img
loading="lazy"
:src="weapon.coop_special_weapon.image | localSplatNetImageUrl"
:title="$t(`splatnet.coop_special_weapons.${weapon.coop_special_weapon.image}.name`, weapon.coop_special_weapon.name)"
/>

View File

@@ -8,13 +8,13 @@
<div class="panel-container" :class="{ 'is-hidden-mobile' : showingResults }">
<div class="image panel-image">
<img :src="image" />
<img loading="lazy" :src="image" />
</div>
<div class="regions">
<span class="region" v-for="(data, region) in festival.regions" :key="region">
<span class="icon is-small">
<img :src="require(`@/web/assets/img/region-${region}.svg`)" />
<img loading="lazy" :src="require(`@/web/assets/img/region-${region}.svg`)" />
</span>
<span>{{ region | upperCase }}</span>

View File

@@ -9,7 +9,7 @@
<div class="column">
<div class="image is-48x48" :title="$t(`splatnet.festivals.${festival.festival_id}.names.alpha_short`, festival.names.alpha_short)">
<img :src="festival.images.alpha | localSplatNetImageUrl" />
<img loading="lazy" :src="festival.images.alpha | localSplatNetImageUrl" />
</div>
</div>
@@ -19,7 +19,7 @@
<div class="column">
<div class="image is-48x48" :title="$t(`splatnet.festivals.${festival.festival_id}.names.bravo_short`, festival.names.bravo_short)">
<img :src="festival.images.bravo | localSplatNetImageUrl" />
<img loading="lazy" :src="festival.images.bravo | localSplatNetImageUrl" />
</div>
</div>
</div>

View File

@@ -1,6 +1,8 @@
<template>
<div class="stage-image" :class="{ hand: clickable }" :style="style" @click="click" v-if="stageDetails">
<figure class="image is-16by9"></figure>
<div class="stage-image" :class="{ hand: clickable }" @click="click" v-if="stageDetails">
<figure class="image is-16by9">
<img :src="image" :alt="name" loading="lazy" />
</figure>
<span class="stage-title" v-if="showTitle">{{ name }}</span>
<Modal v-if="isOpen" @close="isOpen = false" class="is-xwide">
@@ -60,11 +62,6 @@ export default {
return this.stageDetails.largeImage || Vue.filter('localSplatNetImageUrl')(this.stageDetails.image);
return null;
},
style() {
return {
'background-image': `url(${this.image})`,
};
},
},
methods: {
click() {

View File

@@ -25,7 +25,9 @@ test('waits for data, Vue, fonts, images and two layout frames before signaling
fonts: { ready: fonts.promise },
images: [
{
decode: () => {
loading: 'lazy',
decode() {
assert.equal(this.loading, 'eager');
steps.push('image');
return image.promise;