better-scroll(vue2)
1.1 什么是better-scroll?
BetterScroll 是一款重点解决移动端(已支持 PC)各种滚动场景需求的插件,使用纯 JavaScript 实现的,这意味着它是无依赖的。
中文文档:https://better-scroll.github.io/docs/zh-CN/
1.2 安装插件核心
yarn add @better-scroll/core
1.3 使用插件
//引入插件
import BetterScroll from '@better-scroll/core';
//实例化插件
let scroll = new BetterScroll('.wrapper');
注意:只能有一个子元素,才能进行滚动
1.4 滚动插件的坑
第一个:去掉垂直的滚动条,去掉overflow-y:scroll; ,更换成超出隐藏 overflow:hidden;
第二个:滚动插件初始化的位置,必须放在请求数据的后面,并且使用定时器或 $nextTick 回调函数进行初始化
第三个:better-scroll只能滚动条一个子元素,第一个子元素的高度必须超出父容器
1.5 左联右(点击左边分类,右边滚动到指定的商品模块)
实现原理:点击左边商品分类,获取下标,与右边的商品列表进行一一对应
第一步:点击商品分类,获取下标
//前提条件:让滚动元素可以点击
this.bs1 = new BetterScroll('.aside', {
probeType: 3,
click: true, //让滚动元素可以点击
});
//点击商品分类的函数
clickCate(i){
console.log(i);
}
第二步:给商品列表标题设置有规律的id
<h3 class="list-title" :id="'title'+i">{{v.name}}</h3>
第三步:滚动到指定的标签
this.bs2.scrollToElement('#title'+i, 500);
1.6 右联左(滚动右边的商品列表,左边的商品分类跟着变化)
核心思想:计算出每一个标题的位置,通过滚动事件获取当前位置,再遍历位置数组,判断当前位置在哪个区间,得到对应的下标,就可以设置左边激活下标,并跳过去。
//计算所有商品标题的位置
let arr = this.list.map((v,i)=>{
return document.querySelector('#title'+i).offsetTop - document.querySelector('#title0').offsetTop;
});
//滚动右边商品列表时
this.bs2.on('scroll', pos=>{
let y = Math.abs(pos.y);
for(let i=0; i<arr.length-1; i++){
if( y>=arr[i] && y<arr[i+1] ){ //判断当前滚动值在哪个位置区间,设置左边分类项
this.activeKey = i;
this.bs1.scrollToElement('#cate'+i, 300);
}
}
});
扩展阅读: 百度搜索 “js clientWidth offsetWidth scrollWidth”
具体代码体现如下:
<template>
<div class="goods">
<!-- 左边商品分类 -->
<aside class="aside">
<van-sidebar v-model="activeKey">
<van-sidebar-item
v-for="(v, i) in cate"
:title="v"
:key="i"
@click="show(i)"
:id="'cate' + i"
/>
<!-- 占位符 -->
<div class="aside_place"></div>
</van-sidebar>
</aside>
<!-- 右边商品列表 -->
<div class="list">
<ul>
<li v-for="(v, i) in list" :key="i" :id="v.name">
<h3 class="list-title" :id="'title' + i">{{ v.name }}</h3>
<van-card
v-for="(v2, i2) in v.foods"
:key="i2"
:price="v2.price | decimalFix"
:desc="v2.goodsDesc"
:title="v2.name"
:thumb="v2.imgUrl"
@click="detail(v2)"
>
<template #tags>
<van-tag plain round type="warning">特惠</van-tag>
</template>
<template #footer>
<van-stepper
v-model="v2.cartNum"
theme="round"
button-size="22"
disable-input
:min="0"
:max="100"
v-on:click.native.stop
@change="changeCart"
/>
</template>
</van-card>
</li>
<!-- 占位符 -->
<div class="place"></div>
</ul>
</div>
<!-- 购物车 -->
<shop-cart></shop-cart>
</div>
</template>
<script>
import { goodsList } from "@/api/goods.js";
//引入插件
import BetterScroll from "@better-scroll/core";
export default {
components: {
ShopCart: () => import("@/components/ShopCart.vue"),
},
data() {
return {
activeKey: 0,
bs1: null, //左边商品分类滚动实例
bs2: null, //右边商品列表滚动实例
};
},
async created() {
//判断当前vuex中有没有数据,如果没有就发请求,有就不发
if (this.list.length === 0) {
let res = await goodsList();
// console.log(res);
// 给每一个商品加入一个新的购物数量元素
res.data.data.forEach((v) => {
v.foods.forEach((v2) => {
v2.cartNum = 0;
// console.log(v2);
});
});
// 将读到的数据存到vuex中
this.$store.commit("addGoods", res.data.data);
// console.log(this.list);
}
// 初始化better - scroll;
this.$nextTick(() => {
//左边商品分类滚动实例
this.bs1 = new BetterScroll(".aside", {
probeType: 3,
click: true, //让滚动的元素可以被点击
});
//右边商品列表滚动实例
this.bs2 = new BetterScroll(".list", {
probeType: 3,
click: true,
});
//计算所有商品标题的位置
let arr = this.list.map((v, i) => {
return (
// 减去最上方到右边第一个标题的距离
document.querySelector("#title" + i).offsetTop -
document.querySelector("#title0").offsetTop
);
});
// console.log(arr);
//滚动右边商品列表时
this.bs2.on("scroll", (pos) => {
let y = Math.abs(pos.y);
// console.log(y);
// 根据每个标题高度循环遍历来实现标题与左边的联动
for (let i = 0; i < arr.length - 1; i++) {
if (y >= arr[i] && y < arr[i + 1]) {
this.activeKey = i;
// 实现跟左边标题的滑动效果
this.bs1.scrollToElement("#cate" + i, 300);
}
}
});
});
},
methods: {
//点击商品分类实现右边列表的联动 函数(scrollToElement)
show(i) {
// console.log(i);
this.bs2.scrollToElement("#title" + i, 500);
},
// 点击+ -时计算购物车商品实现联动
changeCart() {
this.$store.commit("addCart");
},
// 点击商品时跳转到详情页
detail(row) {
this.$router.push("/detail?id=" + row.id);
},
},
computed: {
//从vuex里面读取出来的商品列表
list() {
return this.$store.state.goodsList;
},
//商品分类数据
cate() {
return this.$store.state.goodsList.map((v) => v.name);
},
},
};
</script>
<style lang="scss" scoped>
// 隐藏所有的滚动条
::-webkit-scrollbar {
display: none;
// Chrome Safari
}
.goods {
display: flex;
height: 100%;
overflow-y: scroll;
// 左边商品分类
.aside {
width: 30%;
background: #f7f8fa;
overflow: hidden;
.van-sidebar {
width: 100%;
}
.van-sidebar-item--select::before {
background-color: #f60;
}
// 占位符
.aside_place {
height: 85px;
}
}
// 右边
.list {
width: 1px;
flex-grow: 1;
// overflow-y: auto;
overflow: hidden;
.list-title {
line-height: 32px;
background: #f4f4f4;
border-left: 2px solid #ccc;
font-size: 16px;
color: #666;
text-indent: 10px;
}
.van-card {
width: 100%;
padding: 6px;
}
.van-card__price {
color: #f00;
}
// 占位符
.place {
height: 85px;
}
}
}
</style>
推荐阅读:
扫描二维码,在手机上阅读