Files
super_video/src/views/basic-elements/composition/index.vue
T
2026-05-09 09:53:06 +08:00

220 lines
6.2 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, onMounted, ref } from 'vue'
import { useRouter } from 'vue-router'
import ImgButton from '@/components/Button/ImgButton.vue'
import StepNavigation from '@/components/Guide/StepNavigation.vue'
import { Video } from '@/components'
import { getPublicPath, getStaticImage, IMAGE_PATHS } from '@/utils'
import { GuideSteps, GuideStepsItem } from '@/components/Guide'
import { useBasicElementsStore } from '@/stores/modules/basic-elements'
const router = useRouter()
const basicElementsStore = useBasicElementsStore()
const introGuideKey = 'basic-composition-intro'
const showIntroGuide = ref(!basicElementsStore.guideShown[introGuideKey])
if (showIntroGuide.value) {
basicElementsStore.markGuideShown(introGuideKey)
}
const props = defineProps<{
label: string
}>()
// 受控播放开关(用于切换到下一个视频时自动播放)
const playing = ref(false)
const status = ref(false)
const guideStepIndex = ref(0)
// 步骤导航数据
const navigationSteps = ref([
{
id: 'step1',
label: '视觉素养',
content: '如何选择构图',
videoUrl: getPublicPath('video/new/composition/xsdh_rhxzgt_batch.mp4'),
type: 'planning' as const,
},
{
id: 'step2',
label: '视觉素养',
content: '九种经典构图方式',
videoUrl: getPublicPath('video/new/composition/xsdh_jzgtf_batch.mp4'),
type: 'planning' as const,
},
])
// 步骤导航相关状态
const showStepNavigation = ref(false)
const currentNavigationStep = ref(0) // 对应视频索引,从0开始
// 视频列表
const currentVideoIndex = ref(0)
//按顺序播放
const currentVideo = computed(
() => navigationSteps.value[currentVideoIndex.value].videoUrl,
)
const handleVideoChange = () => {
if (currentVideoIndex.value < navigationSteps.value.length - 1) {
currentVideoIndex.value++
currentNavigationStep.value = currentVideoIndex.value
} else {
status.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
// 先重置播放状态,确保 v-model 触发 watcher
playing.value = false
nextTick(() => {
playing.value = true
})
}
const handleStepNavigationClose = () => {
showStepNavigation.value = false
}
const handleIntroGuideComplete = () => {
nextTick(() => {
playing.value = true
})
}
onMounted(() => {})
</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 step-navigation-btn"
@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="showIntroGuide"
:highlight-selector="guideStepIndex === 3 ? '.step-navigation-btn' : ''"
position-offset-y="85%"
@step-change="(_: any, index: number) => (guideStepIndex = index)"
@complete="handleIntroGuideComplete"
>
<GuideStepsItem :audio-src="getPublicPath('audio/py_1_005.MP3')">
你有没有过这样的感觉?画面里什么都有,却不知道重点在哪里?
</GuideStepsItem>
<GuideStepsItem :audio-src="getPublicPath('audio/py_1_006.MP3')">
很多时候,不是内容不够好,而是需要有构图思维。
</GuideStepsItem>
<GuideStepsItem :audio-src="getPublicPath('audio/py_1_007.MP3')">
所谓构图,就是在画面中对元素进行选择、取舍和安排
</GuideStepsItem>
<GuideStepsItem :audio-src="getPublicPath('audio/py_1_008.MP3')">
<div class="flex flex-wrap items-center justify-start">
<div>一起来看看吧~</div>
<div class="text-yellow-400">
点击
<button
class="w-10 h-10 bg-blue-500/80 hover:bg-blue-500 rounded-full inline-flex items-center justify-center text-white transition-all duration-200"
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>
可以切换不同的学习内容!
</div>
</div>
</GuideStepsItem>
</GuideSteps>
</div>
</template>
<style scoped>
video {
width: 100%;
height: 100%;
/* 核心:保持视频比例,多余部分被裁剪 */
object-fit: cover;
}
</style>