detail page port
This commit is contained in:
167
components/prismic/Media.vue
Normal file
167
components/prismic/Media.vue
Normal file
@@ -0,0 +1,167 @@
|
||||
<template>
|
||||
<div
|
||||
:class="[
|
||||
'prismic-media',
|
||||
{ 'fill-space': fillSpace },
|
||||
`fit-${fit}`,
|
||||
{ 'video-loaded': videoSrc && videoLoaded },
|
||||
]"
|
||||
:style="{ '--aspect': cmpAspect + '%' }"
|
||||
>
|
||||
<div class="image-sizer">
|
||||
<transition :name="transition">
|
||||
<img
|
||||
v-show="loaded"
|
||||
ref="image"
|
||||
:src="imageSrc"
|
||||
:srcset="srcset"
|
||||
:width="width"
|
||||
:height="height"
|
||||
:alt="imageAlt"
|
||||
@load="loaded = true"
|
||||
/>
|
||||
</transition>
|
||||
|
||||
<template v-if="videoSrc.length">
|
||||
<video
|
||||
v-show="videoLoaded"
|
||||
:height="height"
|
||||
:width="width"
|
||||
:src="videoSrc"
|
||||
:muted="muted"
|
||||
:autoplay="autoplay"
|
||||
ref="video"
|
||||
playsinline
|
||||
loop
|
||||
@canplay="videoLoaded = true"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, computed } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
image: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
required: true,
|
||||
},
|
||||
video: [Object, String],
|
||||
aspect: {
|
||||
type: [String, Number],
|
||||
default: () => -1,
|
||||
},
|
||||
transition: {
|
||||
type: String,
|
||||
default: () => 'fade',
|
||||
},
|
||||
fillSpace: {
|
||||
type: Boolean,
|
||||
default: () => false,
|
||||
},
|
||||
fit: {
|
||||
type: String,
|
||||
default: () => 'cover',
|
||||
},
|
||||
muted: {
|
||||
type: Boolean,
|
||||
default: () => true,
|
||||
},
|
||||
autoplay: {
|
||||
type: Boolean,
|
||||
default: () => true,
|
||||
},
|
||||
})
|
||||
|
||||
const image = ref()
|
||||
const video = ref()
|
||||
const loaded = ref(false)
|
||||
const videoLoaded = ref(false)
|
||||
|
||||
// Set loaded to true if image is in cache
|
||||
onMounted(async () => {
|
||||
await new Promise((res) => setTimeout(res, 50))
|
||||
if (image.value?.complete) loaded.value = true
|
||||
if (video.value?.readyState >= 3) videoLoaded.value = true
|
||||
})
|
||||
|
||||
const imageSrc = computed(() => props.image?.url || '')
|
||||
const imageAlt = computed(() => props.image?.alt || '')
|
||||
const videoSrc = computed(() => props.video?.url || props.video || '')
|
||||
const width = computed(() => props.image?.dimensions?.width || 0)
|
||||
const height = computed(() => props.image?.dimensions?.height || 0)
|
||||
|
||||
const cmpAspect = computed(() => {
|
||||
// calculate if no aspect provided
|
||||
if (props.aspect === -1) {
|
||||
return (height.value / width.value) * 100
|
||||
}
|
||||
|
||||
// otherwise, parse provided aspect, handling both 56.25 and 0.5625 style
|
||||
const toParse = parseFloat(props.aspect)
|
||||
return toParse <= 1 ? toParse * 100 : toParse
|
||||
})
|
||||
const srcset = computed(() => {
|
||||
return [400, 800, 1024, 1280, 1536, 2048, 2560]
|
||||
.map((size) => {
|
||||
const w = size === null ? width.value : size
|
||||
const h = Math.round(width.value / (cmpAspect.value / 100))
|
||||
return imageSrc.value + `&w=${w} ${w}w`
|
||||
})
|
||||
.join(', ')
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.prismic-media {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
|
||||
.image-sizer {
|
||||
overflow: hidden;
|
||||
padding-bottom: var(--aspect);
|
||||
|
||||
& > * {
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
left: 0;
|
||||
top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// fill space
|
||||
&.fill-space {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
top: 0;
|
||||
|
||||
.image-sizer {
|
||||
padding-bottom: 0;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// fits
|
||||
&.fit-cover .image-sizer > * {
|
||||
object-fit: cover;
|
||||
}
|
||||
&.fit-contain .image-sizer > * {
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
/* Hide background image on video load */
|
||||
&.video-loaded img {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user