Vue JS 3图片淡入过渡动画失效,如何正确实现?
Vue 3图片淡入过渡失效的解决办法
问题场景
实现图片加载时显示旋转动画,加载完成后动画淡出、图片淡入的效果。目前旋转动画的淡出正常,但图片直接渲染无过渡效果,替换为文本块则过渡正常。
原代码
图片组件代码
<script setup> import FadeTransition from '../transitions/fade-transition.vue'; import { reactive } from 'vue' const props = defineProps({ imgUrl: String, route: String }) let getURL = function () { return new URL(props.imgUrl, import.meta.url); } let show = reactive({showing: false}); let showImg = function () { show.showing = true; } </script> <template> <div class="frame"> <FadeTransition><div v-show="!show.showing" class="loader"></div></FadeTransition> <RouterLink :to="props.route"><FadeTransition><img v-show="show.showing" class="image" :src="getURL()" @load="showImg()"/></FadeTransition></RouterLink> </div> </template> <style scoped> .frame { width: 33.3333%; aspect-ratio: 1/1; margin: 0px; display: inline-flex; position: relative; padding: 6px; } .image { width: calc(100% - 12px); height: calc(100% - 12px); position: absolute; } .loader { border: 4px solid var(--grey); border-top: 4px solid var(--darkGrey); border-radius: 50%; width: 30px; height: 30px; animation: spin 5s linear infinite; position: absolute; left: calc(50% - 15px); top: calc(50% - 15px); } @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } </style>
FadeTransition组件代码
<script> </script> <template> <Transition name="fade"> <slot></slot> </Transition> </template> <style> .fade-enter-active, .fade-leave-active { transition: opacity 0.5s ease; } .fade-enter-from, .fade-leave-to { opacity: 0; } </style>
问题原因
原代码使用v-show控制图片显示,v-show仅切换元素的display属性,图片始终存在于DOM中。当图片加载完成触发@load事件时,图片已经渲染完成,此时设置show.showing = true,浏览器直接显示图片,Vue的Transition无法捕捉到元素从无到有的插入动作,因此淡入过渡不生效。而文本块因为内容简单,状态变化时过渡能正常触发,但图片因已加载完成,浏览器会直接绘制跳过过渡。
解决方案
改用预加载图片 + v-if控制DOM插入的方式,确保图片加载完成后才插入DOM,让Transition能捕捉到元素插入动作,触发淡入过渡。
修改后的图片组件代码
<script setup> import FadeTransition from '../transitions/fade-transition.vue'; import { ref, onMounted } from 'vue' const props = defineProps({ imgUrl: String, route: String }) // 生成图片URL const getURL = () => new URL(props.imgUrl, import.meta.url); // 控制加载器和图片的显示状态 const isImageLoaded = ref(false); const showLoader = ref(true); onMounted(() => { // 预加载图片,避免插入DOM时才开始加载 const preloadImg = new Image(); preloadImg.src = getURL(); preloadImg.onload = () => { // 先隐藏加载器(触发淡出) showLoader.value = false; // 触发图片插入DOM(触发淡入) isImageLoaded.value = true; } }) </script> <template> <div class="frame"> <FadeTransition> <!-- 用v-if控制加载器的DOM存在状态 --> <div v-if="showLoader" class="loader"></div> </FadeTransition> <RouterLink :to="props.route"> <FadeTransition> <!-- 图片加载完成后才插入DOM,触发淡入过渡 --> <img v-if="isImageLoaded" class="image" :src="getURL()" /> </FadeTransition> </RouterLink> </div> </template> <!-- 样式部分保持不变 --> <style scoped> .frame { width: 33.3333%; aspect-ratio: 1/1; margin: 0px; display: inline-flex; position: relative; padding: 6px; } .image { width: calc(100% - 12px); height: calc(100% - 12px); position: absolute; } .loader { border: 4px solid var(--grey); border-top: 4px solid var(--darkGrey); border-radius: 50%; width: 30px; height: 30px; animation: spin 5s linear infinite; position: absolute; left: calc(50% - 15px); top: calc(50% - 15px); } @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } </style>
FadeTransition组件无需修改
保留原FadeTransition组件的代码即可,它会为插入DOM的元素正确应用淡入淡出过渡。
额外说明
如果希望加载器淡出和图片淡入更连贯,可以在设置isImageLoaded.value = true前添加一小段延迟(比如10ms),确保加载器的淡出过渡先触发:
preloadImg.onload = () => { showLoader.value = false; setTimeout(() => { isImageLoaded.value = true; }, 10); }
内容的提问来源于stack exchange,提问作者jban28
相关产品推荐
相关产品推荐

