All checks were successful
Build and Deploy Nuxt / build (push) Successful in 41s
85 lines
1.7 KiB
Vue
85 lines
1.7 KiB
Vue
<template>
|
|
<div class="image-wrapper">
|
|
<img :src="highResSrc" alt="" class="image-sizer" aria-hidden="true" />
|
|
<div class="subwrapper">
|
|
<img
|
|
:src="lowResSrc"
|
|
:alt="alt"
|
|
class="image-low"
|
|
:class="{ 'opacity-0': isLoaded }"
|
|
/>
|
|
<img
|
|
:src="highResSrc"
|
|
:alt="alt"
|
|
class="image-high"
|
|
:class="{ 'opacity-0': !isLoaded }"
|
|
@load="isLoaded = true"
|
|
/>
|
|
<div v-if="!isLoaded" class="spinner-overlay">
|
|
<div class="spinner" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.image-wrapper {
|
|
position: relative;
|
|
display: flex;
|
|
justify-content: center;
|
|
}
|
|
.image-sizer {
|
|
display: block;
|
|
width: 100%;
|
|
height: auto;
|
|
visibility: hidden;
|
|
}
|
|
.image-low {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 50%;
|
|
transform: translate(-50%, 0%);
|
|
height: 100%;
|
|
filter: blur(20px);
|
|
transition: opacity 0.3s ease;
|
|
}
|
|
.image-high {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 50%;
|
|
transform: translate(-50%, 0%);
|
|
max-width: 100%;
|
|
height: auto;
|
|
transition: opacity 0.3s ease;
|
|
}
|
|
.opacity-0 {
|
|
opacity: 0;
|
|
}
|
|
.spinner-overlay {
|
|
position: absolute;
|
|
inset: 0;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
.spinner {
|
|
width: 32px;
|
|
height: 32px;
|
|
border: 3px solid rgba(255, 255, 255, 0.4);
|
|
border-top-color: rgba(255, 255, 255, 0.9);
|
|
border-radius: 50%;
|
|
animation: spin 0.75s linear infinite;
|
|
}
|
|
@keyframes spin {
|
|
to { transform: rotate(360deg); }
|
|
}
|
|
</style>
|
|
|
|
<script setup>
|
|
defineProps({
|
|
lowResSrc: { type: String, required: true },
|
|
highResSrc: { type: String, required: true },
|
|
alt: { type: String, default: '' },
|
|
})
|
|
const isLoaded = ref(false)
|
|
</script> |