fix: [디자인QA] 슬라이드 인덱스 전환 시 영상이 처음부터 재생되도록 적용

This commit is contained in:
clkim
2025-12-03 15:43:21 +09:00
parent 73adfc9769
commit fd479483bb
15 changed files with 179 additions and 138 deletions

View File

@@ -1,92 +1,123 @@
<script setup lang="ts">
import type { ClassType } from '#layers/types/Common'
interface Props {
src: string
type?: 'mp4' | 'webm'
poster?: string
play?: boolean
autoplay?: boolean
muted?: boolean
loop?: boolean
playsinline?: boolean
bordered?: boolean
class?: ClassType
}
const props = withDefaults(defineProps<Props>(), {
type: 'mp4',
play: false,
play: true,
muted: true,
loop: true,
playsinline: true,
bordered: true,
})
const videoRef = ref<HTMLVideoElement | null>(null)
let pauseTimeoutId: ReturnType<typeof setTimeout> | null = null
const pauseTimer = ref<ReturnType<typeof setTimeout> | null>(null)
// autoplay prop 변경 시 재생/정지 제어
watch(
() => props.play,
shouldPlay => {
if (!videoRef.value) return
// 이전 타이머 정리
if (pauseTimeoutId) {
clearTimeout(pauseTimeoutId)
pauseTimeoutId = null
}
if (shouldPlay) {
videoRef.value.play().catch(err => {
console.warn('Video play failed:', err)
})
} else {
pauseTimeoutId = setTimeout(() => {
if (videoRef.value) {
videoRef.value.pause()
videoRef.value.currentTime = 0
}
pauseTimeoutId = null
}, 200)
}
const clearPauseTimer = () => {
if (pauseTimer.value) {
clearTimeout(pauseTimer.value)
pauseTimer.value = null
}
)
}
onBeforeUnmount(() => {
// 타이머 정리
if (pauseTimeoutId) {
clearTimeout(pauseTimeoutId)
pauseTimeoutId = null
const playVideo = async () => {
if (!videoRef.value) return
clearPauseTimer()
try {
await videoRef.value.play()
} catch (e) {
console.warn('Video play failed:', e)
}
})
}
const pauseVideo = () => {
if (!videoRef.value) return
clearPauseTimer()
// 새 타이머 설정
pauseTimer.value = setTimeout(() => {
if (videoRef.value) {
videoRef.value.pause()
videoRef.value.currentTime = 0
}
pauseTimer.value = null
}, 500)
}
const reloadVideo = async () => {
if (!videoRef.value) return
clearPauseTimer()
videoRef.value.currentTime = 0
videoRef.value.load()
if (props.play) {
await playVideo()
}
}
const syncPlayState = (shouldPlay: boolean) => {
if (shouldPlay) {
void playVideo()
} else {
pauseVideo()
}
}
// src 변경 시 비디오 다시 로드
watch(
() => props.src,
() => {
if (!videoRef.value) return
// 비디오 시간 초기화 및 새 소스 로드
videoRef.value.currentTime = 0
videoRef.value.load()
// 재생 중이었다면 다시 재생
if (props.play) {
nextTick(() => {
videoRef.value?.play().catch(err => {
console.warn('Video play failed:', err)
})
})
}
void reloadVideo()
}
)
// play 상태 변경 시 재생/일시정지
watch(
() => props.play,
shouldPlay => {
syncPlayState(!!shouldPlay)
}
)
onMounted(() => {
nextTick(() => {
if (props.play) {
playVideo()
}
})
})
onBeforeUnmount(() => {
clearPauseTimer()
})
</script>
<template>
<div v-if="props.src" :class="['video-box', props.class]">
<div
v-if="props.src"
:class="['video-box', { 'has-bordered': props.bordered }, props.class]"
>
<video
ref="videoRef"
:autoplay="props.autoplay"
:poster="props.poster"
:muted="props.muted"
:loop="props.loop"
:playsinline="props.playsinline"
playsinline
>
<source :src="props.src" :type="`video/${props.type}`" />
</video>
@@ -95,7 +126,10 @@ watch(
<style scoped>
.video-box {
@apply overflow-hidden relative rounded-[4px] md:rounded-[8px]
@apply overflow-hidden;
}
.video-box.has-bordered {
@apply relative rounded-[4px] md:rounded-[8px]
after:content-[''] after:absolute after:top-0 after:left-0 after:w-full after:h-full after:border after:border-white after:opacity-10 after:rounded-[4px] after:md:rounded-[8px];
}
.video-box video {