087b7e87创建于 2024年8月6日历史提交
<script lang="ts" setup>
import { onMounted, onUnmounted, ref, watch, computed, nextTick } from 'vue';
import { useRoute } from 'vue-router';

import _ from 'lodash-es';
import { useLangStore } from '@/stores';

const props = defineProps({
  className: {
    type: String,
    default: 'h2',
  },
});
const lang = computed(() => {
  return useLangStore().lang;
});
const route = useRoute();

const activeIndex = ref(0);
const anchorList = ref();

const debounceEvent = _.throttle(goAnchor, 300, {
  trailing: true,
});

function goAnchor() {
  const scrollTop =
    document.body.scrollTop || document.documentElement.scrollTop;
  anchorList.value = document.querySelectorAll(props.className);

  const topArr: number[] = [];
  anchorList.value = Array.from(anchorList.value).filter((item: any) => {
    return topArr.push(item.offsetTop + item.clientHeight);
  });
  for (let i = 0; i < topArr.length; i++) {
    if (scrollTop - 100 <= topArr[i]) {
      activeIndex.value = i;
      break;
    }
  }
}

onMounted(() => {
  goAnchor();
  window.addEventListener('scroll', debounceEvent);
});
onUnmounted(() => {
  window.removeEventListener('scroll', debounceEvent);
});
watch(
  () => {
    return route.path;
  },
  () => {
    nextTick(() => {
      goAnchor();
    });
  }
);
</script>
<template>
  <div class="md-anchor" :class="lang === 'en' ? 'en-anchor' : ''">
    <a
      v-for="(item, index) in anchorList"
      :key="item.id"
      :href="`#${item.id}`"
      :class="{ 'active-link': index === activeIndex }"
      class="anchor-link"
    >
      <div class="anchor-link-inner">
        {{
          item.id.split('-').splice(1, item.id.split('-').length).join('-') ||
          item.id
        }}
      </div>
    </a>
  </div>
</template>

<style lang="scss">
.md-anchor {
  position: fixed;
  top: 142px;
  left: calc((100vw - 1416px) / 2 - 110px);
  width: 100px;
  z-index: 999;
  @media screen and (max-width: 1620px) {
    left: 16px;
    width: 40px;
  }
  &::after {
    position: absolute;
    content: '';
    width: 1px;
    height: 100%;
    left: 0;
    bottom: -50%;
    background-color: #2e2e2e;
    z-index: 0;
  }
  @media screen and (max-width: 1100px) {
    display: none;
  }
  .anchor-link {
    position: relative;
    cursor: pointer;
    display: block !important;
    color: var(--o-color-white);
    line-height: var(--o-line-height-text);
    font-size: var(--o-font-size-text);
    border-left: 1px solid #2e2e2e;
    z-index: 1;
    &:hover {
      color: var(--o-color-brand2);
    }
    &-inner {
      padding: 8px var(--o-spacing-h6);
    }
  }
  .active-link {
    color: var(--o-color-brand2);
    border-left: 1px solid var(--o-color-brand2);
  }
}
.en-anchor {
  width: 130px;
  left: calc((100vw - 1416px) / 2 - 130px);
  @media screen and (max-width: 1620px) {
    left: 16px;
    width: 20px;
    .anchor-link {
      writing-mode: vertical-rl;
    }
  }
}
</style>