导航栏吸顶效果 与内容联动组件

125 阅读1分钟

导航栏吸顶效果 与内容联动组件

<template>
  <div>
    <div class="nav-wrapper" :class="{ fixed: fixed }">
      <div class="nav">
        <div
          class="nav-item"
          v-for="(section, index) in sections"
          :key="index"
          @click="scrollToSection(section, index)"
          :class="{ active: activeIndex == index }"
        >
          {{ section.title }}
        </div>
      </div>
    </div>
    <div class="content">
      <div
        ref="content"
        class="section"
        v-for="(section, index) in sections"
        :key="index"
        :id="section.id"
      >
        <img :src="section.src" />
      </div>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      fixed: false,
      activeIndex: 0,
      contentTopList: []
    };
  },
  props: {
    sections: {
      type: Object,
      default: {},
    },
  },
  mounted() {
    // this.sections = [
    //   {
    //     id: "section1",
    //     src: "https://static-core.meditrusthealth.com/b4115419bdb64c2e835c241e726b799c.png",
    //     title: "套装详情",
    //   },
    //   {
    //     id: "section2",
    //     src: "https://static-core.meditrusthealth.com/8ffb2d0c4f094c3db0b006d9d6913998.png",
    //     title: "使用流程",
    //   },
    //   {
    //     id: "section3",
    //     src: "https://static-core.meditrusthealth.com/9d18ea0e43094ffe9f80790d8629c79c.png",
    //     title: "体检须知",
    //   },
    // ];

    window.addEventListener("scroll", this.handleScroll);
  },
  beforeDestroy() {
    window.removeEventListener("scroll", this.handleScroll);
  },
  methods: {
    getContentTopList() {
      this.contentTopList = this.$refs.content.map(item => item.offsetTop)
    },
    handleScroll() {
      this.fixed = window.pageYOffset > 480;
      this.contentTopList = this.$refs.content.map(item => item.offsetTop)

      const scrollTop = document.documentElement.scrollTop || document.body.scrollTop
      for (let i = this.contentTopList.length - 1; i >= 0; i--) {
        if (scrollTop >= this.contentTopList[i] - 190) {
          this.activeIndex = i
          break
        }
      }
    },
    scrollToSection(section, index) {
      this.activeIndex = index;
      const el = document.getElementById(section.id);
      const offsetTop = el.offsetTop - 70;
      window.scrollTo({
        top: offsetTop,
        behavior: "smooth",
      });
    },
  },
};
</script>

<style>
.nav-wrapper {
  position: relative;
  z-index: 1;
}

.nav-wrapper.fixed {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
}

.nav {
  display: flex;
  justify-content: space-between;
  padding: 5px 26px;
  background: #fff;
  height: 46px;
  line-height: 40px;
  font-size: 16px;
  box-sizing: border-box;
}

.nav-item {
  cursor: pointer;
  font-size: 16px;
}

.content {
  margin-top: 20px;
  /* padding: 16px 8px;
  background: #fff; */
  margin: 12px 16px;
}

.section {
  margin-bottom: 30px;
  /* margin: 0 10px; */
}
.section p {
  font-size: 16px;
}

.section img {
  width: 100%;
  height: auto;
}

h2 {
  margin-top: 0;
}

p {
  margin-bottom: 0;
}
.active {
  color: #2945ff;
  position: relative;
}
.active::after {
  content: "";
  display: block;
  width: 28px;
  height: 4px;
  background: #2945ff;
  position: absolute;
  bottom: -4px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 1000;
  border-radius: 2px;
}
</style>

window.addEventListener("scroll", this.handleScroll) 来监听页面滚动事件,当页面滚动时,会触发 handleScroll 方法,根据页面滚动的位置来判断是否需要将导航栏固定在页面顶部。 在导航栏中,我们使用了 v-for 循环来渲染每个导航项,同时绑定了 @click 事件来实现点击导航项后滚动到对应的内容区块 在内容区块中,我们使用了 v-for 循环来渲染每个内容区块,同时给每个内容区块设置了一个 id,用来与导航栏中的链接关联起来 在 scrollToSection 方法中,我们使用 document.getElementById(section.id) 来获取对应的内容区块元素,然后使用 window.scrollTo 方法来实现页面滚动到对应的位置。这里使用了 behavior: "smooth" 来实现平滑滚动的效果 注意注意:不要在 mounted 里去获取元素位置,可能有图片未加载完,图片加载完后高度和元素位置发生了改变