Vue3中实现图片加载时显示Blurhash占位图的问题求助
Vue3 实现图片加载时显示 Blurhash 占位图的可行方案
核心思路
放弃已废弃的Vue2专用包,直接使用官方底层的blurhash库自定义Vue3组件渲染Blurhash,同时优化图片加载监听逻辑,避免布局跳动。
步骤1:安装依赖
安装官方维护的blurhash基础库:
npm install blurhash # 或者 yarn add blurhash
步骤2:完善ItemBlurHashImg组件
这个组件负责将Blurhash哈希值渲染为模糊占位图,通过Canvas绘制实现:
<template> <canvas ref="canvasRef" :width="drawWidth" :height="drawHeight" class="blurhash-canvas" ></canvas> </template> <script setup> import { ref, onMounted, computed } from 'vue'; import { decode } from 'blurhash'; const props = defineProps({ hash: { type: String, required: true, }, // 传入图片原始宽高比,保证模糊图比例和原图一致 aspectRatio: { type: Number, default: 16 / 9, }, }); const canvasRef = ref(null); // 用固定小尺寸解码Blurhash,既保证性能又足够清晰 const drawWidth = computed(() => 32); const drawHeight = computed(() => drawWidth.value / props.aspectRatio); onMounted(() => { if (!canvasRef.value || !props.hash) return; try { const pixels = decode(props.hash, drawWidth.value, drawHeight.value); const ctx = canvasRef.value.getContext('2d'); const imageData = ctx.createImageData(drawWidth.value, drawHeight.value); imageData.data.set(pixels); ctx.putImageData(imageData, 0, 0); } catch (err) { console.error('Blurhash解码失败:', err); } }); </script> <style scoped> .blurhash-canvas { display: block; width: 100%; height: 100%; object-fit: cover; } </style>
步骤3:修改ItemCard组件的加载逻辑
改用叠加占位图的方式,通过透明度过渡实现平滑切换,同时直接监听真实图片的加载事件:
<template> <div class="card"> <div class="card__item"> <div class="card__image-wrapper"> <!-- Blurhash占位图始终存在,图片加载完成后覆盖 --> <ItemBlurHashImg class="card__image placeholder" :hash="data.blurhash" :aspectRatio="15.625 / 15" <!-- 对应你的图片宽高比:width/height --> /> <img class="card__image" :src="getSrc('.jpg')" :alt="data.alt" width="15.625rem" loading="lazy" @load="isLoaded = true" @error="isLoaded = true" <!-- 加载失败也隐藏占位图 --> /> </div> <div class="card__content"> <h2 class="card__title">{{ data.title }}</h2> <p class="card__text">{{ data.text }}</p> <router-link class="card__link" :to="{ name: 'Home' }"> View more </router-link> </div> </div> </div> </template> <script setup> import { ref } from 'vue'; import ItemBlurHashImg from '@/components/SkeletonLoaders/ItemBlurHashImg.vue'; const props = defineProps(['data']); const isLoaded = ref(false); const getSrc = ext => { return new URL( `../assets/images/recipe/${props.data.image}${ext}`, import.meta.url ).href; }; </script> <style lang="scss"> .card { display: flex; justify-content: center; margin-top: 45px; align-items: center; &__item { display: flex; flex-direction: column; justify-content: flex-start; color: $c-black; border-radius: $br-rounded; max-width: 22.5625rem; height: 30.25rem; background-color: $c-white; } &__image-wrapper { position: relative; height: 15rem; border-top-left-radius: $br-rounded; border-top-right-radius: $br-rounded; overflow: hidden; } &__image { position: absolute; top: 0; left: 0; display: block; width: 100%; height: 100%; object-fit: cover; transition: opacity 0.3s ease; opacity: v-bind(isLoaded ? 1 : 0); } &__image.placeholder { position: static; opacity: 1; } } </style>
关键优化点
- 用绝对定位叠加占位图和真实图片,通过opacity过渡实现平滑切换,避免布局跳动
- 直接监听img的
@load和@error事件,比在onMounted中预加载更可靠,适配loading="lazy"场景 - 传入图片真实宽高比,保证模糊占位图的比例与原图一致
- 增加解码错误处理,避免无效哈希导致组件崩溃
内容的提问来源于stack exchange,提问作者Michael
相关产品推荐
相关产品推荐

