vue 中实现商城的楼层效果

1,793 阅读4分钟

这是我参与8月更文挑战的第9天,活动详情查看:8月更文挑战

需求简介

楼层效果在购物网站比较常见。

典型的楼层效果,需要照顾两个到需求:

  1. 点击某个楼层,则页面滚动到相对应的位置
  2. 页面滚动到某个位置,对应的楼层要有高亮的提示效果

来张图示意一下:

当我点击右侧的楼层按钮: 为你推荐,则页面滚动到“为你推荐”这个位置。

在这里插入图片描述

而如果用户手动滚动到“为你推荐”这个位置,那右侧的楼层按钮也要显示为高亮的红色。

这个效果其实可以说是老生常谈了,在 jqery 的插件里随便搜一下,都有大把的案例手把手教你如何来实现。

但如果把这个效果放在 vue 中来实现,那先前的一些思路就用不上了。

实现难点

这需求说实话并不复杂,直接锚点跳转<a href="#id" ></a>就可以实现页面的滚动,让路由中携带的 hash 所对应的 id 名称的元素滚动到页面的顶端。

但是在 vue 项目中,路由的实现占据了 hash,那么再使用锚点跳转,路由也会跟着改变。这显然不是我们想要实现的效果。

另外,页面滚动时,监听滚动事件来点亮对应的楼层按钮,也需要仔细思考下如何优雅的实现。

思路

闭门造车果然不值得提倡,忙碌了半个多小时,小菜鸟终于把目光转向了 npm.org,想看看有没有前辈们已经为后来人栽好了大树。

有道是“前人栽树,后人乘凉”。

果然,让我发现了这样一个插件:vue-scrollto,兼容 vue2 和 vue3,官方网站还提供一个简单易懂的 demo

// main.js
const Vue = require('vue')
const VueScrollTo = require('vue-scrollto')
Vue.use(VueScrollTo)

// .vue
<button v-scroll-to="'#element'">
    Scroll to #element
</button>

<h1 id="element">Hi. I'm element</h1>

看一看 API 和参数的介绍,感觉很OK,通俗易懂,对于我这样的小白菜而言,十分的友好,于是就开开心心的开始摸鱼 :)

眼看着距离下班的时间愈发接近,小菜鸟终于想起来自己的活还没干完

实现

首先安装一下这个包

npm install --save vue-scrollto

然后在main.js 中全局引入

const VueScrollTo = require('vue-scrollto')

Vue.use(VueScrollTo)

// 参数中有一些默认值
Vue.use(VueScrollTo, {
  container: 'body',   // 滚动元素
  duration: 500,  // 动画时长
  easing: 'ease',  // 动画曲线
  offset: 0,  // 滚动终点距离父元素顶部距离
  force: true,  // 强制立即执行,即便元素是可见的
  cancelable: true,
  onStart: false,
  onDone: false,
  onCancel: false,
  x: false, // x 轴禁止滚动
  y: true
})

然后在已经写好的页面中的楼层按钮上添加

<li
  v-scroll-to="{
    el: '#ID',
    container: '#wraper',
    duration: 50,
    easing: 'linear',
    offset: -10,
  }"
  :class="{'active': activeClass===ID}"
><a>为你推荐</a></li>

到这里就已经实现了点击楼层的按钮之后,页面能够自动滚动到对应的内容。

整个页面布局,参考下面这张图

未命名.png

还有一些其他参数,这里就不介绍了,大家可以去文档里看看。


这里画一条分割线,为啥呢?因为上面的代码,我已经实现了需求1,即:点击某个楼层,则页面滚动到相对应的位置。

下面要来实现需求2。

这个需求实现的逻辑是这样的:

  • 首先给父元素添加监听滚动的事件
  • 其次获取每个楼层的顶端到父元素顶端的距离,存成一个数组
  • 然后在滚动事件里,用每个楼层到父元素顶端的距离与父元素的滚动距离做判断
  • 如果两个距离很接近,那么就给这个楼层对应的按钮添加高亮样式

撸代码来说明一下

initEvent() {
  const el = this.$el.querySelector('#wraper') // 父元素,其内部元素滚动
  const h = el.querySelectorAll('h2') // 页面中所有的内容的标题,如上面的为你推荐
  const offettopList = [] // 存储标题距离父元素顶部的位置
  for (const k of h) { // 将所有内容的标题距离其父元素顶部的距离存成一个数组
    offettopList.push(k.offsetTop)
  }
  el.addEventListener('scroll', () => {
    offettopList.forEach((t, i) => {
      if (t - el.scrollTop - 10 < 5) { // 当某个元素距离父元素顶部距离小于 5 时
        this.activeClass = h[i].getAttributes('id') // 切换高亮的楼层按钮
      }
    })
  })
},

这样,一个完整的楼层效果就实现了。如果小伙伴有更好的思路,欢迎在评论区里留言讨论。