| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151 |
- <template>
- <div class="history">
- <i class="history__close" @click="$router.back" />
- <div class="history-tabs">
- <div
- v-for="(tab, index) in TABS"
- :key="tab"
- :class="[tab, { active: tabIndex === index }]"
- @click="onTabClick(index)"
- />
- </div>
- <div class="history-main" ref="mainRef">
- <div class="audio-panel" ref="audioPanel">
- <AudioPanel />
- </div>
- <div class="history-panel" ref="videoPanel">
- <VideoPanel />
- </div>
- <div class="history-panel" ref="imagePanel">
- <ImagePanel />
- </div>
- <div class="history-panel" ref="docPanel">
- <DocPanel />
- </div>
- </div>
- </div>
- </template>
- <script setup>
- import { ref, onMounted, onUnmounted, nextTick } from "vue";
- import AudioPanel from "./components/Audio.vue";
- import VideoPanel from "./components/Video.vue";
- import ImagePanel from "./components/Image.vue";
- import DocPanel from "./components/Doc.vue";
- const TABS = ["audio", "video", "image", "doc"];
- const tabIndex = ref(0);
- const mainRef = ref(null);
- const audioPanel = ref(null);
- const videoPanel = ref(null);
- const imagePanel = ref(null);
- const docPanel = ref(null);
- const panelEls = () => [
- audioPanel.value,
- videoPanel.value,
- imagePanel.value,
- docPanel.value,
- ];
- function scrollToPanel(index) {
- const el = panelEls()[index];
- if (!el) return;
- // 对 document/viewport 场景,scrollIntoView 直接可用
- el.scrollIntoView({ behavior: "smooth", block: "start" });
- }
- function onTabClick(index) {
- tabIndex.value = index;
- scrollToPanel(index);
- }
- let observer = null;
- // 根据 viewport(document)滚动计算可见比例并设置高亮
- function updateActiveByScroll() {
- let bestIdx = -1;
- let bestRatio = 0;
- const vh = window.innerHeight || document.documentElement.clientHeight;
- panelEls().forEach((el, idx) => {
- if (!el) return;
- const rect = el.getBoundingClientRect();
- const elHeight = rect.height || 0;
- const visible = Math.max(
- 0,
- Math.min(rect.bottom, vh) - Math.max(rect.top, 0)
- );
- const ratio = elHeight > 0 ? visible / elHeight : 0;
- if (ratio > bestRatio) {
- bestRatio = ratio;
- bestIdx = idx;
- }
- });
- if (bestIdx >= 0) tabIndex.value = bestIdx;
- }
- let scrollHandler = null;
- let resizeHandler = null;
- onMounted(async () => {
- await nextTick();
- const roots = panelEls().filter(Boolean);
- if (!roots.length) return;
- // 使用 viewport 作为 root(document 的滚动)
- observer = new IntersectionObserver(
- (entries) => {
- let best = null;
- entries.forEach((e) => {
- if (e.isIntersecting) {
- if (!best || e.intersectionRatio > best.intersectionRatio) best = e;
- }
- });
- if (best) {
- const idx = panelEls().indexOf(best.target);
- if (idx >= 0) tabIndex.value = idx;
- }
- },
- {
- root: null, // viewport
- threshold: [0.25, 0.5, 0.75],
- }
- );
- panelEls().forEach((el) => {
- if (el) observer.observe(el);
- });
- // 绑定到 window 的滚动(document)和 resize
- scrollHandler = () => {
- updateActiveByScroll();
- };
- resizeHandler = () => {
- updateActiveByScroll();
- };
- window.addEventListener("scroll", scrollHandler, { passive: true });
- window.addEventListener("resize", resizeHandler, { passive: true });
- // 初始运行一次以设置正确状态
- updateActiveByScroll();
- });
- onUnmounted(() => {
- if (observer) observer.disconnect();
- if (scrollHandler) window.removeEventListener("scroll", scrollHandler);
- if (resizeHandler) window.removeEventListener("resize", resizeHandler);
- });
- </script>
- <style lang="scss" scoped>
- @use "./index.scss";
- </style>
|