Skip to content

Commit 81cfb0b

Browse files
committed
feat: extract player timeline to custom component
1 parent 0b99313 commit 81cfb0b

2 files changed

Lines changed: 286 additions & 62 deletions

File tree

resources/js/components/video/VideoPlayer.vue

Lines changed: 69 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@ import type { ComputedRef, Ref } from 'vue';
66
import { getScreenSize, handleStorageURL, isInputLikeElement, isMobileDevice, toFormattedDate, toFormattedDuration } from '@/service/util';
77
import { computed, nextTick, onBeforeUnmount, onMounted, ref, useTemplateRef, watch } from 'vue';
88
import { copyVideoFrame, saveVideoFrame } from '@/service/video/frameService';
9-
import { debounce, round, throttle } from 'lodash';
109
import { useRoute, useRouter } from 'vue-router';
1110
import { UseCreatePlayback } from '@/service/mutations';
1211
import { useVideoPlayback } from '@/service/queries';
1312
import { useContentStore } from '@/stores/ContentStore';
13+
import { debounce, round } from 'lodash';
1414
import { useAuthStore } from '@/stores/AuthStore';
1515
import { useAppStore } from '@/stores/AppStore';
1616
import { storeToRefs } from 'pinia';
@@ -20,13 +20,12 @@ import { onSeek } from '@/service/video/seekBus';
2020
import { toast } from '@/service/toaster/toastService';
2121
2222
import VideoPopoverSlider from '@/components/video/VideoPopoverSlider.vue';
23-
import VideoTooltipSlider from '@/components/video/VideoTooltipSlider.vue';
2423
import VideoPopoverItem from '@/components/video/VideoPopoverItem.vue';
2524
import VideoPartyPanel from '@/components/video/VideoPartyPanel.vue';
25+
import VideoTimeline from '@/components/video/VideoTimeline.vue';
2626
import ButtonCorner from '@/components/inputs/ButtonCorner.vue';
2727
import VideoHeatmap from '@/components/video/VideoHeatmap.vue';
2828
import VideoPopover from '@/components/video/VideoPopover.vue';
29-
3029
import VideoButton from '@/components/video/VideoButton.vue';
3130
import VideoSlider from '@/components/video/VideoSlider.vue';
3231
import VideoLyrics from '@/components/video/VideoLyrics.vue';
@@ -179,11 +178,11 @@ const keyBinds = computed(() => {
179178
});
180179
181180
// Elements
182-
const container = useTemplateRef('video-container');
183-
const progressBar = useTemplateRef('progress-bar');
184-
const popover = useTemplateRef('popover');
185-
const progressTooltip = useTemplateRef('progress-tooltip');
186181
const player = useTemplateRef('player');
182+
const popover = useTemplateRef('popover');
183+
const container = useTemplateRef('video-container');
184+
const timeline = useTemplateRef('video-timeline');
185+
const progressTooltip = timeline.value?.progressTooltip;
187186
// const url = ref('');
188187
189188
const contextMenuItems = computed(() => {
@@ -730,7 +729,7 @@ function resetControlsTimeout() {
730729
controlsHideTimeout.value = window.setTimeout(() => {
731730
isShowingControls.value = false;
732731
popover.value?.handleClose();
733-
progressTooltip.value?.tooltipToggle(false);
732+
progressTooltip?.tooltipToggle(false);
734733
}, controlsHideTime);
735734
}
736735
@@ -792,25 +791,6 @@ function getPlayerInfo() {
792791
frameHealth.value = `${playbackQuality.droppedVideoFrames} / ${playbackQuality.totalVideoFrames}`;
793792
}
794793
795-
const handleProgressTooltip = throttle((event: MouseEvent) => {
796-
getProgressTooltip(event);
797-
requestAnimationFrame(() => {
798-
if (!progressTooltip.value) return;
799-
progressTooltip.value.calculateTooltipPosition(event);
800-
});
801-
}, 7);
802-
803-
const getProgressTooltip = (event: MouseEvent) => {
804-
if (!player.value || !timeDuration.value || !progressBar.value) return;
805-
const rect = progressBar.value.getBoundingClientRect();
806-
const offsetX = event.clientX - rect.left;
807-
const percent = offsetX / rect.width;
808-
809-
const time = percent < 0 ? 0 : Math.min(timeDuration.value, percent * timeDuration.value);
810-
811-
timeSeeking.value = toFormattedDuration(time, true, 'digital') ?? '00:00';
812-
};
813-
814794
const handleLoadSavedVolume = () => {
815795
const savedVolume = parseFloat(localStorage.getItem('videoVolume') ?? '');
816796
if (isNaN(savedVolume) || !player.value) {
@@ -1023,7 +1003,7 @@ defineExpose({
10231003
preload="metadata"
10241004
:class="[
10251005
`relative focus:outline-none object-contain h-full select-none`,
1026-
`${!stateVideo?.path ? 'aspect-video' : (isAudio || aspectRatio.isPortrait) && !isFullScreen ? ` max-h-[60vh]` : ' aspect-video'}`,
1006+
`${!stateVideo?.path ? 'aspect-video' : (isAudio || aspectRatio.isPortrait) && !isFullScreen ? ` max-h-[71vh]` : ' aspect-video'}`,
10271007
{ 'bg-black': !isAudio && !aspectRatio.isAspectVideo },
10281008
`${isShowingControls ? 'cursor-auto' : 'cursor-none'}`,
10291009
]"
@@ -1135,7 +1115,20 @@ defineExpose({
11351115
style="z-index: 7"
11361116
>
11371117
<!-- Heatmap and Timeline -->
1138-
<section class="flex-1 w-full rounded-full flex flex-col-reverse px-2 h-8 relative">
1118+
<VideoTimeline
1119+
ref="video-timeline"
1120+
:buffer-percentage="bufferPercentage"
1121+
:time-duration="timeDuration"
1122+
:time-elapsed-verbose="timeStrings.timeElapsedVerbose"
1123+
:video-button-offset="videoButtonOffset"
1124+
v-model="timeElapsed"
1125+
@seek="handleSeek"
1126+
@seek-preview="handleSeekPreview"
1127+
@key-bind="handleKeyBinds"
1128+
>
1129+
<VideoHeatmap :playback-data="playbackData" />
1130+
</VideoTimeline>
1131+
<!-- <section class="flex-1 w-full rounded-full flex flex-col-reverse px-2 h-8 relative">
11391132
<VideoTooltipSlider
11401133
ref="progress-tooltip"
11411134
tooltip-position="top"
@@ -1145,39 +1138,53 @@ defineExpose({
11451138
:offset="videoButtonOffset"
11461139
:tooltip-arrow="false"
11471140
/>
1148-
<input
1149-
@mousemove="handleProgressTooltip"
1150-
@pointerdown="handleSeekPreview"
1151-
@pointerup="handleSeek()"
1152-
@mouseenter="
1153-
() => {
1154-
if (!progressTooltip) return;
1155-
progressTooltip?.tooltipToggle();
1156-
}
1157-
"
1158-
@mouseleave="
1159-
() => {
1160-
if (!progressTooltip) return;
1161-
progressTooltip?.tooltipToggle(false);
1162-
}
1163-
"
1164-
@keydown.prevent="(e) => handleKeyBinds(e, true)"
1165-
ref="progress-bar"
1166-
placeholder="0"
1167-
v-model="timeElapsed"
1168-
type="range"
1169-
min="0"
1170-
max="100"
1171-
value="0"
1172-
step="0.01"
1173-
:class="[`peer w-full h-2 flex items-center slider timeline pointer-events-auto focus:outline-none`]"
1174-
:style="{
1175-
'--buffer': bufferPercentage,
1176-
}"
1177-
:aria-valuetext="timeStrings.timeElapsedVerbose"
1178-
/>
1179-
<VideoHeatmap :playback-data="playbackData" />
1180-
</section>
1141+
<div class="relative group h-2 bg-white/10 flex items-center hover:bg-white/20 pointer-events-auto">
1142+
<input
1143+
@mousemove="handleProgressTooltip"
1144+
@pointerdown="handleSeekPreview"
1145+
@pointerup="handleSeek()"
1146+
@mouseenter="
1147+
() => {
1148+
if (!progressTooltip) return;
1149+
progressTooltip?.tooltipToggle();
1150+
}
1151+
"
1152+
@mouseleave="
1153+
() => {
1154+
if (!progressTooltip) return;
1155+
progressTooltip?.tooltipToggle(false);
1156+
}
1157+
"
1158+
@keydown.prevent="(e) => handleKeyBinds(e, true)"
1159+
ref="progress-bar"
1160+
placeholder="0"
1161+
v-model="timeElapsed"
1162+
type="range"
1163+
min="0"
1164+
max="100"
1165+
value="0"
1166+
step="0.01"
1167+
:class="[`peer absolute left-0 top-0 w-full h-2 flex items-center slider timeline pointer-events-auto focus:outline-none -px-0.5`]"
1168+
:style="{
1169+
'--buffer': bufferPercentage,
1170+
'--progress-color': 'ffffff00',
1171+
'--thumb-color': 'ffffff00',
1172+
'--track-color': 'ffffff00',
1173+
}"
1174+
:aria-valuetext="timeStrings.timeElapsedVerbose"
1175+
/>
1176+
<div
1177+
class="h-1 group-hover:h-2 transition-all duration-200 ease-in-out w-full z-[5] rounded-full group-hover:rounded-[1px] overflow-clip pointer-events-none"
1178+
>
1179+
<div class="h-full bg-purple-600 z-[5] max-w-full relative" :style="`--progress-color: #111827; width: ${timeElapsed}%; `">
1180+
<button
1181+
class="size-1 group-hover:size-2 bg-white rounded-full -right-0.5 group-hover:-right-1 absolute transition-all duration-200 ease-in-out hover:bg-neutral-300"
1182+
></button>
1183+
</div>
1184+
</div>
1185+
</div>
1186+
1187+
</section> -->
11811188

11821189
<!-- Controls -->
11831190
<section class="w-full flex items-center gap-2 p-2 pointer-events-auto">

0 commit comments

Comments
 (0)