<div class="featureVideo accent-primary">
<div class="featureVideo__media">
<div class="media">
<p-lazy>
<video class="media__content" inert controls autoplay playsinline>
<source
data-src="https://www.w3schools.com/html/mov_bbb.mp4"
type="video/mp4"
/>
Your browser does not support the video tag.
</video>
<template #placeholder="{ load, modifier }">
<picture class="media__mask" :class="modifier">
<img
srcset="https://unsplash.it/960/540?random&gravity=center"
alt=""
/>
</picture>
<button class="media__trigger" @click="load" :class="modifier">
<span class="srOnly">play</span>
<svg class="media__icon icon -circle" viewBox="0 0 96 96">
<use href="/main-icons-sprite.svg#play" />
</svg>
</button>
</template>
</p-lazy>
</div>
</div>
<div class="featureVideo__background"></div>
<div class="featureVideo__content">
<h3 class="featureVideo__title h3">Heading Placeholder</h3>
<p class="featureVideo__text">
Signissim lacus in eros convallis porta. Pellentesque hendrerit neque a
cursus tristique. Phasellus eleifend consectetur felis, in eleifend
nulla vulputate eget.
</p>
</div>
</div>
@use "@imarc/pronto/resources/styles/imported" as *;
.featureVideo {
color: var(--accent-color-text);
display: grid;
gap: #{fluid-rems(1.5, 2)};
padding: 0 #{fluid-rems(1.5, 2, 2.5)} #{fluid-rems(2.5, 3, 4.5)};
position: relative;
width: 100%;
min-width: 0;
grid-template-columns: 1fr;
grid-template-rows: 1fr 1fr auto;
> * {
grid-column: 1;
}
&__media {
display: grid;
justify-items: center;
width: 100%;
min-width: 0;
grid-row: 1 / 3;
z-index: 2;
.media {
margin: 0;
width: 100%;
min-width: 200px;
overflow: hidden;
@include at(md) {
width: min(100%, 80%);
}
}
.media__mask img,
.media__content,
iframe {
width: 100%;
aspect-ratio: 16 / 9;
object-fit: cover;
display: block;
box-shadow: 0 16px 64px 0 rgba(0, 0, 0, 0.05);
}
}
&__background {
background: var(--accent-color);
grid-row: 2 / -1;
z-index: 0;
}
&__content {
display: grid;
gap: .75rem;
text-align: center;
justify-items: center;
width: 100%;
max-width: 42rem;
margin: 0 auto;
padding: 0 1rem #{fluid-rems(1.5, 3, 4)};
align-self: start;
grid-row: 3 / -1;
z-index: 1;
}
&__title,
&__text {
margin: 0;
}
}
<script setup>
import { computed, ref, nextTick, useTemplateRef } from 'vue'
/**
* PLazy is a vue component that lazy loads its content after it's been clicked.
*
* @slot default - the content to lazyload. It will remove inert attributes, change data-src
* attributes to src attributes, and call .load() on videos.
* @slot placeholder - the content to display before it's lazyloaded. The following properties
* are exposed to this slot:
* load {function} - function to call to load the content.
* loaded {boolean} - whether the content is loaded or not.
* modifier {string} - either '-loaded' or '' based on loaded.
*/
const loaded = ref(false)
const content = useTemplateRef('content')
const modifier = computed(() => loaded.value ? '-loaded' : '')
const load = async () => {
if (loaded.value) return
await nextTick()
const promises = []
/* Change data-src attributes to src attributes */
content.value.querySelectorAll('[data-src]')
.forEach(element => element.setAttribute('src', element.dataset.src))
/* remove inert attribute */
content.value.querySelectorAll('[inert]')
.forEach(element => element.inert = false)
/* load videos */
content.value.querySelectorAll('video')
.forEach(element => {
element.load()
promises.push(new Promise(resolve => {
element.addEventListener('loadeddata', resolve, { once: true })
}))
})
/* load iframes */
content.value.querySelectorAll('iframe')
.forEach(element => {
promises.push(new Promise(resolve => {
element.addEventListener('load', resolve, { once: true })
}))
})
/* wait for all videos and iframes to load, then update loaded.value */
Promise.all(promises).then(() => loaded.value = true)
}
</script>
<template>
<div ref="content">
<slot name="default"></slot>
</div>
<slot name="placeholder" v-bind="{ load, loaded, modifier }"></slot>
</template>
<style scoped>
div {
container-type: size;
}
</style>