Merge pull request #9 from cikezhu/main

重写SlideView组件
This commit is contained in:
jxxghp
2023-08-03 15:32:31 +08:00
committed by GitHub
6 changed files with 246 additions and 301 deletions
+131
View File
@@ -0,0 +1,131 @@
<script lang="ts" setup>
import SlideViewTitle from '@/components/slide/SlideViewTitle.vue'
// 输入参数
const props = defineProps({
linkurl: String,
title: String,
})
// 元素
const slideview_content = ref()
// 分页切换状态
const disabled = ref(0)
// 所有卡片数量
let slide_card_length: number
// 卡片间距
let slide_gap_px: number
// 卡片宽度
let card_width: number
// 容器最多显示N张卡片
let card_max: number
// 当前定位
let card_current: number
// 分页切换
function slideNext(next: boolean) {
let run_to_left_px
if (next) {
const card_index = card_current + card_max
run_to_left_px = card_index * card_width
if (run_to_left_px >= slideview_content.value.scrollWidth - slideview_content.value.clientWidth)
run_to_left_px = slideview_content.value.scrollWidth - slideview_content.value.clientWidth
// console.log(`最多显示: ${card_max} 当前起点: ${card_current} 目标起点: ${card_index} 卡片宽度: ${card_width}`)
}
else {
const card_index = card_current - card_max
run_to_left_px = card_index * card_width
if (run_to_left_px <= 0)
run_to_left_px = 0
// console.log(`最多显示: ${card_max} 当前起点: ${card_current} 目标起点: ${card_index} 卡片宽度: ${card_width}`)
}
slideview_content.value.scrollTo({
top: 0,
left: run_to_left_px,
behavior: 'smooth',
})
}
// 计算最大显示数量
function countMaxNumber() {
slide_card_length = slideview_content.value.children.length
card_width = slideview_content.value.firstElementChild.getBoundingClientRect().width
slide_gap_px = (slideview_content.value.scrollWidth / slide_card_length) - card_width
card_width += slide_gap_px
card_max = Math.trunc(slideview_content.value.clientWidth / card_width)
countDisabled()
}
// 修改分页切换按钮状态
function countDisabled() {
card_current = slideview_content.value.scrollLeft === 0 ? 0 : Math.trunc((slideview_content.value.scrollLeft + card_width / 2) / card_width)
if (slide_card_length * card_width <= slideview_content.value.clientWidth)
disabled.value = 3
else if (slideview_content.value.scrollLeft === 0)
disabled.value = 0
else if (slideview_content.value.scrollLeft >= slideview_content.value.scrollWidth - slideview_content.value.clientWidth - 2)
disabled.value = 2
else
disabled.value = 1
}
// 组件加载完成
onMounted(() => {
// 初次获取元素参数
countMaxNumber()
// 窗口大小发生改变时
window.addEventListener('resize', countMaxNumber)
})
onUnmounted(() => {
// 卸载事件
window.removeEventListener('resize', countMaxNumber)
})
</script>
<template>
<div class="flex justify-between mt-3">
<slot name="title">
<SlideViewTitle v-bind="props" />
</slot>
<div v-if="disabled !== 3" class="me-1 d-none d-md-flex">
<VBtn
class="rounded-circle"
variant="text"
icon="mdi-chevron-left"
color="grey"
:disabled="disabled === 0"
@click="slideNext(false)"
/>
<VBtn
class="rounded-circle"
variant="text"
icon="mdi-chevron-right"
color="grey"
:disabled="disabled === 2"
@click="slideNext(true)"
/>
</div>
</div>
<div
ref="slideview_content"
class="slideview_content grid grid-rows-1 grid-flow-col gap-4 p-3"
@scroll="countDisabled"
>
<slot name="content" />
</div>
</template>
<style lang="scss" scoped>
.slideview_content {
-ms-overflow-style: none !important;
overflow-x: scroll !important;
overflow-y: hidden !important;
overscroll-behavior-x: contain !important;
scrollbar-width: none !important;
}
.slideview_content::-webkit-scrollbar {
display: none;
}
</style>
+24
View File
@@ -0,0 +1,24 @@
<script lang="ts" setup>
// 输入参数
const props = defineProps({
linkurl: String,
title: String,
})
</script>
<template>
<div
class="ms-1"
>
<RouterLink
:to="props.linkurl ? props.linkurl : ''"
class="slider-title"
>
<span>{{ props.title }}</span>
<VIcon
icon="mdi-arrow-right-circle-outline"
class="ms-1"
/>
</RouterLink>
</div>
</template>
+40 -173
View File
@@ -4,185 +4,52 @@ import MediaCardSlideView from '@/views/discover/MediaCardSlideView.vue'
<template> <template>
<div> <div>
<MediaCardSlideView apipath="tmdb/trending"> <MediaCardSlideView
<template #title="{ loaded }"> apipath="tmdb/trending"
<div linkurl="/browse/tmdb/trending?title=流行趋势"
v-if="loaded" title="流行趋势"
class="slider-header ms-1" />
>
<RouterLink
to="/browse/tmdb/trending?title=流行趋势"
class="slider-title"
>
<span>流行趋势</span>
<VIcon
icon="mdi-arrow-right-circle-outline"
class="ms-1"
/>
</RouterLink>
</div>
</template>
</MediaCardSlideView>
<MediaCardSlideView apipath="tmdb/movies"> <MediaCardSlideView
<template #title="{ loaded }"> apipath="tmdb/movies"
<div linkurl="/browse/tmdb/movies?title=热门电影"
v-if="loaded" title="热门电影"
class="slider-header mt-3 ms-1" />
>
<RouterLink
to="/browse/tmdb/movies?title=热门电影"
class="slider-title"
>
<span>热门电影</span>
<VIcon
icon="mdi-arrow-right-circle-outline"
class="ms-1"
/>
</RouterLink>
</div>
</template>
</MediaCardSlideView>
<MediaCardSlideView apipath="tmdb/tvs"> <MediaCardSlideView
<template #title="{ loaded }"> apipath="tmdb/tvs"
<div linkurl="/browse/tmdb/tvs?title=热门电视剧"
v-if="loaded" title="热门电视剧"
class="slider-header mt-3 ms-1" />
>
<RouterLink
to="/browse/tmdb/tvs?title=热门电视剧"
class="slider-title"
>
<span>热门电视剧</span>
<VIcon
icon="mdi-arrow-right-circle-outline"
class="ms-1"
/>
</RouterLink>
</div>
</template>
</MediaCardSlideView>
<MediaCardSlideView apipath="douban/movies"> <MediaCardSlideView
<template #title="{ loaded }"> apipath="douban/movies"
<div linkurl="/browse/douban/movies?title=最新电影"
v-if="loaded" title="最新电影"
class="slider-header mt-3 ms-1" />
>
<RouterLink
to="/browse/douban/movies?title=最新电影"
class="slider-title"
>
<span>最新电影</span>
<VIcon
icon="mdi-arrow-right-circle-outline"
class="ms-1"
/>
</RouterLink>
</div>
</template>
</MediaCardSlideView>
<MediaCardSlideView apipath="douban/tvs"> <MediaCardSlideView
<template #title="{ loaded }"> apipath="douban/tvs"
<div linkurl="/browse/douban/tvs?title=最新电视剧"
v-if="loaded" title="最新电视剧"
class="slider-header mt-3 ms-1" />
>
<RouterLink
to="/browse/douban/tvs?title=最新电视剧"
class="slider-title"
>
<span>最新电视剧</span>
<VIcon
icon="mdi-arrow-right-circle-outline"
class="ms-1"
/>
</RouterLink>
</div>
</template>
</MediaCardSlideView>
<MediaCardSlideView apipath="douban/movie_top250"> <MediaCardSlideView
<template #title="{ loaded }"> apipath="douban/movie_top250"
<div linkurl="/browse/douban/movie_top250?title=电影TOP250"
v-if="loaded" title="电影TOP250"
class="slider-header mt-3 ms-1" />
>
<RouterLink
to="/browse/douban/movie_top250?title=电影TOP250"
class="slider-title"
>
<span>电影TOP250</span>
<VIcon
icon="mdi-arrow-right-circle-outline"
class="ms-1"
/>
</RouterLink>
</div>
</template>
</MediaCardSlideView>
<MediaCardSlideView apipath="douban/tv_weekly_chinese"> <MediaCardSlideView
<template #title="{ loaded }"> apipath="douban/tv_weekly_chinese"
<div linkurl="/browse/douban/tv_weekly_chinese?title=国产剧集榜"
v-if="loaded" title="国产剧集榜"
class="slider-header mt-3 ms-1" />
>
<RouterLink
to="/browse/douban/tv_weekly_chinese?title=国产剧集榜"
class="slider-title"
>
<span>国产剧集榜</span>
<VIcon
icon="mdi-arrow-right-circle-outline"
class="ms-1"
/>
</RouterLink>
</div>
</template>
</MediaCardSlideView>
<MediaCardSlideView apipath="douban/tv_weekly_global"> <MediaCardSlideView
<template #title="{ loaded }"> apipath="douban/tv_weekly_global"
<div linkurl="/browse/douban/tv_weekly_global?title=全球剧集榜"
v-if="loaded" title="全球剧集榜"
class="slider-header mt-3 ms-1" />
>
<RouterLink
to="/browse/douban/tv_weekly_global?title=全球剧集榜"
class="slider-title"
>
<span>全球剧集榜</span>
<VIcon
icon="mdi-arrow-right-circle-outline"
class="ms-1"
/>
</RouterLink>
</div>
</template>
</MediaCardSlideView>
</div> </div>
</template> </template>
<style lang="css">
.slider-header {
position: relative;
display: flex;
@apply mb-1;
}
.slider-title {
--tw-text-opacity: 1;
display: inline-flex;
align-items: center;
color: inherit;
font-size: 1.25rem;
font-weight: 700;
line-height: 1.75rem;
transition-duration: 0.3s;
}
</style>
+18 -49
View File
@@ -2,10 +2,13 @@
import api from '@/api' import api from '@/api'
import type { MediaInfo } from '@/api/types' import type { MediaInfo } from '@/api/types'
import MediaCard from '@/components/cards/MediaCard.vue' import MediaCard from '@/components/cards/MediaCard.vue'
import SlideView from '@/components/slide/SlideView.vue'
// 输入参数 // 输入参数
const props = defineProps({ const props = defineProps({
apipath: String, apipath: String,
linkurl: String,
title: String,
}) })
// 组件加载完成 // 组件加载完成
@@ -34,55 +37,21 @@ onMounted(fetchData)
</script> </script>
<template> <template>
<slot <SlideView
name="title" v-if="componentLoaded"
:loaded="componentLoaded" v-bind="props"
/> >
<VSlideGroup show-arrows="false"> <template #content>
<template #prev> <template
<VBtn v-for="data in dataList"
class="rounded-circle shadow-none"
icon="mdi-chevron-left"
color="grey"
/>
</template>
<VSlideGroupItem
v-for="data in dataList"
:key="data.tmdb_id"
>
<MediaCard
:key="data.tmdb_id || data.douban_id" :key="data.tmdb_id || data.douban_id"
:media="data" >
height="15rem" <MediaCard
width="10rem" :media="data"
/> height="15rem"
</VSlideGroupItem> width="10rem"
<template #next> />
<VBtn </template>
class="rounded-circle shadow-none"
icon="mdi-chevron-right"
color="grey"
/>
</template> </template>
</VSlideGroup> </SlideView>
</template> </template>
<style lang="scss">
.v-slide-group .v-card {
@apply m-2;
}
.v-slide-group__prev {
@apply absolute right-11;
z-index: 3;
margin-block-start: -40px;
}
.v-slide-group__next {
@apply absolute right-1;
z-index: 3;
margin-block-start: -40px;
}
</style>
+15 -30
View File
@@ -573,40 +573,25 @@ onBeforeMount(() => {
</div> </div>
</div> </div>
<div v-if="mediaDetail.tmdb_id"> <div v-if="mediaDetail.tmdb_id">
<PersonCardSlideView :apipath="`tmdb/credits/${mediaDetail.tmdb_id}/${mediaProps.type}`"> <PersonCardSlideView
<template #title="{ loaded }"> :apipath="`tmdb/credits/${mediaDetail.tmdb_id}/${mediaProps.type}`"
<div v-if="loaded" class="slider-header"> :linkurl="`/credits/tmdb/credits/${mediaDetail.tmdb_id}/${mediaProps.type}?title=演员阵容`"
<RouterLink :to="`/credits/tmdb/credits/${mediaDetail.tmdb_id}/${mediaProps.type}?title=演员阵容`" class="slider-title"> title="演员阵容"
<span>演员阵容</span> />
<VIcon icon="mdi-arrow-right-circle-outline" class="ms-1" />
</RouterLink>
</div>
</template>
</PersonCardSlideView>
</div> </div>
<div v-if="mediaDetail.tmdb_id"> <div v-if="mediaDetail.tmdb_id">
<MediaCardSlideView :apipath="`tmdb/recommend/${mediaDetail.tmdb_id}/${mediaProps.type}`"> <MediaCardSlideView
<template #title="{ loaded }"> :apipath="`tmdb/recommend/${mediaDetail.tmdb_id}/${mediaProps.type}`"
<div v-if="loaded" class="slider-header"> :linkurl="`/browse/tmdb/recommend/${mediaDetail.tmdb_id}/${mediaProps.type}?title=推荐`"
<RouterLink :to="`/browse/tmdb/recommend/${mediaDetail.tmdb_id}/${mediaProps.type}?title=推荐`" class="slider-title"> title="推荐"
<span>推荐</span> />
<VIcon icon="mdi-arrow-right-circle-outline" class="ms-1" />
</RouterLink>
</div>
</template>
</MediaCardSlideView>
</div> </div>
<div v-if="mediaDetail.tmdb_id"> <div v-if="mediaDetail.tmdb_id">
<MediaCardSlideView :apipath="`tmdb/similar/${mediaDetail.tmdb_id}/${mediaProps.type}`"> <MediaCardSlideView
<template #title="{ loaded }"> :apipath="`tmdb/similar/${mediaDetail.tmdb_id}/${mediaProps.type}`"
<div v-if="loaded" class="slider-header"> :linkurl="`/browse/tmdb/similar/${mediaDetail.tmdb_id}/${mediaProps.type}?title=类似`"
<RouterLink :to="`/browse/tmdb/similar/${mediaDetail.tmdb_id}/${mediaProps.type}?title=类似`" class="slider-title"> title="类似"
<span>类似</span> />
<VIcon icon="mdi-arrow-right-circle-outline" class="ms-1" />
</RouterLink>
</div>
</template>
</MediaCardSlideView>
</div> </div>
</div> </div>
</div> </div>
+18 -49
View File
@@ -2,10 +2,13 @@
import PersionCard from '@/components/cards/PersonCard.vue' import PersionCard from '@/components/cards/PersonCard.vue'
import api from '@/api' import api from '@/api'
import type { TmdbPerson } from '@/api/types' import type { TmdbPerson } from '@/api/types'
import SlideView from '@/components/slide/SlideView.vue'
// 输入参数 // 输入参数
const props = defineProps({ const props = defineProps({
apipath: String, apipath: String,
linkurl: String,
title: String,
}) })
// 组件加载完成 // 组件加载完成
@@ -34,55 +37,21 @@ onMounted(fetchData)
</script> </script>
<template> <template>
<slot <SlideView
name="title" v-if="componentLoaded"
:loaded="componentLoaded" v-bind="props"
/> >
<VSlideGroup show-arrows="false"> <template #content>
<template #prev> <template
<VBtn v-for="data in dataList"
class="rounded-circle shadow-none"
icon="mdi-chevron-left"
color="grey"
/>
</template>
<VSlideGroupItem
v-for="data in dataList"
:key="data.id"
>
<PersionCard
:key="data.id" :key="data.id"
:person="data" >
height="15rem" <PersionCard
width="10rem" :person="data"
/> height="15rem"
</VSlideGroupItem> width="10rem"
<template #next> />
<VBtn </template>
class="rounded-circle shadow-none"
icon="mdi-chevron-right"
color="grey"
/>
</template> </template>
</VSlideGroup> </SlideView>
</template> </template>
<style lang="scss">
.v-slide-group .v-card {
@apply m-2;
}
.v-slide-group__prev {
@apply absolute right-11;
z-index: 3;
margin-block-start: -40px;
}
.v-slide-group__next {
@apply absolute right-1;
z-index: 3;
margin-block-start: -40px;
}
</style>