这是我参与8月更文挑战的第9天,活动详情查看:8月更文挑战
需求简介
楼层效果在购物网站比较常见。
典型的楼层效果,需要照顾两个到需求:
- 点击某个楼层,则页面滚动到相对应的位置
- 页面滚动到某个位置,对应的楼层要有高亮的提示效果
来张图示意一下:
当我点击右侧的楼层按钮: 为你推荐,则页面滚动到“为你推荐”这个位置。
而如果用户手动滚动到“为你推荐”这个位置,那右侧的楼层按钮也要显示为高亮的红色。
这个效果其实可以说是老生常谈了,在 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>
到这里就已经实现了点击楼层的按钮之后,页面能够自动滚动到对应的内容。
整个页面布局,参考下面这张图
还有一些其他参数,这里就不介绍了,大家可以去文档里看看。
这里画一条分割线,为啥呢?因为上面的代码,我已经实现了需求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') // 切换高亮的楼层按钮
}
})
})
},
这样,一个完整的楼层效果就实现了。如果小伙伴有更好的思路,欢迎在评论区里留言讨论。