vue导航吸顶且锚点跳转

vue2写法

<template>
  <div style="height: 20000px">
    <div style="height: 300px; width: 100%; background-color: lightpink"></div>
    <div class="nav" :class="{ 'fix-nav': navBarFixed }">
      <div class="outside">
        <div
          class="inside"
          :class="{ active: inside1 }"
          @click="clickPan('content1')"
        >
          <span>内容1导航</span>
        </div>
        <div
          class="inside"
          :class="{ active: inside2 }"
          @click="clickPan('content2')"
        >
          <span>内容2导航</span>
        </div>
      </div>
    </div>
    <div class="content1" id="content1">
      <div
        style="
          height: 300px;
          width: 100%;
          background-color: lightyellow;
          padding-top: 30px;
          text-align: center;
        "
      >
        这是一块内容1
      </div>
    </div>
    <div class="content2" id="content2">
      <div
        style="
          height: 300px;
          width: 100%;
          background-color: lightblue;
          padding-top: 30px;
          text-align: center;
        "
      >
        这是一块内容2
      </div>
    </div>
  </div>
</template>
<script>
export default {
  data () {
    return {
      inside1: true,
      inside2: false,
      navBarFixed: false
    }
  },
  mounted () {
    window.addEventListener('scroll', this.watchScroll)
  },
  beforeDestroy () {
    window.removeEventListener('scroll', this.watchScroll)
  },
  methods: {
    clickPan (position) {
      // document.getElementById(position).scrollIntoView({
      //   behavior: "smooth",
      //   block: "start"
      // });
      const targetElem = document.getElementById(position)
      let targetElemPosition = targetElem.offsetTop - 72
      if (this.navBarFixed === false && position === 'content1') {
        targetElemPosition = targetElemPosition - 1
      }
      if (this.navBarFixed === false && position === 'content2') {
        targetElemPosition = targetElemPosition - 72
      }
      let scrollTop =
        window.pageYOffset ||
        document.documentElement.scrollTop ||
        document.body.scrollTop
      const step = function () {
        const distance = targetElemPosition - scrollTop
        scrollTop = scrollTop + distance / 4
        if (Math.abs(distance) < 1) {
          window.scrollTo(0, targetElemPosition)
        } else {
          window.scrollTo(0, scrollTop)
          setTimeout(step, 30)
        }
      }
      step()
    },
    watchScroll () {
      const scrollTop =
        window.pageYOffset ||
        document.documentElement.scrollTop ||
        document.body.scrollTop
      const offsetTop = document.querySelector('.content1').offsetTop - 72
      if (scrollTop >= offsetTop) {
        this.navBarFixed = true
      } else {
        this.navBarFixed = false
      }
      if (scrollTop < document.querySelector('.content2').offsetTop - 72) {
        this.inside1 = true
        this.inside2 = false
      } else {
        this.inside2 = true
        this.inside1 = false
      }
    }
  }
}
</script>
<style lang="scss" scoped>
.outside {
  margin: auto;
  width: 400px;
  height: 100%;
  span {
    position: relative;
  }
  .inside {
    line-height: 70px;
    text-align: center;
    margin: auto;
    width: 200px;
    height: 100%;
    float: left;
    cursor: pointer;
    span::before {
      content: "";
      position: absolute;
      height: 3px;
      top: 47.5px;
      background: #3054eb;
      width: 100%;
      opacity: 0;
      transition: all 0.8s ease;
    }
  }
  .active {
    color: #1890ff;
    span::before {
      opacity: 1;
    }
  }
  .inside:hover {
    color: #1890ff;
  }
}
.fix-nav {
  position: fixed;
  z-index: 1;
  top: 0;
  height: 70px;
  width: 100%;
  font-size: 1.3em;
  background-color: white;
  border-bottom: 2px solid #f2f2f2;
}
.nav {
  height: 70px;
  width: 100%;
  font-size: 1.3em;
  background-color: white;
  border-bottom: 2px solid #f2f2f2;
}
</style>

vue3写法

<template>
  <div style="height: 200px">
    <div style="height: 500px; width: 100%; background-color: lightpink"></div>
    <div class="nav" :class="{ 'fix-nav': navBarFixed }">
      <div class="outside" v-for="(item, index) in navdata" :key="index">
        <div class="inside" :class="{ active: inside }" @click="clickPan(item.content)">
          <span>{{ item.navname }}</span>
        </div>
        <!-- <div class="inside" :class="{ active: inside2 }" @click="clickPan('content2')">
          <span>内容2导航</span>
        </div> -->
      </div>
    </div>
    <div >
    <div class="content1" id="content1">
      <div style="
          height: 300px;
          width: 100%;
          background-color: lightyellow;
          padding-top: 30px;
          text-align: center;
        ">
        这是一块内容1
      </div>
    </div>
    <div class="content2" id="content2">
      <div style="
          height: 300px;
          width: 100%;
          background-color: lightblue;
          padding-top: 30px;
          text-align: center;
        ">
        这是一块内容2
      </div>
    </div>
    <div class="content3" id="content3">
      <div style="
          height: 300px;
          width: 100%;
          background-color: yellowgreen;
          padding-top: 30px;
          text-align: center;
        ">
        这是一块内容3
      </div>
    </div>
    <div id="content4">
      <div style="
          height: 300px;
          width: 100%;
          background-color: pink;
          padding-top: 30px;
          text-align: center;
        ">
        这是一块内容4
      </div>
    </div>
  </div>
  </div>
</template>
<script setup lang="ts">
import { ref, onMounted, onUnmounted, reactive } from 'vue';
// let inside = ref(true);
// let inside2 = ref(false);
let navBarFixed = ref(false);
// let scrollTop = ref(0);
const navdata = reactive([{
  navname: 'name1',
  navcontent: '11111',
  content:'content1',
}, {
  navname: 'name2',
  navcontent: '2',
  content:'content2',
},
{
  navname: 'name3',
  navcontent: '3',
  content:'content3',
},
{
  navname: 'name4',
  navcontent: '444',
  content:'content4',
},])

onMounted(() => {
  window.addEventListener("scroll", watchScroll);
});

onUnmounted(() => {
  window.removeEventListener("scroll", watchScroll);
});

const clickPan = (position: any) => {

  const targetElem = document.getElementById(position);
  let targetElemPosition = targetElem.offsetTop - 72;
  if (!navBarFixed.value && position === 'content1') {
    targetElemPosition = targetElemPosition - 1;
  }
  if (!navBarFixed.value && position === 'content2') {
    targetElemPosition = targetElemPosition - 72;
  }
  console.log('targetElemPosition',targetElemPosition)
  let scrollTop =
    window.pageYOffset ||
    document.documentElement.scrollTop ||
    document.body.scrollTop;
  const step = function () {
    let distance = targetElemPosition - scrollTop;
    scrollTop = scrollTop + distance / 4;
    console.log('scrollTop',scrollTop,'distance',distance)
    if (Math.abs(distance) < 1) {
      window.scrollTo(0, targetElemPosition);
    } else {
      window.scrollTo(0, scrollTop);
      setTimeout(step, 30);
    }
  };
  step();  
};
// 是否固定导航
const watchScroll = () => {
  var scrollTop =
    window.pageYOffset ||
    document.documentElement.scrollTop ||
    document.body.scrollTop;
  var offsetTop = document.querySelector(".content1").offsetTop - 72;
  if (scrollTop >= offsetTop) {
    navBarFixed.value = true;
  } else {
    navBarFixed.value = false;
  }
  // if (scrollTop < document.querySelector(".content2").offsetTop - 72) {
  //   inside.value = true;
  
  // } else {
 
  //   inside.value = false;
  // }

}
</script>
<style lang="scss" scoped>
.outside {
  display: flex;
  justify-content: space-evenly;
  margin: auto;
  // width: 400px;
  height: 100%;

  span {
    position: relative;
  }

  .inside {
    line-height: 70px;
    text-align: center;
    margin: auto;
    // width: 200px;
    height: 100%;
    float: left;
    cursor: pointer;

    span::before {
      content: '';
      position: absolute;
      height: 3px;
      top: 47.5px;
      background: #3054eb;
      width: 100%;
      opacity: 0;
      transition: all 0.8s ease;
    }
  }

  .active {
    color: #1890ff;

    span::before {
      opacity: 1;
    }
  }

  .inside:hover {
    color: #1890ff;
  }
}

.fix-nav {
  position: fixed;
  z-index: 1;
  top: 0;
  height: 70px;
  width: 100%;
  font-size: 1.3em;
  background-color: white;
  border-bottom: 2px solid #f2f2f2;
}

.nav {
  display: flex;
  justify-content: space-evenly;
  height: 70px;
  width: 100%;
  font-size: 1.3em;
  background-color: white;
  border-bottom: 2px solid #f2f2f2;
}
</style>

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值