202 lines
3.8 KiB
Vue
202 lines
3.8 KiB
Vue
<template>
|
|
<div class="background-image-loader">
|
|
<div class="background-container">
|
|
<div class="background background-default"></div>
|
|
<transition name="fade-fast"
|
|
>
|
|
<div v-if="loadedImageUrl !== null"
|
|
class="background background-img background-blur"
|
|
:style="backgroundBlurStyle"
|
|
>
|
|
</div>
|
|
</transition>
|
|
<transition name="fade"
|
|
@beforeEnter="handleBeforeEnter"
|
|
@afterLeave="handleAfterLeave"
|
|
>
|
|
<div class="background background-img"
|
|
:key="loadedImageUrl"
|
|
:style="backgroundStyle"
|
|
>
|
|
</div>
|
|
</transition>
|
|
<slot name="overlay">
|
|
</slot>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import imageLoader from '~/mixins/imageLoader.js'
|
|
|
|
export default {
|
|
mixins: [ imageLoader ],
|
|
|
|
props: {
|
|
imageUrl: {
|
|
type: String,
|
|
required: false,
|
|
default: function () {
|
|
return null
|
|
}
|
|
},
|
|
},
|
|
|
|
data () {
|
|
return {
|
|
loadedImageUrl: null,
|
|
isInTransition: false,
|
|
transitionTimeout: null,
|
|
}
|
|
},
|
|
|
|
computed: {
|
|
backgroundStyle () {
|
|
let style = {}
|
|
if (this.loadedImageUrl) {
|
|
style.backgroundImage = `url(${this.loadedImageUrl})`
|
|
}
|
|
|
|
return style
|
|
},
|
|
|
|
backgroundBlurStyle () {
|
|
let style = {}
|
|
if (this.loadedImageUrl) {
|
|
style.backgroundImage = `url(${this.loadedImageUrl})`
|
|
}
|
|
|
|
return style
|
|
}
|
|
},
|
|
|
|
watch: {
|
|
imageUrl () {
|
|
if (!this.isInTransition && this.imageUrl) {
|
|
this.setImage(this.imageUrl)
|
|
}
|
|
},
|
|
},
|
|
|
|
mounted () {
|
|
this.setImage(this.imageUrl)
|
|
},
|
|
|
|
methods: {
|
|
setImage(url) {
|
|
if (url === null) { return }
|
|
this.loadImage(url)
|
|
.then(img => {
|
|
this.loadedImageUrl = img.src
|
|
this.$emit('imageLoaded', img)
|
|
})
|
|
.catch(err => {
|
|
this.$emit('imageLoadError', err)
|
|
})
|
|
},
|
|
|
|
handleBeforeEnter() {
|
|
// TOOD get the actual value programatically
|
|
// use ref and ref.style.transitionDuration or whatever
|
|
let transitionDuration = 4000
|
|
|
|
this.isInTransition = true
|
|
|
|
clearTimeout(this.transitionTimeout)
|
|
this.transitionTimeout = setTimeout(() => {
|
|
this.isInTransition = false
|
|
if (this.imageUrl !== this.loadedImageUrl) {
|
|
this.setImage(this.imageUrl)
|
|
}
|
|
}, transitionDuration)
|
|
},
|
|
|
|
handleAfterLeave() {
|
|
|
|
}
|
|
},
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
|
|
.background-image-loader {
|
|
position: relative;
|
|
width: 100%;
|
|
height: 100%;
|
|
}
|
|
|
|
.background {
|
|
position: absolute;
|
|
width: 100%;
|
|
height: 100%;
|
|
top: 0;
|
|
left: 0;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.background-img {
|
|
background-size: cover;
|
|
background-position: center center;
|
|
opacity: 1;
|
|
}
|
|
|
|
.background-default {
|
|
$shadow-opacity: .6;
|
|
$shadow-color: $color__neutral-100;
|
|
background:
|
|
linear-gradient(
|
|
to right,
|
|
rgba($shadow-color, $shadow-opacity),
|
|
rgba($shadow-color, 0) 20%,
|
|
rgba($shadow-color, 0) 80%,
|
|
rgba($shadow-color, $shadow-opacity)
|
|
),
|
|
linear-gradient(
|
|
to bottom,
|
|
rgba($shadow-color, $shadow-opacity),
|
|
rgba($shadow-color, 0) 20%,
|
|
rgba($shadow-color, 0) 80%,
|
|
rgba($shadow-color, $shadow-opacity)
|
|
),
|
|
linear-gradient(
|
|
to top,
|
|
$color__neutral-100,
|
|
$color__neutral-300
|
|
)
|
|
;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.background-blur {
|
|
filter: blur(30px);
|
|
}
|
|
|
|
.fade {
|
|
$timing: .7;
|
|
&-enter-active {
|
|
transition: opacity 4s * $timing 1s * $timing ease-in;
|
|
}
|
|
|
|
&-leave-active {
|
|
transition: opacity 2s * $timing ease-out;
|
|
}
|
|
|
|
&-enter, &-leave-to {
|
|
opacity: 0;
|
|
}
|
|
}
|
|
|
|
.fade-fast {
|
|
$timing: 1;
|
|
&-enter-active {
|
|
transition: opacity 3s * $timing ease-in;
|
|
transition-delay: .2s;
|
|
}
|
|
|
|
&-enter {
|
|
opacity: 0;
|
|
}
|
|
}
|
|
</style>
|