fix(StudyCoursePage): 修复滚动容器引用和滚动事件处理问题
修复滚动容器引用未正确初始化的bug 将全局滚动事件改为容器内滚动事件 优化标签切换时的滚动行为
Showing
1 changed file
with
42 additions
and
35 deletions
| ... | @@ -48,7 +48,7 @@ | ... | @@ -48,7 +48,7 @@ |
| 48 | </div> | 48 | </div> |
| 49 | 49 | ||
| 50 | <!-- 滚动区域:详情、目录和互动内容 --> | 50 | <!-- 滚动区域:详情、目录和互动内容 --> |
| 51 | - <div class="overflow-y-auto flex-1" | 51 | + <div ref="scroll_container_ref" class="overflow-y-auto flex-1" |
| 52 | :style="{ paddingTop: topWrapperHeight, height: 'calc(100vh - ' + topWrapperHeight + ')' }"> | 52 | :style="{ paddingTop: topWrapperHeight, height: 'calc(100vh - ' + topWrapperHeight + ')' }"> |
| 53 | <!-- 详情区域 --> | 53 | <!-- 详情区域 --> |
| 54 | <div id="detail" class="py-4 px-4"> | 54 | <div id="detail" class="py-4 px-4"> |
| ... | @@ -195,6 +195,9 @@ const currentTabIndex = computed(() => { | ... | @@ -195,6 +195,9 @@ const currentTabIndex = computed(() => { |
| 195 | const tabs_container_ref = ref(null); | 195 | const tabs_container_ref = ref(null); |
| 196 | const tabs_container_width = ref(0); | 196 | const tabs_container_width = ref(0); |
| 197 | const tabs_resize_observer = ref(null); | 197 | const tabs_resize_observer = ref(null); |
| 198 | +const scroll_container_ref = ref(null); | ||
| 199 | +const scroll_container_el = ref(null); | ||
| 200 | +const is_tab_scrolling = ref(false); | ||
| 198 | 201 | ||
| 199 | /** | 202 | /** |
| 200 | * 初始化并监听标签容器宽度 | 203 | * 初始化并监听标签容器宽度 |
| ... | @@ -325,8 +328,12 @@ onMounted(async () => { | ... | @@ -325,8 +328,12 @@ onMounted(async () => { |
| 325 | /** | 328 | /** |
| 326 | * 初始化时计算topWrapperHeight | 329 | * 初始化时计算topWrapperHeight |
| 327 | */ | 330 | */ |
| 328 | - // 添加滚动监听 | 331 | + nextTick(() => { |
| 329 | - window.addEventListener('scroll', handleScroll); | 332 | + if (scroll_container_ref.value) { |
| 333 | + scroll_container_el.value = scroll_container_ref.value; | ||
| 334 | + scroll_container_el.value.addEventListener('scroll', handleScroll, { passive: true }); | ||
| 335 | + } | ||
| 336 | + }); | ||
| 330 | // 添加窗口大小变化监听 | 337 | // 添加窗口大小变化监听 |
| 331 | window.addEventListener('resize', updateTopWrapperHeight); | 338 | window.addEventListener('resize', updateTopWrapperHeight); |
| 332 | // 确保在组件挂载后计算高度 | 339 | // 确保在组件挂载后计算高度 |
| ... | @@ -337,7 +344,10 @@ onMounted(async () => { | ... | @@ -337,7 +344,10 @@ onMounted(async () => { |
| 337 | 344 | ||
| 338 | // 在组件卸载时移除监听器 | 345 | // 在组件卸载时移除监听器 |
| 339 | onUnmounted(() => { | 346 | onUnmounted(() => { |
| 340 | - window.removeEventListener('scroll', handleScroll); | 347 | + if (scroll_container_el.value) { |
| 348 | + scroll_container_el.value.removeEventListener('scroll', handleScroll); | ||
| 349 | + scroll_container_el.value = null; | ||
| 350 | + } | ||
| 341 | window.removeEventListener('resize', updateTopWrapperHeight); | 351 | window.removeEventListener('resize', updateTopWrapperHeight); |
| 342 | // 组件卸载时取消监听 | 352 | // 组件卸载时取消监听 |
| 343 | if (resizeObserver.value) { | 353 | if (resizeObserver.value) { |
| ... | @@ -354,19 +364,14 @@ onUnmounted(() => { | ... | @@ -354,19 +364,14 @@ onUnmounted(() => { |
| 354 | // 处理滚动事件 | 364 | // 处理滚动事件 |
| 355 | // 在script setup中添加 | 365 | // 在script setup中添加 |
| 356 | const isTabFixed = ref(false); | 366 | const isTabFixed = ref(false); |
| 357 | -const tabOriginalTop = ref(0); | ||
| 358 | 367 | ||
| 359 | -onMounted(async () => { | 368 | +const get_container_relative_top = (el) => { |
| 360 | - setTimeout(() => { | 369 | + const container = scroll_container_ref.value; |
| 361 | - nextTick(() => { | 370 | + if (!container || !el) return 0; |
| 362 | - // 记录标签页原始位置 | 371 | + const container_rect = container.getBoundingClientRect(); |
| 363 | - const tabElement = document.querySelector('.py-3.bg-white'); | 372 | + const el_rect = el.getBoundingClientRect(); |
| 364 | - if (tabElement) { | 373 | + return el_rect.top - container_rect.top + container.scrollTop; |
| 365 | - tabOriginalTop.value = tabElement.getBoundingClientRect().top + window.scrollY; | 374 | +}; |
| 366 | - } | ||
| 367 | - }) | ||
| 368 | - }, 500); | ||
| 369 | -}); | ||
| 370 | 375 | ||
| 371 | // 防抖函数 | 376 | // 防抖函数 |
| 372 | const debounce = (fn, delay) => { | 377 | const debounce = (fn, delay) => { |
| ... | @@ -385,27 +390,27 @@ const debounce = (fn, delay) => { | ... | @@ -385,27 +390,27 @@ const debounce = (fn, delay) => { |
| 385 | * 注释:当互动区域不存在时,仅在详情与目录之间联动 | 390 | * 注释:当互动区域不存在时,仅在详情与目录之间联动 |
| 386 | */ | 391 | */ |
| 387 | const handleScroll = debounce(() => { | 392 | const handleScroll = debounce(() => { |
| 393 | + if (is_tab_scrolling.value) return; | ||
| 394 | + | ||
| 388 | const detailElement = document.getElementById('detail'); | 395 | const detailElement = document.getElementById('detail'); |
| 389 | const catalogElement = document.getElementById('catalog'); | 396 | const catalogElement = document.getElementById('catalog'); |
| 390 | const interactionElement = document.getElementById('interaction'); | 397 | const interactionElement = document.getElementById('interaction'); |
| 391 | - const tabElement = document.querySelector('.py-3.bg-white'); | 398 | + const container = scroll_container_ref.value; |
| 392 | - if (!detailElement || !catalogElement || !tabElement) return; | 399 | + if (!detailElement || !catalogElement || !container) return; |
| 393 | 400 | ||
| 394 | - const currentScrollY = window.scrollY; | 401 | + isTabFixed.value = false; |
| 395 | - isTabFixed.value = currentScrollY >= tabOriginalTop.value; | ||
| 396 | 402 | ||
| 397 | - const scrollTop = window.scrollY; | 403 | + const scrollTop = container.scrollTop; |
| 398 | - const tabHeight = tabElement.offsetHeight; | 404 | + const buffer = 20; // 缓冲区域 |
| 399 | - const buffer = 50; // 缓冲区域 | ||
| 400 | 405 | ||
| 401 | // 计算各区域顶部位置(互动不存在则设为无穷大) | 406 | // 计算各区域顶部位置(互动不存在则设为无穷大) |
| 402 | - const detailTop = detailElement.offsetTop - tabHeight - buffer; | 407 | + const detailTop = get_container_relative_top(detailElement) - buffer; |
| 403 | - const catalogTop = catalogElement.offsetTop - tabHeight - buffer; | 408 | + const catalogTop = get_container_relative_top(catalogElement) - buffer; |
| 404 | - const interactionTop = interactionElement ? (interactionElement.offsetTop - tabHeight - buffer) : Infinity; | 409 | + const interactionTop = interactionElement ? (get_container_relative_top(interactionElement) - buffer) : Infinity; |
| 405 | 410 | ||
| 406 | // 页面高度与底部判断 | 411 | // 页面高度与底部判断 |
| 407 | - const scrollHeight = document.documentElement.scrollHeight; | 412 | + const scrollHeight = container.scrollHeight; |
| 408 | - const clientHeight = document.documentElement.clientHeight; | 413 | + const clientHeight = container.clientHeight; |
| 409 | const isAtBottom = scrollTop + clientHeight >= scrollHeight - buffer; | 414 | const isAtBottom = scrollTop + clientHeight >= scrollHeight - buffer; |
| 410 | 415 | ||
| 411 | // 联动判断 | 416 | // 联动判断 |
| ... | @@ -434,15 +439,17 @@ const getTransitionTiming = (current, previous) => { | ... | @@ -434,15 +439,17 @@ const getTransitionTiming = (current, previous) => { |
| 434 | const handleTabChange = (name) => { | 439 | const handleTabChange = (name) => { |
| 435 | previousTab.value = activeTab.value; | 440 | previousTab.value = activeTab.value; |
| 436 | activeTab.value = name; // 立即更新activeTab的值 | 441 | activeTab.value = name; // 立即更新activeTab的值 |
| 437 | - let offset = 100; // 调整偏移量, 配合目录的高度 | 442 | + is_tab_scrolling.value = true; |
| 438 | nextTick(() => { | 443 | nextTick(() => { |
| 439 | const element = document.getElementById(name); | 444 | const element = document.getElementById(name); |
| 440 | - if (element) { | 445 | + if (element && scroll_container_ref.value) { |
| 441 | - const topOffset = element.offsetTop - offset - parseInt(topWrapperHeight.value); | 446 | + const topOffset = Math.max(0, get_container_relative_top(element) - 10); |
| 442 | - window.scrollTo({ | 447 | + scroll_container_ref.value.scrollTo({ top: topOffset, behavior: 'smooth' }); |
| 443 | - top: topOffset, | 448 | + setTimeout(() => { |
| 444 | - behavior: 'smooth' | 449 | + is_tab_scrolling.value = false; |
| 445 | - }); | 450 | + }, 500); |
| 451 | + } else { | ||
| 452 | + is_tab_scrolling.value = false; | ||
| 446 | } | 453 | } |
| 447 | }); | 454 | }); |
| 448 | }; | 455 | }; | ... | ... |
-
Please register or login to post a comment