在外卖app中,经常能看到右侧商品列表滚动变化,左边的分类标题也跟着改变。实现的思路如下:
1、先安装better-scroll库:
npm install --save better-scroll
2、在组件中引入
import BScroll from "@better-scroll/core";
import MouseWheel from "@better-scroll/mouse-wheel";
BScroll.use(MouseWheel);
3、在data中定义scroll、scrollGoods分别代表左侧滚动和右侧滚动,并定义好scrollY(用来收集y的坐标)和tops(存放元素高度的数组)。
data() {
return {
scrollY: 0,
tops: [],
scroll: '',
scrollGoods: ''
};
},
4、先定义初始化滚动和高度的方法。
4.1、在初始化滚动的时候,需要配置click为true,因为better-scroll默认阻止了原生的点击事件。
4.2、在BScroll对象上通过on监听事件回调:scroll是滚动监听、scrollEnd是滚动结束的监听。
4.3、在初始化高度的时候,用querySelectorAll获取到的是一个伪数组,可以通过Array.prototype.slice.call(lis)或者Array.from(lis)转化为一个真数组。再对数组中的每一个元素进行遍历,push到tops中。
初始化后的tops为[0, 80, 160, 240, 320, 400, 480, 560, 640, 720, 800]
methods: {
// 初始化滚动
_initScroll() {
this.scroll = new BScroll(this.$refs.scroll, {
mouseWheel: true,
scrollY: true,
click: true
});
this.scrollGoods = new BScroll(this.$refs.scrollGoods, {
mouseWheel: true,
scrollY: true,
click: true,
probeType: 3, // 滚动的方式(0、1、2、3)3是受惯性滚动
});
// 滚动监听
this.scrollGoods.on("scroll", (position) => {
// position对象上有两个值:x、y
// console.log(position.x, position.y);
// 对监听到进行处理
this.scrollY = Math.abs(Math.round(position.y));
});
// 滚动结束的监听
this.scrollGoods.on("scrollEnd", (position) => {
this.scrollY = Math.abs(Math.round(position.y));
});
},
// 初始化高度
_initTops() {
let tops = [];
let top = 0; // 类似于求和累加
tops.push(top);
// 收集tops
let lis = document.querySelectorAll(".goods-content"); // 伪数组
Array.prototype.slice.call(lis).forEach((li) => {
top += li.clientHeight;
tops.push(top);
});
// 更新tops
this.tops = tops;
},
}
5、接下来就是要考虑什么时候初始化滚动和初始化高度。
(要初始化滚动和高度,初始化的时机很重要,必须要知道dom元素的高度,而dom元素的高度是通过请求来的数据渲染而成的,这时候则需要在watch搭配$nextTick进行初始化。)
这里的goodsList是请求来的列表数据
watch: {
goodsList(val) {
this.$nextTick(() => {
this._initScroll();
this._initTops();
});
}
}
6、计算得到当前分类的下标。
计算下标的时候需要注意的是:当前滚动距离scrollY是需要大于等于当前top值(top是tops数组中的每一项),还要小于下一个top,拿到在这个区间内的数的下标,返回index。
computed: {
// 计算当前分类的下标
currentIndex() {
// 得到条件数据
let { scrollY, tops } = this;
// 根据条件计算产生一个结果
// findIndex方法常用来查找数组中满足条件的第一项元素的下标
// 返回的是满足条件的第一项元素的下标,这要注意的是findIndex会给数组中的每一项执行一个函数来判断是否满足表达式,如果满足条件后,剩下的元素则不再执行
let index = tops.findIndex((top, index) => {
// scrollY要大于等于当前top值,还要小于下一个top
// tops数据:[0, 80, 160, 240, 320, 400, 480, 560, 640, 720, 800]
return scrollY >= top && scrollY < tops[index + 1];
});
// 返回结果
return index;
},
},
让当前v-for循环元素的index下标和currentIndex比较,如果相等,则动态添加类名active。
<li
v-for="(item, index) in sidebarList"
:key="index"
:class="index === currentIndex ? 'active' : ''"
>
</li>
7、实现点击左侧分类的li,右侧商品列表滚动定位到指定位置。
<li
v-for="(item, index) in sidebarList"
:key="index"
:class="index === currentIndex ? 'active' : ''"
@click="isActive(index)"
>
</li>
对li绑定点击事件,需要传入点击元素的下标。
通过js控制跳转(scrollTo方法)
isActive(index) {
// 获取到需要跳转的高度
const scrollY = this.tops[index];
this.scrollY = scrollY;
// scrollTo(x, y, 动画时间)
this.scrollGoods.scrollTo(0, -scrollY, 300);
},
效果展示
屏幕录制2023-02-01 01.26.35