Skip to content

Commit 25cc462

Browse files
committed
feat: add VRoadizVideo + VVideoPlayer
1 parent 2b12c83 commit 25cc462

17 files changed

+796
-1
lines changed

assets/scss/main.scss

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,6 @@
1616

1717
// transitions
1818
@import "transitions/fade";
19+
20+
// vendors
21+
@import "vendors/plyr";

assets/stories/fixtures/documents/vimeo-01.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
"mediaDuration": 281,
1111
"imageAverageColor": "#585256",
1212
"alt": "Virtual Depictions: San Francisco \/ Public Art Project",
13-
"thumbnail": false,
1413
"relativePath": "/stories/images/vimeo-01.jpg",
1514
"processable": true,
1615
"type": "image",
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<script setup lang="ts">
2+
import document from '~/assets/stories/fixtures/documents/vimeo-01.json'
3+
</script>
4+
5+
<template>
6+
<NuxtStory>
7+
<NuxtStoryVariant title="Background">
8+
<VRoadizVideo
9+
:document="document"
10+
:class="$style.video"
11+
background
12+
/>
13+
</NuxtStoryVariant>
14+
15+
<NuxtStoryVariant title="Background">
16+
<VRoadizVideo
17+
:document="document"
18+
width="500"
19+
height="1200"
20+
:class="$style.video"
21+
background
22+
/>
23+
</NuxtStoryVariant>
24+
</NuxtStory>
25+
</template>
26+
27+
<style lang="scss" module>
28+
.video {
29+
max-width: rem(600);
30+
}
31+
</style>
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<script setup lang="ts">
2+
import document from '~/assets/stories/fixtures/documents/vimeo-01.json'
3+
</script>
4+
5+
<template>
6+
<NuxtStory>
7+
<div :class="$style.wrapper">
8+
<VRoadizVideo
9+
:document="document"
10+
fit="cover"
11+
:class="$style.video"
12+
background
13+
/>
14+
</div>
15+
</NuxtStory>
16+
</template>
17+
18+
<style lang="scss" module>
19+
.root {
20+
position: relative;
21+
}
22+
23+
.wrapper {
24+
--v-player-height: 100%;
25+
--v-player-video-object-fit: cover;
26+
27+
position: absolute;
28+
height: 100svh;
29+
inset: 0;
30+
}
31+
</style>
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
<script lang="ts" setup>
2+
import type { RoadizDocument } from '@roadiz/types'
3+
import type { PropType } from 'vue'
4+
import { isImage } from '~/utils/roadiz/document'
5+
import { commonVideoProps, videoAttributes, videoSrc } from '~/utils/video/video-props'
6+
import { getVideoAttrsValues } from '~/utils/video/video-attributes'
7+
8+
const props = defineProps({
9+
...commonVideoProps,
10+
...videoAttributes,
11+
document: { type: Object as PropType<RoadizDocument>, required: true },
12+
thumbnail: { type: Object as PropType<RoadizDocument> },
13+
hideThumbnail: Boolean,
14+
})
15+
16+
const hadInteraction = ref(false)
17+
const onClick = (event: Event) => {
18+
if (event.defaultPrevented || !props.playsinline) return
19+
20+
hadInteraction.value = true
21+
}
22+
const onVideoEnded = () => (hadInteraction.value = false)
23+
24+
const displayedThumbnail = computed(() => {
25+
const documents = [props.thumbnail, props.document?.thumbnail, props.document]
26+
return documents.find(document => isImage(document))
27+
})
28+
29+
const filteredVideoProps = computed(() => {
30+
return Object.keys(props).reduce((acc, key) => {
31+
// @ts-expect-error TODO: use pick() here
32+
if (commonVideoProps[key] || videoAttributes[key] || videoSrc[key]) acc[key] = props[key]
33+
return acc
34+
}, {})
35+
})
36+
37+
const dimension = computed(() => {
38+
return {
39+
width: props.document.imageWidth,
40+
height: props.document.imageHeight,
41+
}
42+
})
43+
44+
const videoRatio = computed(() => {
45+
return Number(dimension.value.width) / Number(dimension.value.height)
46+
})
47+
48+
const embedVideoAttrs = computed(() => {
49+
return {
50+
embedPlatform: props.document.embedPlatform,
51+
embedId: props.document.embedId,
52+
src: props.document.relativePath,
53+
altSources: props.document.altSources,
54+
...dimension.value,
55+
...getVideoAttrsValues(props, !!props?.background),
56+
...filteredVideoProps.value,
57+
}
58+
})
59+
</script>
60+
61+
<template>
62+
<VVideoPlayer
63+
v-if="hideThumbnail || props.background"
64+
v-bind="embedVideoAttrs"
65+
/>
66+
<div
67+
v-else
68+
:class="[$style.root, hadInteraction && $style['root--had-interaction']]"
69+
>
70+
<VButton
71+
:label="$t('watch_the_video')"
72+
theme="dark"
73+
icon-name="play"
74+
filled
75+
:class="$style.button"
76+
@click="onClick"
77+
/>
78+
<slot>
79+
<VRoadizImage
80+
v-if="displayedThumbnail"
81+
:document="displayedThumbnail"
82+
:class="$style.thumbnail"
83+
@click="onClick"
84+
/>
85+
<div
86+
v-else
87+
:class="[$style.thumbnail, $style['thumbnail--placeholder']]"
88+
:style="{ aspectRatio: videoRatio || 16 / 9 }"
89+
/>
90+
</slot>
91+
<VVideoPlayer
92+
v-if="hadInteraction"
93+
v-bind="embedVideoAttrs"
94+
:autoplay="true"
95+
:plyr="{ listener: { ended: onVideoEnded } }"
96+
/>
97+
</div>
98+
</template>
99+
100+
<style lang="scss" module="">
101+
.root {
102+
--v-player-position: absolute;
103+
--v-player-height: 100%;
104+
--v-player-width: 100%;
105+
106+
position: relative;
107+
display: flex;
108+
align-items: center;
109+
justify-content: center;
110+
}
111+
112+
.button {
113+
--v-button-position: absolute;
114+
115+
z-index: 1;
116+
top: 50%;
117+
left: 50%;
118+
transform: translate(-50%, -50%);
119+
120+
@include v-button-size('m');
121+
122+
@include media('>=lg') {
123+
@include v-button-size('l');
124+
}
125+
126+
.root--had-interaction & {
127+
pointer-events: none;
128+
visibility: hidden;
129+
}
130+
}
131+
132+
.thumbnail {
133+
width: 100%;
134+
cursor: pointer;
135+
136+
.root--had-interaction & {
137+
pointer-events: none;
138+
visibility: hidden;
139+
}
140+
141+
&--placeholder {
142+
aspect-ratio: 16 / 9;
143+
background-color: color(grey-50);
144+
}
145+
}
146+
</style>

0 commit comments

Comments
 (0)