Files
super_video/src/views/basic-elements/camera-movement/index.vue
T

205 lines
5.6 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script lang="ts" setup>
import { computed, nextTick, ref } from 'vue'
import { useRouter } from 'vue-router'
import ImgButton from '@/components/Button/ImgButton.vue'
import { StepNavigation } from '@/components/Guide'
import { Video } from '@/components'
import { getBackgroundImage, getPublicPath, getStaticImage, IMAGE_PATHS } from '@/utils'
// 运镜初识 - 暂时用运镜的基本方式代替
import video1 from '/video/new/xinshou_yunjingdejibenfangshi.mp4'
import video2 from '/video/new/xinshou_zhengquedeshoujipaishe.mp4'
import video3 from '/video/new/xinshou_yunjingdejibenfangshi.mp4'
import { GuideSteps, GuideStepsItem } from '@/components/Guide'
const router = useRouter()
const props = defineProps<{
label: string
}>()
// 受控播放开关(用于切换到下一个视频时自动播放)
const playing = ref(false)
const status = ref(false)
const showCameraMovementGuide = ref(false)
const cameraMovementGuideShown = ref(false)
const handleIntroGuideComplete = () => {
nextTick(() => {
playing.value = true
})
}
const handleCameraMovementGuideComplete = () => {
showCameraMovementGuide.value = false
cameraMovementGuideShown.value = true
nextTick(() => {
playing.value = true
})
}
// 步骤导航数据
const navigationSteps = ref([
// {
// id: 'step1',
// label: '视觉素养',
// content: '运镜初识',
// type: 'planning' as const
// },
{
id: 'step2',
label: '视觉素养',
content: '正确的手机拍摄持机姿势',
type: 'planning' as const
},
{
id: 'step3',
label: '视觉素养',
content: '运镜的基本方式',
type: 'planning' as const
}
])
// 步骤导航相关状态
const showStepNavigation = ref(false)
const currentNavigationStep = ref(0) // 对应视频索引,从0开始
// 视频列表
const videoList = [video2, video3]
const currentVideoIndex = ref(0)
//按顺序播放
const currentVideo = computed(() => videoList[currentVideoIndex.value])
const handleVideoChange = () => {
if (currentVideoIndex.value < videoList.length - 1) {
currentVideoIndex.value++
currentNavigationStep.value = currentVideoIndex.value
} else {
status.value = true
return
}
// 切到“运镜的基本方式”(index 1)时,先暂停并展示引导对话
if (currentVideoIndex.value === 1 && !cameraMovementGuideShown.value) {
playing.value = false
showCameraMovementGuide.value = true
return
}
nextTick(() => {
// 使用受控播放来触发下一个视频自动播放
playing.value = true
})
}
const handleClose = () => {
router.back()
}
const label = computed(() => props.label as string)
const handlePlay = () => {}
const onEnded = () => {
handleVideoChange()
}
// 步骤导航事件处理
const handleStepClick = (index: number) => {
currentVideoIndex.value = index
currentNavigationStep.value = index
nextTick(() => {
playing.value = true
})
}
const handleStepNavigationClose = () => {
showStepNavigation.value = false
}
</script>
<template>
<div class="w-full h-full relative bg-white/10">
<ImgButton
class="absolute right-0 top-0 z-10"
:image="IMAGE_PATHS.COMPONENTS.BACK"
@click="handleClose"
/>
<!-- 步骤导航按钮 -->
<button
class="absolute right-4 top-1/2 z-10 -translate-y-1/2 w-12 h-12 bg-blue-500/80 hover:bg-blue-500 rounded-full flex items-center justify-center text-white transition-all duration-200"
@click="showStepNavigation = true"
title="步骤导航"
>
<svg width="20" height="20" viewBox="0 0 20 20" fill="none">
<path
d="M3 4H17M3 8H17M3 12H17M3 16H17"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
/>
</svg>
</button>
<Video
:src="currentVideo"
v-model="playing"
:muted="false"
:volume="1"
:controls="true"
:hideNativeControls="true"
:anchors="[]"
@play="handlePlay"
@ended="onEnded"
>
<template v-slot:overlay>
<ImgButton
class="absolute right-0 top-0 z-10 pointer-events-auto"
:image="IMAGE_PATHS.COMPONENTS.BACK"
@click="handleClose"
/>
</template>
</Video>
<!-- 步骤导航抽屉 -->
<StepNavigation
:visible="showStepNavigation"
:background-image="getStaticImage('work/vlog背景全屏待定')"
:steps="navigationSteps"
:current-step-index="currentNavigationStep"
@step-click="handleStepClick"
@close="handleStepNavigationClose"
/>
<GuideSteps :visible="true" position-offset-y="85%" @complete="handleIntroGuideComplete">
<GuideStepsItem :audio-src="getPublicPath('audio/py_1_014.MP3')">
想拍出好的视频效果,画面稳定性是最基本的要求。
</GuideStepsItem>
<GuideStepsItem :audio-src="getPublicPath('audio/py_1_015.MP3')">
接下来,我们来学习正确的持机方式和拍摄姿势。
</GuideStepsItem>
</GuideSteps>
<GuideSteps
v-if="showCameraMovementGuide"
position-offset-y="85%"
@complete="handleCameraMovementGuideComplete"
>
<GuideStepsItem :audio-src="getPublicPath('audio/py_1_016.MP3')">
当我们将画面稳定之后,就来到了镜头语言。
</GuideStepsItem>
<GuideStepsItem :audio-src="getPublicPath('audio/py_1_017.MP3')">
同一个场景,用不同运镜,表达就不一样。跟着我,去了解5种基础运镜吧~
</GuideStepsItem>
</GuideSteps>
</div>
</template>
<style scoped>
video {
width: 100%;
height: 100%;
/* 核心:保持视频比例,多余部分被裁剪 */
object-fit: cover;
}
</style>