Skip to content

Commit c2519d7

Browse files
authored
refactor: add preloader until the image is loaded (#53)
1 parent bdc6110 commit c2519d7

File tree

2 files changed

+91
-18
lines changed

2 files changed

+91
-18
lines changed

src/components/ImageStack.vue

Lines changed: 6 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,7 @@
33
<ul class="list" :style="{ paddingBottom: `${aspectRatio}%` }" role="button" @click="next">
44
<TransitionGroup name="slide">
55
<li v-for="(stack, i) in stacks" :key="stack.src" class="item" :class="`item-${i}`">
6-
<figure class="figure">
7-
<img class="img" :src="stack.src" :alt="stack.alt">
8-
</figure>
6+
<ImageStackItem :image="stack" />
97
</li>
108
</TransitionGroup>
119
</ul>
@@ -38,6 +36,7 @@
3836
import Vue, { PropOptions } from 'vue'
3937
import IconChevronLeft from './icons/IconChevronLeft.vue'
4038
import IconChevronRight from './icons/IconChevronRight.vue'
39+
import ImageStackItem from './ImageStackItem.vue'
4140
4241
interface Image {
4342
src: string
@@ -53,7 +52,8 @@ interface Classes {
5352
export default Vue.extend({
5453
components: {
5554
IconChevronLeft,
56-
IconChevronRight
55+
IconChevronRight,
56+
ImageStackItem
5757
},
5858
5959
props: {
@@ -218,8 +218,8 @@ export default Vue.extend({
218218
transform: translate(0, 0) scale(1);
219219
}
220220
221-
.item-0 .figure { filter: blur(32px); }
222-
.item-1 .figure { filter: blur(16px); }
221+
.item-0 >>> .figure { filter: blur(32px); }
222+
.item-1 >>> .figure { filter: blur(16px); }
223223
224224
.item-0.slide-enter,
225225
.item-0.slide-leave-to,
@@ -229,18 +229,6 @@ export default Vue.extend({
229229
opacity: 0;
230230
}
231231
232-
.figure {
233-
filter: blur(0);
234-
transition: filter 0.75s;
235-
}
236-
237-
.img {
238-
width: 100%;
239-
height: 100%;
240-
border-radius: 8px;
241-
object-fit: cover;
242-
}
243-
244232
.control {
245233
position: relative;
246234
display: flex;

src/components/ImageStackItem.vue

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
<template>
2+
<div class="ImageStackItem">
3+
<div class="loader" :class="{ loaded }">
4+
<IconPreloaderDark class="loader-icon" />
5+
</div>
6+
7+
<figure class="figure">
8+
<img class="img" :src="image.src" :alt="image.alt" @load="loaded = true">
9+
</figure>
10+
</div>
11+
</template>
12+
13+
<script lang="ts">
14+
import Vue, { PropOptions } from 'vue'
15+
import IconPreloaderDark from './icons/IconPreloaderDark.vue'
16+
17+
interface Image {
18+
src: string
19+
alt: string
20+
}
21+
22+
export default Vue.extend({
23+
components: {
24+
IconPreloaderDark
25+
},
26+
27+
props: {
28+
image: { type: Object, required: true } as PropOptions<Image>
29+
},
30+
31+
data () {
32+
return {
33+
loaded: false
34+
}
35+
},
36+
37+
mounted () {
38+
// In case the image load event would not fire, we'll force displaying
39+
// the image after 3 sec.
40+
setTimeout(() => { this.loaded = true }, 3000)
41+
}
42+
})
43+
</script>
44+
45+
<style lang="postcss" scoped>
46+
@import '@/assets/styles/variables';
47+
48+
.loader {
49+
position: absolute;
50+
top: 0;
51+
right: 0;
52+
bottom: 0;
53+
left: 0;
54+
z-index: 20;
55+
background-color: var(--c-white);
56+
transition: opacity 0.5s;
57+
}
58+
59+
.loader.loaded {
60+
opacity: 0;
61+
}
62+
63+
.loader-icon {
64+
position: absolute;
65+
top: 50%;
66+
left: 50%;
67+
width: 48px;
68+
height: 48px;
69+
transform: translate(-50%);
70+
}
71+
72+
.figure {
73+
position: relative;
74+
z-index: 10;
75+
filter: blur(0);
76+
transition: filter 0.75s;
77+
}
78+
79+
.img {
80+
width: 100%;
81+
height: 100%;
82+
border-radius: 8px;
83+
object-fit: cover;
84+
}
85+
</style>

0 commit comments

Comments
 (0)