11<script setup lang="ts">
2- import { lazyLoad } from ' unlazy'
3- import { createPngDataUri as createPngDataUriFromThumbHash } from ' unlazy/thumbhash'
4- import { createPngDataUri as createPngDataUriFromBlurHash } from ' unlazy/blurhash'
2+ import { createPlaceholderFromHash , lazyLoad } from ' unlazy'
53import type { ImgHTMLAttributes } from ' vue'
6- import { computed , onBeforeUnmount , onMounted , ref , useRuntimeConfig , watchEffect } from ' #imports'
4+ import { onBeforeUnmount , ref , useRuntimeConfig , watchEffect } from ' #imports'
75
86const props = withDefaults (
97 defineProps <{
@@ -55,47 +53,49 @@ const props = withDefaults(
5553 },
5654)
5755
56+ // SSR-decoded BlurHash as PNG data URI placeholder image
5857const { unlazy } = useRuntimeConfig ().public
58+ const hash = props .thumbhash || props .blurhash
59+ const loadSSR = process .server && (props .ssr ?? unlazy .ssr )
60+ const pngPlaceholder = (loadSSR && hash )
61+ ? createPlaceholderFromHash ({
62+ hash ,
63+ hashType: props .thumbhash ? ' thumbhash' : ' blurhash' ,
64+ size: props .placeholderSize || unlazy .placeholderSize ,
65+ ratio: props .placeholderRatio ,
66+ })
67+ : undefined
68+
5969const target = ref <HTMLImageElement | undefined >()
60- let isLoaded = false
6170let cleanup: () => void | undefined
71+ let hasPlaceholder = false
6272
63- // SSR-decoded BlurHash as PNG data URI placeholder image
64- const pngPlaceholder = computed (() => {
65- if (
66- (process .client || (props .ssr ?? unlazy .ssr ))
67- && (props .blurhash || props .thumbhash )
68- ) {
69- return props .blurhash
70- ? createPngDataUriFromBlurHash (props .blurhash , {
71- size: props .placeholderSize || unlazy .placeholderSize ,
72- ratio: props .placeholderRatio ,
73- })
74- : createPngDataUriFromThumbHash (props .thumbhash ! )
75- }
76- })
73+ watchEffect (() => {
74+ cleanup ?.()
7775
78- onMounted (() => {
7976 if (! target .value )
8077 return
8178
82- watchEffect (() => {
83- cleanup ?.()
84-
85- if (pngPlaceholder .value && ! isLoaded )
86- target .value ! .src = pngPlaceholder .value
87-
88- if (! props .lazyLoad )
89- return
90-
91- cleanup = lazyLoad (target .value ! , {
79+ if (! hasPlaceholder ) {
80+ const placeholder = createPlaceholderFromHash ({
81+ image: target .value ,
9282 hash: props .thumbhash || props .blurhash ,
9383 hashType: props .thumbhash ? ' thumbhash' : ' blurhash' ,
94- placeholderSize: props .placeholderSize || unlazy .placeholderSize ,
84+ size: props .placeholderSize || unlazy .placeholderSize ,
85+ ratio: props .placeholderRatio ,
9586 })
9687
97- isLoaded = true
98- })
88+ if (placeholder )
89+ target .value .src = placeholder
90+
91+ hasPlaceholder = true
92+ }
93+
94+ if (! props .lazyLoad )
95+ return
96+
97+ // Placeholder is already decoded
98+ cleanup = lazyLoad (target .value , { hash: false })
9999})
100100
101101onBeforeUnmount (() => {
@@ -104,16 +104,7 @@ onBeforeUnmount(() => {
104104 </script >
105105
106106<template >
107- <img
108- v-if =" !props.sources?.length"
109- ref =" target"
110- :src =" pngPlaceholder || placeholderSrc"
111- :data-src =" src"
112- :data-srcset =" srcSet"
113- :data-sizes =" autoSizes ? 'auto' : undefined"
114- loading =" lazy"
115- >
116- <picture v-else >
107+ <picture v-if =" props.sources?.length" >
117108 <source
118109 v-for =" (source, index) in props.sources"
119110 :key =" index"
@@ -130,4 +121,13 @@ onBeforeUnmount(() => {
130121 loading =" lazy"
131122 >
132123 </picture >
124+ <img
125+ v-else
126+ ref =" target"
127+ :src =" pngPlaceholder || placeholderSrc"
128+ :data-src =" src"
129+ :data-srcset =" srcSet"
130+ :data-sizes =" autoSizes ? 'auto' : undefined"
131+ loading =" lazy"
132+ >
133133</template >
0 commit comments