使用element-ul实现描点导航

左侧为导航栏,点击内容滚动到指定位置,监听滚动条,左侧锚点导航高亮

效果图如下:

 组件封装PointTags:使用el-tabs实现左侧导航

<template>
  <div class="point-wrap">
    <el-tabs
      tab-position="left"
      v-model="activeTag"
      size="middle"
      @tab-click="tabHandle">
      <el-tab-pane
        :key="item.id"
        v-for="item in tagsList"
        :label="item.label"
        :name="item.id"></el-tab-pane>
    </el-tabs>
  </div>
</template>

<script setup lang="ts">
import { ref, watch } from 'vue';
import type { TabsPaneContext } from 'element-plus';

let activeTag = ref();
const props = defineProps({
  tagsList: {
    type: Array<{ id: String; label: String }>,
    default: () => []
  }
});
defineExpose({
  setActiveTag: (id:string) => {
    activeTag.value = id;
  }
})
watch(
  () => props.tagsList,
  value => {
    if (value.length) { 
      activeTag.value = value[0].id;
    }
  },
  {
    immediate: true,
    deep: true
  }
);

const emit = defineEmits(['active-tag']);
const tabHandle = (tab: TabsPaneContext) => {
  console.log(tab);
  emit('active-tag', tab.paneName as string);
};
</script>

<style lang="less" scoped>
:deep(.el-tabs--left .el-tabs__header.is-left) {
  width: 100%;
}
:deep(.el-tabs--left .el-tabs__item.is-left) {
  justify-content: flex-start;
  text-align: left;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  display: block;
}
:deep(.el-tabs--left .el-tabs__active-bar.is-left) {
  left: 0;
}
:deep(.el-tabs--left .el-tabs__nav-wrap.is-left::after) {
  left: 0;
}
:deep(.el-tabs__nav) {
  width: 100%;
}
:deep(.el-tabs__item) {
  padding: 0 16px;
}
</style>

父级调用

<!-- 左侧导航 -->
<div class="left" v-show="!isCollapse">
  <PointTags
    :tagsList="tagsList"
    @active-tag="activeTagHandle"
    ref="pointTags" />
</div>
<!-- 右侧主体内容 -->
<div class="doc-list" ref="docListRef">
  <DocCard
    v-for="item in docList"
    :id="item.id"
    :key="item.id"
    :title="item.title"
    :content="item.content"
    :isActive="activeId === item.id" />
</div>
<script lang="ts">
import { onMounted, ref } from 'vue';
import PointTags from './components/PointTags.vue';
import DocCard from './components/DocCard.vue';

// 导航栏模块
type Tags = {
  id: string;
  label: string;
};
let tagsList = ref<Tags[]>([]);
const pointTags = ref();
const docListRef = ref();
let timeout: null | number = null;
const scrollHandle = (e: any) => {
  timeout && clearTimeout(timeout);
  window.setTimeout(() => {
    let scrollItems: NodeListOf<HTMLDivElement> =
      document.querySelectorAll('.doc-card');
    for (let i = scrollItems.length - 1; i >= 0; i--) {
      // 判断滚动条滚动距离是否大于当前滚动项可滚动距离
      if (e.target) {
        let judge =
          e.target.scrollTop >=
          scrollItems[i].offsetTop - scrollItems[0].offsetTop - 30;
        if (judge) {
          const id = docList.value[i].id;
          pointTags.value.setActiveTag(id);
          // console.log('id', id);
          break;
        }
      }
    }
  }, 200);
};
const activeTagHandle = (id: string) => {
  let height: number = docListRef.value.scrollTop;
  let dom = document.getElementById(id);
  let domHeight: number = dom!.offsetTop - 20;
  //滚动距离计算
  let H = Number(height) - Number(domHeight);
  console.log('滚动距离计算', H);
  if (H < 0) {
    //下滚
    H = Math.abs(H);
    docListRef.value.scrollBy({ top: H, behavior: 'smooth' });
  } else if (H == 0) {
    //不滚
    docListRef.value.scrollBy({ top: 0, behavior: 'smooth' });
  } else {
    //上滚
    H = -H;
    docListRef.value.scrollBy({ top: H, behavior: 'smooth' });
  }
};

onMounted(() => {
  docListRef.value.addEventListener('scroll', scrollHandle, true);
});
<script>

 

posted @ 2023-10-25 15:17  webHYT  阅读(455)  评论(0编辑  收藏  举报