hookehuyr

refactor(StudyCoursePage): 优化标签页固定和切换动画

重构了标签页的固定逻辑,使其在滚动时更流畅地固定到顶部。同时,改进了标签页切换时的动画效果,增加了过渡动画的平滑性。这些改动提升了用户体验和页面交互的流畅性。
......@@ -6,7 +6,7 @@
<div class="study-course-page bg-gradient-to-b from-green-50/70 to-white/90 min-h-screen">
<div v-if="course" class="flex flex-col h-screen">
<!-- 固定区域:课程封面和标签页 -->
<div class="fixed top-0 left-0 right-0 z-10 top-wrapper bg-white">
<div class=" bg-white">
<!-- 课程封面区域 -->
<van-image class="w-full aspect-video object-cover" :src="course?.cover || 'https://cdn.ipadbiz.cn/mlaj/images/default_block.png'" :alt="course?.title" />
<div class="p-4">
......@@ -21,15 +21,26 @@
<div class="h-2 bg-gray-100"></div>
<!-- 标签页区域 -->
<div class="py-3 bg-white">
<van-tabs v-model:active="activeTab" sticky animated swipeable shrink @change="handleTabChange">
<van-tab title="详情" name="detail">
</van-tab>
<van-tab title="目录" name="catalog">
</van-tab>
<van-tab title="课程互动" name="interaction">
</van-tab>
</van-tabs>
<div class="py-3 bg-white transition-all duration-300" :class="{'fixed top-0 left-0 right-0 z-10': isTabFixed}" :style="isTabFixed ? { transform: `translateY(0)` } : {}">
<div class="flex justify-around items-center relative">
<div
v-for="(tab, index) in [{title: '详情', name: 'detail'}, {title: '目录', name: 'catalog'}, {title: '课程互动', name: 'interaction'}]"
:key="tab.name"
:class="['px-4 py-2 cursor-pointer transition-colors relative', activeTab === tab.name ? 'text-green-600 font-medium' : 'text-gray-600']"
@click="handleTabChange(tab.name)"
ref="tabRefs"
>
{{ tab.title }}
</div>
<div
class="absolute bottom-0 left-0 bg-green-600 transition-all duration-300 z-20"
:style="{
width: tabRefs && tabRefs[activeTab === 'detail' ? 0 : activeTab === 'catalog' ? 1 : 2]?.offsetWidth + 'px',
transform: `translateX(${tabRefs && tabRefs[activeTab === 'detail' ? 0 : activeTab === 'catalog' ? 1 : 2]?.offsetLeft}px)`,
height: '2px',
}"
></div>
</div>
</div>
</div>
......@@ -123,12 +134,17 @@ useTitle('课程详情');
const activeTab = ref('detail');
const topWrapperHeight = ref(0);
const resizeObserver = ref(null);
const tabRefs = ref([]);
// 计算topWrapperHeight的函数
const updateTopWrapperHeight = () => {
nextTick(() => {
const topWrapper = document.querySelector('.top-wrapper');
if (topWrapper) {
// 断开之前的observer连接
if (resizeObserver.value) {
resizeObserver.value.disconnect();
}
// 使用 ResizeObserver 监听元素尺寸变化
resizeObserver.value = new ResizeObserver(() => {
topWrapperHeight.value = `${topWrapper.offsetHeight}px`;
......@@ -175,21 +191,44 @@ onUnmounted(() => {
window.removeEventListener('scroll', handleScroll);
window.removeEventListener('resize', updateTopWrapperHeight);
// 组件卸载时取消监听
resizeObserver.value.disconnect();
if (resizeObserver.value) {
resizeObserver.value.disconnect();
resizeObserver.value = null;
}
});
// 处理滚动事件
// 在script setup中添加
const isTabFixed = ref(false);
const tabOriginalTop = ref(0);
onMounted(async () => {
setTimeout(() => {
nextTick(() => {
// 记录标签页原始位置
const tabElement = document.querySelector('.py-3.bg-white');
if (tabElement) {
tabOriginalTop.value = tabElement.getBoundingClientRect().top + window.scrollY;
}
})
}, 500);
});
// 修改handleScroll函数
const handleScroll = () => {
const detailElement = document.getElementById('detail');
const catalogElement = document.getElementById('catalog');
const interactionElement = document.getElementById('interaction');
if (!detailElement || !catalogElement || !interactionElement) return;
const tabElement = document.querySelector('.py-3.bg-white');
if (!detailElement || !catalogElement || !interactionElement || !tabElement) return;
const currentScrollY = window.scrollY;
isTabFixed.value = currentScrollY >= tabOriginalTop.value;
const scrollTop = window.scrollY;
const catalogOffset = catalogElement.offsetTop - parseInt(topWrapperHeight.value);
const interactionOffset = interactionElement.offsetTop - parseInt(topWrapperHeight.value);
const catalogOffset = catalogElement.offsetTop - (isTabFixed.value ? tabElement.offsetHeight : 0);
const interactionOffset = interactionElement.offsetTop - (isTabFixed.value ? tabElement.offsetHeight : 0);
// 根据滚动位置更新activeTab
if (scrollTop >= interactionOffset) {
activeTab.value = 'interaction';
} else if (scrollTop >= catalogOffset) {
......@@ -200,7 +239,17 @@ const handleScroll = () => {
};
// 处理标签页切换
// 在script setup中添加
const previousTab = ref('detail');
const getTransitionTiming = (current, previous) => {
const tabOrder = { detail: 0, catalog: 1, interaction: 2 };
return tabOrder[current] > tabOrder[previous] ? 'cubic-bezier(0.4, 0, 0.2, 1)' : 'cubic-bezier(0.4, 0, 0.2, 1)';
};
// 修改handleTabChange函数
const handleTabChange = (name) => {
previousTab.value = activeTab.value;
nextTick(() => {
const element = document.getElementById(name);
if (element) {
......