forked from zhongyidian/super_video
feat: 完善模块引导与学习进度体验
统一引导提示的持久化控制,补充学习进度入口与模块资源,避免用户返回页面时重复触发提示。 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
fc9633f371
commit
6f8252472f
114 files changed
+2758
-1798
No files matched your search
@@ -1,98 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, ref, watch } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import ImgButton from '@/components/Button/ImgButton.vue'
|
||||
// import { GuideSteps } from '@/components/Guide'
|
||||
import { IMAGE_PATHS } from '@/utils'
|
||||
const router = useRouter()
|
||||
|
||||
// 导入视频文件
|
||||
import videoadjustmentColor from '/video/vlog_renwuqujing_tiaose.mp4'
|
||||
const videoList = [videoadjustmentColor]
|
||||
|
||||
const videoRef = ref<HTMLVideoElement>()
|
||||
const currentVideoIndex = ref(0)
|
||||
|
||||
onMounted(() => {
|
||||
// 确保视频元素加载并自动播放
|
||||
if (videoRef.value) {
|
||||
videoRef.value.src = videoList[currentVideoIndex.value]
|
||||
videoRef.value.load()
|
||||
|
||||
// 尝试自动播放
|
||||
const playPromise = videoRef.value.play()
|
||||
|
||||
if (playPromise !== undefined) {
|
||||
playPromise.catch(error => {
|
||||
// 自动播放失败,可能是浏览器策略阻止,需要用户交互
|
||||
console.log('自动播放被阻止:', error)
|
||||
})
|
||||
}
|
||||
|
||||
// 监听视频结束事件
|
||||
videoRef.value.addEventListener('ended', playNextVideo)
|
||||
}
|
||||
})
|
||||
|
||||
// 播放下一个视频
|
||||
const playNextVideo = () => {
|
||||
if (currentVideoIndex.value < videoList.length - 1) {
|
||||
currentVideoIndex.value++
|
||||
if (videoRef.value) {
|
||||
videoRef.value.src = videoList[currentVideoIndex.value]
|
||||
videoRef.value.load()
|
||||
|
||||
// 自动播放下一个视频
|
||||
const playPromise = videoRef.value.play()
|
||||
|
||||
if (playPromise !== undefined) {
|
||||
playPromise.catch(error => {
|
||||
console.log('自动播放被阻止:', error)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 监听当前视频索引变化
|
||||
watch(currentVideoIndex, newIndex => {
|
||||
if (videoRef.value) {
|
||||
videoRef.value.src = videoList[newIndex]
|
||||
videoRef.value.load()
|
||||
}
|
||||
})
|
||||
|
||||
const handleClose = () => {
|
||||
router.back()
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<!--使用动态绑定和ref-->
|
||||
<video
|
||||
ref="videoRef"
|
||||
:src="videoList[currentVideoIndex]"
|
||||
type="video/mp4"
|
||||
controls
|
||||
preload="auto"
|
||||
class="video-player"
|
||||
autoplay
|
||||
>
|
||||
<p>你的浏览器不支持video标签.</p>
|
||||
</video>
|
||||
<ImgButton
|
||||
class="absolute right-0 top-0 z-10"
|
||||
:image="IMAGE_PATHS.COMPONENTS.BACK"
|
||||
@click="handleClose"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.video-player {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
}
|
||||
</style>
|
||||
@@ -1,38 +1,311 @@
|
||||
<script lang="ts" setup>
|
||||
import { IMAGE_PATHS, getStaticImage } from '@/utils'
|
||||
import { ImgButton } from '@/components/Button'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { GuideSteps, GuideStepsItem } from '@/components/Guide'
|
||||
import { getBackgroundImage, getStaticImage, IMAGE_PATHS } from '@/utils'
|
||||
import { ImgButton } from '@/components/Button'
|
||||
import { Overlay } from '@/components/Overlay'
|
||||
import { ref } from 'vue'
|
||||
import ImageFilter from '@/components/ImageFilter/ImageFilter.vue'
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
const back = () => {
|
||||
const selected = ref<number | undefined>(1)
|
||||
const btnState = ref(false)
|
||||
const screenshot = ref()
|
||||
const showModal = ref(false)
|
||||
const activeTab = ref<'color' | 'filter'>('color') // 'color' | 'filter'
|
||||
|
||||
const adjustmentList = ref([
|
||||
{ name: '亮度', value: 0, min: -100, max: 100 },
|
||||
{ name: '对比度', value: 0, min: -100, max: 100 },
|
||||
{ name: '饱和度', value: 0, min: -100, max: 100 },
|
||||
{ name: '曝光', value: 0, min: -100, max: 100 },
|
||||
{ name: '色调', value: 0, min: 0, max: 100 },
|
||||
{ name: '锐化', value: 0, min: 0, max: 100 },
|
||||
{ name: '模糊', value: 0, min: 0, max: 100 }
|
||||
])
|
||||
|
||||
const colorBarList = ref([
|
||||
{ id: 1, name: '原图', filter: 'none' },
|
||||
{ id: 2, name: '暖阳', filter: 'saturate(2.5) contrast(2.1)' },
|
||||
{ id: 3, name: '冷调', filter: 'hue-rotate(180deg) contrast(0.9)' },
|
||||
{ id: 4, name: '复古', filter: 'sepia(0.6) contrast(1.2) brightness(0.9)' },
|
||||
{ id: 5, name: '黑白', filter: 'grayscale(1)' },
|
||||
{ id: 6, name: '鲜艳', filter: 'sepia(0.1) saturate(2.5) contrast(2.1) brightness(1)' }
|
||||
])
|
||||
|
||||
// 获取指定参数的值
|
||||
// 获取指定参数的值
|
||||
const imageFilterRef = ref<InstanceType<typeof ImageFilter> | null>(null)
|
||||
|
||||
// 获取指定参数的值
|
||||
const getVal = (name: string) => {
|
||||
const val = adjustmentList.value.find(item => item.name === name)?.value || 0
|
||||
return Number(val)
|
||||
}
|
||||
|
||||
// 更新滤镜效果
|
||||
const updateFilter = () => {
|
||||
if (!imageFilterRef.value) return
|
||||
|
||||
// 如果开启了对比模式(显示原图),则不应用任何滤镜
|
||||
if (btnState.value) {
|
||||
imageFilterRef.value.setOptions({
|
||||
brightness: 0,
|
||||
contrast: 0,
|
||||
saturation: 0,
|
||||
exposure: 0,
|
||||
hue: 0,
|
||||
blur: 0,
|
||||
sharpen: 0,
|
||||
filter: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
const selectedFilter = colorBarList.value.find(v => v.id === selected.value)?.filter || 'none'
|
||||
|
||||
imageFilterRef.value.setOptions({
|
||||
brightness: getVal('亮度'),
|
||||
contrast: getVal('对比度'),
|
||||
saturation: getVal('饱和度'),
|
||||
exposure: getVal('曝光'),
|
||||
hue: getVal('色调'),
|
||||
blur: getVal('模糊'),
|
||||
sharpen: getVal('锐化'),
|
||||
filter: selectedFilter
|
||||
})
|
||||
}
|
||||
|
||||
const changeOriginalImgae = () => {
|
||||
btnState.value = !btnState.value
|
||||
updateFilter()
|
||||
}
|
||||
|
||||
// Tab 切换处理
|
||||
const handleTabChange = (tab: 'color' | 'filter') => {
|
||||
activeTab.value = tab
|
||||
// 切换时重置所有参数
|
||||
handleReset()
|
||||
}
|
||||
|
||||
// 重置所有参数
|
||||
const handleReset = () => {
|
||||
adjustmentList.value.forEach(item => (item.value = 0))
|
||||
selected.value = 1
|
||||
updateFilter()
|
||||
}
|
||||
|
||||
// 生成图片
|
||||
const handleGenerate = async () => {
|
||||
if (!imageFilterRef.value) return
|
||||
|
||||
try {
|
||||
const dataUrl = await imageFilterRef.value.generate()
|
||||
screenshot.value = dataUrl
|
||||
showModal.value = true
|
||||
} catch (error) {
|
||||
console.error('生成图片失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
const handleModalClose = () => {
|
||||
showModal.value = false
|
||||
screenshot.value = null
|
||||
}
|
||||
|
||||
const handleTryOther = () => {
|
||||
showModal.value = false
|
||||
}
|
||||
|
||||
const handleFinish = () => {
|
||||
router.push({
|
||||
name: 'Interview'
|
||||
})
|
||||
}
|
||||
|
||||
const handleClose = () => {
|
||||
router.back()
|
||||
}
|
||||
|
||||
const changeColor = (item: { id: number; name: string }) => {
|
||||
selected.value = item.id
|
||||
updateFilter()
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="relative w-full h-full overflow-hidden">
|
||||
<div class="w-full h-full relative pt-44">
|
||||
<!-- 返回 -->
|
||||
<ImgButton
|
||||
class="absolute right-0 top-0 z-20"
|
||||
class="absolute right-0 top-0 z-10"
|
||||
:image="IMAGE_PATHS.COMPONENTS.BACK"
|
||||
@click="back"
|
||||
@click="handleClose"
|
||||
/>
|
||||
<img class="absolute inset-0" :src="getStaticImage('img/背景.png')" alt="" />
|
||||
|
||||
<img class="absolute top-5 z-10" :src="getStaticImage('img/调色-标题.png')" alt="" />
|
||||
<!-- 背景图片-center -->
|
||||
<div
|
||||
class="absolute inset-0 bg-cover bg-center bg-no-repeat -z-10"
|
||||
:style="{
|
||||
backgroundImage: getBackgroundImage('after-editing/color/底图')
|
||||
}"
|
||||
></div>
|
||||
|
||||
<div class="relative h-full pt-28 overflow-y-scroll">
|
||||
<img :src="getStaticImage('interview/img/调色实践.png')" alt="" />
|
||||
<ImgButton
|
||||
class="absolute top-[1180px] left-[51.5%] -translate-x-1/2"
|
||||
image="after-editing/color/调色实践"
|
||||
@click="router.push({ name: 'InterviewColor' })"
|
||||
/>
|
||||
<div class="flex h-[759px] justify-center space-x-12">
|
||||
<!-- 效果图 -->
|
||||
<div
|
||||
class="relative w-[1273px] h-[732px] bg-no-repeat bg-cover mt-10 p-2 pb-[26px] pr-[26px]"
|
||||
:style="{ backgroundImage: getBackgroundImage('after-editing/color/图片框.png') }"
|
||||
>
|
||||
<ImageFilter ref="imageFilterRef" :src="getStaticImage('interview/color/访谈调色')" />
|
||||
|
||||
<!-- 开启对比 -->
|
||||
<ImgButton
|
||||
class="absolute top-2 right-6 flex-[0_0_200px]"
|
||||
@click="changeOriginalImgae"
|
||||
:image="
|
||||
btnState
|
||||
? 'after-editing/color/开启对比 - 可开启'
|
||||
: 'after-editing/color/开启对比 - 不可开启'
|
||||
"
|
||||
></ImgButton>
|
||||
</div>
|
||||
|
||||
<!-- 滤镜/参数配置 -->
|
||||
<div>
|
||||
<div
|
||||
class="w-[445px] h-[759px] bg-no-repeat bg-cover p-2 rounded-xl"
|
||||
:style="{ backgroundImage: getBackgroundImage('after-editing/color/小底板') }"
|
||||
>
|
||||
<!-- tab切换 -->
|
||||
<header class="flex h-[75px] items-center justify-center space-x-4">
|
||||
<div @click="handleTabChange('color')">
|
||||
<ImgButton
|
||||
:image="
|
||||
activeTab !== 'color'
|
||||
? 'after-editing/color/调色-暗'
|
||||
: 'after-editing/color/调色-亮'
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
<div @click="handleTabChange('filter')">
|
||||
<ImgButton
|
||||
:image="
|
||||
activeTab !== 'filter'
|
||||
? 'after-editing/color/滤镜-暗'
|
||||
: 'after-editing/color/滤镜-亮'
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
</header>
|
||||
<!-- tab-content -->
|
||||
<div class="px-4 h-[calc(100%-75px)]">
|
||||
<!-- 调色内容 -->
|
||||
<div
|
||||
v-show="activeTab === 'color'"
|
||||
class="space-y-6 h-full overflow-y-auto custom-scrollbar pt-4"
|
||||
>
|
||||
<div v-for="item in adjustmentList" :key="item.name" class="px-2">
|
||||
<div class="flex justify-between text-[#648ab2] text-2xl font-bold mb-2">
|
||||
<span>{{ item.name }}</span>
|
||||
<span>{{ item.value }}</span>
|
||||
</div>
|
||||
<input
|
||||
type="range"
|
||||
v-model.number="item.value"
|
||||
:min="item.min"
|
||||
:max="item.max"
|
||||
@change="updateFilter"
|
||||
class="w-full h-3 bg-[#b8cfe7] rounded-lg appearance-none cursor-pointer slider-thumb"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 滤镜内容 -->
|
||||
<div
|
||||
v-show="activeTab === 'filter'"
|
||||
class="space-y-10 h-full overflow-y-auto custom-scrollbar pt-10"
|
||||
>
|
||||
<div
|
||||
v-for="item in colorBarList"
|
||||
:key="item.id"
|
||||
:style="{
|
||||
backgroundImage:
|
||||
selected == item.id
|
||||
? getBackgroundImage('after-editing/color/滤镜-选中')
|
||||
: getBackgroundImage('after-editing/color/滤镜-未选中')
|
||||
}"
|
||||
:class="[
|
||||
'mx-auto w-[373px] h-[62px] text-white text-2xl font-bold flex items-center justify-center cursor-pointer bg-no-repeat bg-contain bg-center transition-all hover:scale-105',
|
||||
selected !== item.id && 'text-gray-700'
|
||||
]"
|
||||
@click="changeColor(item)"
|
||||
>
|
||||
<span>{{ item.name }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex justify-around mt-4">
|
||||
<!-- 重置 -->
|
||||
<ImgButton image="after-editing/color/重置" @click="handleReset" />
|
||||
<!-- 生成 -->
|
||||
<ImgButton image="after-editing/color/生成" @click="handleGenerate" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<GuideSteps :visible="true" position-offset-y="85%">
|
||||
<GuideStepsItem>这份长图文干货满满,滚动鼠标查看完整内容吧</GuideStepsItem>
|
||||
</GuideSteps>
|
||||
<!-- title -->
|
||||
<img class="absolute top-0 left-0" :src="getStaticImage('after-editing/color/标题-调色实践')" />
|
||||
|
||||
<!-- 回到顶部按钮 - 最后一页显示 -->
|
||||
<p class="absolute left-40 bottom-10 flex items-center">
|
||||
<img class="rotate-0 w-28 mr-4" src="@/assets/svg/deng.svg" />
|
||||
<span class="text-white text-2xl font-bold">
|
||||
推荐参数:亮度 90、对比度 100、饱和度 100、曝光 - 90、色调 0、锐化 100、模糊 0
|
||||
</span>
|
||||
</p>
|
||||
|
||||
<!-- 保存图片弹窗 -->
|
||||
<Overlay :visible="showModal" @close="handleModalClose">
|
||||
<div class="relative w-[1000px] h-[600px] flex items-center justify-center">
|
||||
<!-- 截图内容 -->
|
||||
<div class="bg-white p-2 rounded-md -rotate-6">
|
||||
<img :src="screenshot" alt="调色结果" />
|
||||
</div>
|
||||
<!-- <img :src="screenshot" alt="调色结果" class="max-w-full max-h-full object-contain rotate-2" /> -->
|
||||
<!-- 操作按钮 -->
|
||||
<div class="absolute bottom-1/2 -left-1/3 translate-y-1/2">
|
||||
<ImgButton image="after-editing/color/按钮-试试其他调色" @click="handleTryOther" />
|
||||
</div>
|
||||
<div class="absolute bottom-1/2 -right-1/3 translate-y-1/2">
|
||||
<ImgButton image="after-editing/color/按钮-结束调色实践" @click="handleFinish" />
|
||||
</div>
|
||||
</div>
|
||||
</Overlay>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
/* 自定义滑块样式 */
|
||||
.slider-thumb::-webkit-slider-thumb {
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
background: #92c6f2;
|
||||
border: 3px solid #ffffff;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
box-shadow: 4px 4px 2px rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
|
||||
.slider-thumb::-moz-range-thumb {
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
background: #92c6f2;
|
||||
border: 3px solid #ffffff;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
box-shadow: 4px 4px 2px rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
</style>
|
||||
@@ -1,309 +1,38 @@
|
||||
<script lang="ts" setup>
|
||||
import router from '@/router'
|
||||
import { getBackgroundImage, getStaticImage, IMAGE_PATHS } from '@/utils'
|
||||
import { IMAGE_PATHS, getStaticImage } from '@/utils'
|
||||
import { ImgButton } from '@/components/Button'
|
||||
import { Overlay } from '@/components/Overlay'
|
||||
import { ref } from 'vue'
|
||||
import ImageFilter from '@/components/ImageFilter/ImageFilter.vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
// import { GuideSteps, GuideStepsItem } from '@/components/Guide'
|
||||
|
||||
const selected = ref<number | undefined>(1)
|
||||
const btnState = ref(false)
|
||||
const screenshot = ref()
|
||||
const showModal = ref(false)
|
||||
const activeTab = ref<'color' | 'filter'>('color') // 'color' | 'filter'
|
||||
const router = useRouter()
|
||||
|
||||
const adjustmentList = ref([
|
||||
{ name: '亮度', value: 0, min: -100, max: 100 },
|
||||
{ name: '对比度', value: 0, min: -100, max: 100 },
|
||||
{ name: '饱和度', value: 0, min: -100, max: 100 },
|
||||
{ name: '曝光', value: 0, min: -100, max: 100 },
|
||||
{ name: '色调', value: 0, min: 0, max: 100 },
|
||||
{ name: '锐化', value: 0, min: 0, max: 100 },
|
||||
{ name: '模糊', value: 0, min: 0, max: 100 }
|
||||
])
|
||||
|
||||
const colorBarList = ref([
|
||||
{ id: 1, name: '原图', filter: 'none' },
|
||||
{ id: 2, name: '暖阳', filter: 'saturate(2.5) contrast(2.1)' },
|
||||
{ id: 3, name: '冷调', filter: 'hue-rotate(180deg) contrast(0.9)' },
|
||||
{ id: 4, name: '复古', filter: 'sepia(0.6) contrast(1.2) brightness(0.9)' },
|
||||
{ id: 5, name: '黑白', filter: 'grayscale(1)' },
|
||||
{ id: 6, name: '鲜艳', filter: 'sepia(0.1) saturate(2.5) contrast(2.1) brightness(1)' }
|
||||
])
|
||||
|
||||
// 获取指定参数的值
|
||||
// 获取指定参数的值
|
||||
const imageFilterRef = ref<InstanceType<typeof ImageFilter> | null>(null)
|
||||
|
||||
// 获取指定参数的值
|
||||
const getVal = (name: string) => {
|
||||
const val = adjustmentList.value.find(item => item.name === name)?.value || 0
|
||||
return Number(val)
|
||||
}
|
||||
|
||||
// 更新滤镜效果
|
||||
const updateFilter = () => {
|
||||
if (!imageFilterRef.value) return
|
||||
|
||||
// 如果开启了对比模式(显示原图),则不应用任何滤镜
|
||||
if (btnState.value) {
|
||||
imageFilterRef.value.setOptions({
|
||||
brightness: 0,
|
||||
contrast: 0,
|
||||
saturation: 0,
|
||||
exposure: 0,
|
||||
hue: 0,
|
||||
blur: 0,
|
||||
sharpen: 0,
|
||||
filter: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
const selectedFilter = colorBarList.value.find(v => v.id === selected.value)?.filter || 'none'
|
||||
|
||||
imageFilterRef.value.setOptions({
|
||||
brightness: getVal('亮度'),
|
||||
contrast: getVal('对比度'),
|
||||
saturation: getVal('饱和度'),
|
||||
exposure: getVal('曝光'),
|
||||
hue: getVal('色调'),
|
||||
blur: getVal('模糊'),
|
||||
sharpen: getVal('锐化'),
|
||||
filter: selectedFilter
|
||||
})
|
||||
}
|
||||
|
||||
const changeOriginalImgae = () => {
|
||||
btnState.value = !btnState.value
|
||||
updateFilter()
|
||||
}
|
||||
|
||||
// Tab 切换处理
|
||||
const handleTabChange = (tab: 'color' | 'filter') => {
|
||||
activeTab.value = tab
|
||||
// 切换时重置所有参数
|
||||
handleReset()
|
||||
}
|
||||
|
||||
// 重置所有参数
|
||||
const handleReset = () => {
|
||||
adjustmentList.value.forEach(item => (item.value = 0))
|
||||
selected.value = 1
|
||||
updateFilter()
|
||||
}
|
||||
|
||||
// 生成图片
|
||||
const handleGenerate = async () => {
|
||||
if (!imageFilterRef.value) return
|
||||
|
||||
try {
|
||||
const dataUrl = await imageFilterRef.value.generate()
|
||||
screenshot.value = dataUrl
|
||||
showModal.value = true
|
||||
} catch (error) {
|
||||
console.error('生成图片失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
const handleModalClose = () => {
|
||||
showModal.value = false
|
||||
screenshot.value = null
|
||||
}
|
||||
|
||||
const handleTryOther = () => {
|
||||
showModal.value = false
|
||||
}
|
||||
|
||||
const handleFinish = () => {
|
||||
router.push({
|
||||
name: 'Film'
|
||||
})
|
||||
}
|
||||
|
||||
const handleClose = () => {
|
||||
const back = () => {
|
||||
router.back()
|
||||
}
|
||||
|
||||
const changeColor = (item: { id: number; name: string }) => {
|
||||
selected.value = item.id
|
||||
updateFilter()
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="w-full h-full relative pt-44">
|
||||
<!-- 返回 -->
|
||||
<div class="relative w-full h-full overflow-hidden">
|
||||
<ImgButton
|
||||
class="absolute right-0 top-0 z-10"
|
||||
class="absolute right-0 top-0 z-20"
|
||||
:image="IMAGE_PATHS.COMPONENTS.BACK"
|
||||
@click="handleClose"
|
||||
@click="back"
|
||||
/>
|
||||
<img class="absolute inset-0" :src="getStaticImage('img/背景.png')" alt="" />
|
||||
|
||||
<!-- 背景图片-center -->
|
||||
<div
|
||||
class="absolute inset-0 bg-cover bg-center bg-no-repeat -z-10"
|
||||
:style="{
|
||||
backgroundImage: getBackgroundImage('after-editing/color/底图')
|
||||
}"
|
||||
></div>
|
||||
<img class="absolute top-5 z-10" :src="getStaticImage('img/调色-标题.png')" alt="" />
|
||||
|
||||
<div class="flex h-[759px] justify-center space-x-12">
|
||||
<!-- 效果图 -->
|
||||
<div
|
||||
class="relative w-[1273px] h-[732px] bg-no-repeat bg-cover mt-10 p-2 pb-[26px] pr-[26px]"
|
||||
:style="{ backgroundImage: getBackgroundImage('after-editing/color/图片框.png') }"
|
||||
>
|
||||
<ImageFilter ref="imageFilterRef" :src="getStaticImage('after-editing/color/film_img')" />
|
||||
|
||||
<!-- 开启对比 -->
|
||||
<ImgButton
|
||||
class="absolute top-2 right-6 flex-[0_0_200px]"
|
||||
@click="changeOriginalImgae"
|
||||
:image="
|
||||
btnState
|
||||
? 'after-editing/color/开启对比 - 可开启'
|
||||
: 'after-editing/color/开启对比 - 不可开启'
|
||||
"
|
||||
></ImgButton>
|
||||
</div>
|
||||
|
||||
<!-- 滤镜/参数配置 -->
|
||||
<div>
|
||||
<div
|
||||
class="w-[445px] h-[759px] bg-no-repeat bg-cover p-2 rounded-xl"
|
||||
:style="{ backgroundImage: getBackgroundImage('after-editing/color/小底板') }"
|
||||
>
|
||||
<!-- tab切换 -->
|
||||
<header class="flex h-[75px] items-center justify-center space-x-4">
|
||||
<div @click="handleTabChange('color')">
|
||||
<ImgButton
|
||||
:image="
|
||||
activeTab !== 'color'
|
||||
? 'after-editing/color/调色-暗'
|
||||
: 'after-editing/color/调色-亮'
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
<div @click="handleTabChange('filter')">
|
||||
<ImgButton
|
||||
:image="
|
||||
activeTab !== 'filter'
|
||||
? 'after-editing/color/滤镜-暗'
|
||||
: 'after-editing/color/滤镜-亮'
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
</header>
|
||||
<!-- tab-content -->
|
||||
<div class="px-4 h-[calc(100%-75px)]">
|
||||
<!-- 调色内容 -->
|
||||
<div
|
||||
v-show="activeTab === 'color'"
|
||||
class="space-y-6 h-full overflow-y-auto custom-scrollbar pt-4"
|
||||
>
|
||||
<div v-for="item in adjustmentList" :key="item.name" class="px-2">
|
||||
<div class="flex justify-between text-[#648ab2] text-2xl font-bold mb-2">
|
||||
<span>{{ item.name }}</span>
|
||||
<span>{{ item.value }}</span>
|
||||
</div>
|
||||
<input
|
||||
type="range"
|
||||
v-model.number="item.value"
|
||||
:min="item.min"
|
||||
:max="item.max"
|
||||
@change="updateFilter"
|
||||
class="w-full h-3 bg-[#b8cfe7] rounded-lg appearance-none cursor-pointer slider-thumb"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 滤镜内容 -->
|
||||
<div
|
||||
v-show="activeTab === 'filter'"
|
||||
class="space-y-10 h-full overflow-y-auto custom-scrollbar pt-10"
|
||||
>
|
||||
<div
|
||||
v-for="item in colorBarList"
|
||||
:key="item.id"
|
||||
:style="{
|
||||
backgroundImage:
|
||||
selected == item.id
|
||||
? getBackgroundImage('after-editing/color/滤镜-选中')
|
||||
: getBackgroundImage('after-editing/color/滤镜-未选中')
|
||||
}"
|
||||
:class="[
|
||||
'mx-auto w-[373px] h-[62px] text-white text-2xl font-bold flex items-center justify-center cursor-pointer bg-no-repeat bg-contain bg-center transition-all hover:scale-105',
|
||||
selected !== item.id && 'text-gray-700'
|
||||
]"
|
||||
@click="changeColor(item)"
|
||||
>
|
||||
<span>{{ item.name }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex justify-around mt-4">
|
||||
<!-- 重置 -->
|
||||
<ImgButton image="after-editing/color/重置" @click="handleReset" />
|
||||
<!-- 生成 -->
|
||||
<ImgButton image="after-editing/color/生成" @click="handleGenerate" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="relative h-full pt-28 overflow-y-scroll">
|
||||
<img :src="getStaticImage('interview/img/调色实践.png')" alt="" />
|
||||
<ImgButton
|
||||
class="absolute top-[1180px] left-[51.5%] -translate-x-1/2"
|
||||
image="after-editing/color/调色实践"
|
||||
@click="router.push({ name: 'InterviewColorPractice' })"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- title -->
|
||||
<img class="absolute top-0 left-0" :src="getStaticImage('after-editing/color/标题-调色实践')" />
|
||||
|
||||
<!-- 回到顶部按钮 - 最后一页显示 -->
|
||||
<p class="absolute left-40 bottom-10 flex items-center">
|
||||
<img class="rotate-0 w-28 mr-4" src="@/assets/svg/deng.svg" />
|
||||
<span class="text-white text-2xl font-bold">
|
||||
推荐参数:亮度 90、对比度 100、饱和度 100、曝光 - 90、色调 0、锐化 100、模糊 0
|
||||
</span>
|
||||
</p>
|
||||
|
||||
<!-- 保存图片弹窗 -->
|
||||
<Overlay :visible="showModal" @close="handleModalClose">
|
||||
<div class="relative w-[1000px] h-[600px] flex items-center justify-center">
|
||||
<!-- 截图内容 -->
|
||||
<div class="bg-white p-2 rounded-md -rotate-6">
|
||||
<img :src="screenshot" alt="调色结果" />
|
||||
</div>
|
||||
<!-- <img :src="screenshot" alt="调色结果" class="max-w-full max-h-full object-contain rotate-2" /> -->
|
||||
<!-- 操作按钮 -->
|
||||
<div class="absolute bottom-1/2 -left-1/3 translate-y-1/2">
|
||||
<ImgButton image="after-editing/color/按钮-试试其他调色" @click="handleTryOther" />
|
||||
</div>
|
||||
<div class="absolute bottom-1/2 -right-1/3 translate-y-1/2">
|
||||
<ImgButton image="after-editing/color/按钮-结束调色实践" @click="handleFinish" />
|
||||
</div>
|
||||
</div>
|
||||
</Overlay>
|
||||
<!-- <GuideSteps :visible="true" position-offset-y="85%">
|
||||
<GuideStepsItem>这份长图文干货满满,滚动鼠标查看完整内容吧</GuideStepsItem>
|
||||
</GuideSteps> -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
/* 自定义滑块样式 */
|
||||
.slider-thumb::-webkit-slider-thumb {
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
background: #92c6f2;
|
||||
border: 3px solid #ffffff;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
box-shadow: 4px 4px 2px rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
|
||||
.slider-thumb::-moz-range-thumb {
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
background: #92c6f2;
|
||||
border: 3px solid #ffffff;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
box-shadow: 4px 4px 2px rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
</style>
|
||||
@@ -1,23 +1,38 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { nextTick, ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { ImgButton } from '@/components/Button'
|
||||
import { Video } from '@/components'
|
||||
import { Video, GuideSteps, GuideStepsItem } from '@/components'
|
||||
import { IMAGE_PATHS, getPublicPath } from '@/utils'
|
||||
import { useInterviewStore } from '@/stores/modules/interview'
|
||||
|
||||
const router = useRouter()
|
||||
const interviewStore = useInterviewStore()
|
||||
const guideKey = 'interview-export-publish-intro'
|
||||
const playing = ref(false)
|
||||
const showGuide = ref(!interviewStore.guideShown[guideKey])
|
||||
|
||||
if (showGuide.value) {
|
||||
interviewStore.markGuideShown(guideKey)
|
||||
}
|
||||
|
||||
const handleBack = () => {
|
||||
router.back()
|
||||
}
|
||||
|
||||
const handleComplete = () => {
|
||||
showGuide.value = false
|
||||
nextTick(() => {
|
||||
playing.value = true
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="relative w-full h-full overflow-hidden bg-black">
|
||||
<Video
|
||||
class="w-full h-full"
|
||||
:src="getPublicPath('video/on-site/dygmk_zlsxhbj.mp4')"
|
||||
:src="getPublicPath('video/interview/publish/ftmk_jsyrgs_.mp4')"
|
||||
v-model="playing"
|
||||
:muted="false"
|
||||
:volume="1"
|
||||
@@ -29,5 +44,16 @@ const handleBack = () => {
|
||||
:image="IMAGE_PATHS.COMPONENTS.BACK"
|
||||
@click="handleBack"
|
||||
/>
|
||||
|
||||
<GuideSteps
|
||||
v-if="showGuide"
|
||||
position-offset-y="85%"
|
||||
@complete="handleComplete"
|
||||
@skip="handleComplete"
|
||||
>
|
||||
<GuideStepsItem :audio-src="getPublicPath('audio/py_3_013.MP3')">
|
||||
现在一起来看看导出后的影片效果吧!
|
||||
</GuideStepsItem>
|
||||
</GuideSteps>
|
||||
</div>
|
||||
</template>
|
||||
Reference in new issue
Block a user