vue 实现滚动类似anchor组件

本文介绍了如何在Vue中实现滚动效果,包括不监听全局滚动的点击滚动和全局监听滚动的跨页面滚动。主要利用IntersectionObserver方法,详细步骤可参考阮一峰的文章。

html+css

<template>
      <div class="wrap" :style="style">
        <div>
          <div
            v-for="(item,i) in list"
            :key="i"
            @click="autoScroll(item.top, item.id)"
            :class="{item: item,active: item.active}"
          >
            {{item.title}}
          </div>
        </div>
      </div>
    </template>
    <script>
      export default {
        data() {
          return {
            style: '',
            list: [
              {
                title: 'demo1',
                active: true,
                id: 'title-1',
              },
              {
                title: 'demo2',
                active: false,
                id: 'title-2',
              },
              {
                title: 'demo3',
                active: false,
                id: 'title-3',
              },
            ],
          }
        },
        // 将需要的跳转的组件引入
        computed: {
          ...mapState('merchant/register', ['demo1', 'demo2', 'demo3']),
        },
<style>
    .wrap {
      position: fixed;
      top: 0;
      bottom: 0;
      display: flex;
      justify-content: center;
      align-items: center;
    }
    .item {
      position: relative;
      padding: 14px 0px 14px 24px;
      min-width: 130px;
      font-size: 14px;
      line-height: 14px;
      color: #fff;
      cursor: pointer;
    }
    .active {
      color: rgb(50, 90, 180);
      &:after {
        content: ' ';
        position: absolute;
        left: 0px;
        top: 20px;
        height: 2px;
        width: 12px;
        background-color: rgb(50, 90, 180);
      }
    }
  </style>

没有全局监听滚动,单纯的点击滚动, 跨页面

        methods: {
          // 点击锚点进行滚动
          autoScroll(id) {
            // 点击的时候获取当前元素的offsetTop值
            const el = document.querySelector('#' + id)
            const top = el.offsetTop
            try {
              // 返回文档在垂直方向已滚动的像素值。
              let curY = window.scrollY
              let STEP = Math.abs(curY - top) / 40
              if (STEP < 10) STEP = 10
              const f = () => {
                if (top == null || curY == null) return
                let delt = Math.abs(curY - top)
                if (!delt) return
                if (delt <= STEP) curY = top
                else curY = curY + (curY > top ? -STEP : STEP)
                window.scrollTo(0, curY)
                setTimeout(f, 0)
              }
              f()
            } catch (e) {
              console.log(e)
            }
            // deep copy
            const list = [...this.list.map((v) => ({ ...v, active: false }))]
            list.forEach((v) => {
              if (v.id === id) {
                v.active = true
                return true
              }
              return false
            })
            this.list = list
          },
        },
        mounted() {
          const width = (document.body.clientWidth - 1200) / 2 - 120
          this.style = `left: ${width}px`
        },
      }

全局监听滚动, 跨页面

使用的IntersectionObserver 方法
具体使用方法参照阮大的文章, 点击即可进入

        methods: {
          // 初始化 快捷链接位置 和 锚点 坐标,当list发生改变时需要调用
          initPosition() {
            try {
              if (typeof window === 'undefined') return
              const width = (document.body.clientWidth - 1200) / 2 - 130
              this.style = `left: ${width}px`
              const anchorList = this.list.map((item) => {
                const el = document.querySelector('#' + item.id)
                return {
                  ...item,
                  top: el.offsetTop,
                }
              })
              this.list = anchorList
            } catch (e) {
              console.log(e)
            }
          },
          async observerHandle(entrys) {
            // deep copy
            const anchorList = [
              ...this.list.map((v) => ({ ...v, active: false })),
            ]
            entrys.forEach((v) => {
              let index = +v.target.id.split('title-')[1] - 1
              anchorList[index].show =
                v.intersectionRatio > 0 && v.intersectionRect.bottom >= 100
            })
            // 只活跃第一个显示在视口的title
            anchorList.some((v) => {
              if (v.show) {
                v.active = true
                return true
              }
              return false
            })

            this.list = anchorList
          },
          intersectionObserverInit() {
            // 停止监听工作
            this.io && this.io.disconnect()
            const thresholds = []
            for (let i = 0; i <= 1; i += 0.01) thresholds.push(i)
            this.io = new IntersectionObserver(this.observerHandle, {
              threshold: thresholds,
            })
            this.list.length &&
              this.list.forEach(({ id }) => {
                const el = document.querySelector('#' + id)
                // 监听
                el && this.io.observe(el)
              })
          },
          // 点击锚点进行滚动
          autoScroll(id) {
            try {
              // 返回文档在垂直方向已滚动的像素值。
              let curY = window.scrollY
              let STEP = Math.abs(curY - top) / 40
              if (STEP < 10) STEP = 10
              const f = () => {
                if (top == null || curY == null) return
                let delt = Math.abs(curY - top)
                if (!delt) return
                if (delt <= STEP) curY = top
                else curY = curY + (curY > top ? -STEP : STEP)
                window.scrollTo(0, curY)
                setTimeout(f, 0)
              }
              f()
            } catch (e) {
              console.log(e)
            }
          },
        },
        mounted() {
          this.$nextTick(() => {
            this.initPosition()
            this.intersectionObserverInit()
          })
        },
      }
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值