实现手机端左右联动better-scroll(vue3)
使用步棸
1.安装滚动插件
yarn add better-scroll@1.11.1 -D
2.引入插件实例化
import BScroll from "better-scroll"; //引入插件
3.定义左右两边实例
const bs1 = ref(); //左边商品分类滚动实例
const bs2 = ref(); //右边商品列表滚动实例
4.初始化better - scroll
onMounted(() => {
nextTick(() => {
//左边商品列表滚动实例
bs1.value = new BScroll(".aside", {
probeType: 3,
click: true, //让滚动的元素可以被点击
});
//右边商品列表滚动实例
bs2.value = new BScroll(".list", {
probeType: 3,
click: true, //让滚动的元素可以被点击
});
//计算所有商品标题的位置
const arr = computed(() => {
let data: number[] = [];
list.value.map((v: unknown, i: any) => {
const title = document.querySelector("#title" + i) as any;
const title0 = document.querySelector("#title" + 0) as any;
data.push(title.offsetTop - title0.offsetTop);
});
return data;
});
//滚动右边商品列表时
bs2.value.on("scroll", (pos: any) => {
let y = Math.abs(pos.y);
// console.log(y);
// 根据每个标题高度循环遍历来实现标题与左边的联动
for (let i = 0; i < arr.value.length - 1; i++) {
if (y >= arr.value[i] && y < arr.value[i + 1]) {
active.value = i;
// 5.实现跟左边标题的滑动效果
bs1.value.scrollToElement("#cate" + i, 400);
}
}
// console.log(document.querySelector("#title0"));
});
});
});
6.左联右实现
const show = (i: any) => {
bs2.value.scrollToElement("#title" + i, 400);
};
具体项目体现,可以根据具体需求来改编
<template>
<div class="goods">
<!-- 左边商品分类 -->
<aside class="aside">
<van-sidebar v-model="active">
<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.toFixed(2)"
:desc="v2.goodsDesc"
:title="v2.name"
:origin-price="v2.id"
:thumb="v2.imgUrl"
:tag="v2.category"
@click="detail(v2)"
>
<template #tags>
<van-tag round type="primary">特价</van-tag> 
<van-tag round type="danger">八折</van-tag>
</template>
<template #footer>
<van-stepper
v-model="v2.num"
theme="round"
button-size="22"
disable-input
:min="0"
:max="100"
v-on:click.native.stop
/>
</template>
</van-card>
</li>
<!-- 占位符 -->
<div class="place"></div>
</ul>
</div>
<!-- 购物车 -->
<shop-cart></shop-cart>
</div>
</template>
<script lang="ts" setup>
import ShopCart from "@/components/ShopCart.vue";
import { ref, onMounted, nextTick, computed } from "vue";
import { useRouter } from "vue-router";
import { $_goodslist } from "@/apis/goods";
import { Info } from "@/stores/counter";
const store = Info();
let goodsList = ref<Array<any>>([]);
const active = ref(0);
const bs1 = ref(); //左边商品分类滚动实例
const bs2 = ref(); //右边商品列表滚动实例
const cate = ref<Array<any>>([]); //左边分类名字
const list = ref<Array<any>>([]); //右边数据
let router = useRouter(); //获取全局最大的实例路由对象
//引入插件
import BScroll from "better-scroll"; //引入插件
$_goodslist().then((res) => {
if (store.goods.length > 0) return;
for (let obj of res.data.data)
for (let item of obj.foods) {
item.num = 0;
}
goodsList.value = res.data.data;
store.GoodsChange(goodsList.value);
// 商品分类(pinai数据)
cate.value = store.goods.map((v: any) => v.name);
list.value = res.data.data;
console.log(res);
});
// 初始化better - scroll;
onMounted(() => {
nextTick(() => {
//左边商品列表滚动实例
bs1.value = new BScroll(".aside", {
probeType: 3,
click: true, //让滚动的元素可以被点击
});
//右边商品列表滚动实例
bs2.value = new BScroll(".list", {
probeType: 3,
click: true, //让滚动的元素可以被点击
});
//计算所有商品标题的位置
const arr = computed(() => {
let data: number[] = [];
list.value.map((v: unknown, i: any) => {
const title = document.querySelector("#title" + i) as any;
const title0 = document.querySelector("#title" + 0) as any;
data.push(title.offsetTop - title0.offsetTop);
});
return data;
});
//滚动右边商品列表时
bs2.value.on("scroll", (pos: any) => {
let y = Math.abs(pos.y);
// console.log(y);
// 根据每个标题高度循环遍历来实现标题与左边的联动
for (let i = 0; i < arr.value.length - 1; i++) {
if (y >= arr.value[i] && y < arr.value[i + 1]) {
active.value = i;
// 实现跟左边标题的滑动效果
bs1.value.scrollToElement("#cate" + i, 400);
}
}
// console.log(document.querySelector("#title0"));
});
});
});
// 左联右
const show = (i: any) => {
bs2.value.scrollToElement("#title" + i, 400);
};
// 跳转到详情页面request
const detail = (v2: any) => {
router.push({ path: "/detail", query: { v2: JSON.stringify(v2) } }); //跳转路由
};
</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-y: scroll;
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;
background: #fff;
}
.van-card__price {
color: #f00;
}
// 占位符
.place {
height: 85px;
}
}
}
</style>
pinai数据
export const Info = defineStore("Info", () => {
// 商品
let goods = ref<any>([]);
let GoodsChange = (val: any) => {
goods.value = val;
};
// 商品数量增加减少(后台数据没有,循环给每个商品添加一个属性判断)
let addNum = (val: any, num: any) => {
for (let obj of goods.value) {
for (let arr of obj.foods) {
if (arr.id === val.id) {
arr.num += num;
}
}
}
};
return {
goods,
GoodsChange,
addNum,
};
});
推荐阅读:
扫描二维码,在手机上阅读