npm init vue@latest
下载BetterScroll :
npm install @better-scroll/core --save
一.结构部分:
<template>
<div class="container"> //最大的盒子
<div class="left" ref="left_" @click="left_Click"> //左侧内容盒子
<div class="left_"> //存在于左侧内容盒子内的滚动容器
<p
v-for="(item, index) in leftList" //循环数据并渲染
:key="index"
:class="index == activeIndex ? 'active' : ''" //此处进行高亮判断
:data-index="index" //自定义属性因为是事件委托,拿不到点击的index所以自定义
>
{{ item }}
</p>
</div>
</div>
<div class="right" ref="right_"> //右侧内容盒子
<div class="right_"> //存在于右侧内容盒子下的滚动容器
<p v-for="(item, index) in leftList" :key="index">{{ item }}</p> //循环数据并渲染
</div>
</div>
</div>
</template>
二.CSS部分:
.container {
width: 100vw;
height: 100vh;
display: flex;
overflow: hidden;
}
.left {
width: 2.6667rem;
flex: none;
background-color: aqua;
}
.left p {
width: 2.6667rem;
height: 1.3333rem;
background-color: aquamarine;
text-align: center;
line-height: 1.3333rem;
}
.right {
flex: 1;
background-color: pink;
}
.right p {
height: 10.6667rem;
background-color: hotpink;
text-align: center;
line-height: 10.6667rem;
margin-bottom: 0.2667rem;
}
.left .active {
color: white;
background-color: red;
}
三.JS部分:
<script setup lang="ts">
import { onMounted,ref } from "vue"
import BScroll from "@better-scroll/core" //引入Betterscroll核心包(并不是完整包)
//此处进行JS原生的移动端适配(vite自带适配方案,而原生JS可以应对仍和环境勿忘)
(function (window) {
function setRem() {
let width = window.document.documentElement.clientWidth;
window.document.documentElement.style.fontSize = width / 10 + "px";
}
window.addEventListener("load", setRem);
window.addEventListener("resize", setRem);
})(window)
//ref 获取dom元素
const left_ = ref<any>(null) //获取左侧内容盒子
const right_ = ref<any>(null) //获取右侧内容盒子
//渲染数据,以下为模拟数据,由于左右数据目前一样,所以共用这一条数据
const leftList = ref([
"红米手机",
"手机配件",
"电脑平板",
"智能穿戴",
"电视",
"大家电",
"小家电",
"智能家居",
"出行运动",
"日用百货",
"儿童用品",
"有品精选",
"小米服务",
"飞机大炮",
"飙车一族",
"鬼火少年",
"鬼火少女",
]);
//存放高亮的变量,用于三元判断
const activeIndex = ref<string | number>(0);
//开关变量,用于控制左侧是否高亮
const flag = ref(false);
//声明全局变量,用于存储左右两个内容盒子的BScroll实例
let bs: any; //左实例
let bs1: any; //有实例
//bs实例
const init = ()=>{ //声明初始化函数
bs = new BScroll(left_.value, { //用于左侧滚动实例
probeType:3, //probeType:3是代表实时监听滚动条事件
click:true,
})
bs1 = new BScroll(right_.value, { //用于右侧滚动实例
probeType:3,
click:true,
})
//页面发生变化时,需要重新计算滚动区域的大小和位置
bs.refresh()
bs1.refresh()
//右侧滚动开始时将开关变量设置为ture,用来实现点击时直接高亮效果,如不设置会出现点击时高亮移动到点击位 置,而不是瞬间高亮与点击位置。 scrollStart是BScroll的滚动开始事件
bs1.on("scrollStart", ()=>{
flag.value = true
})
//右侧滚动时设置左侧高亮,y是盒子滚动的距离是负数
bs1.on("scroll", function ({y}:{y:number})){
if(flag.value){
//ref的children是子标签的集合,拿到所有的p标签
let RP = right_.value.children[0].children
//y滚动的距离,现将负值变为正值
let h = Math.abs(y)
//循环所有p标签,判断p标签距离顶部的距离
for(let i = 0;i < RP.length; i++){
//如果小于60,就让左侧对应的高亮
if(Math.abs(h - RP[i].offsetTop) < 60) {
//设置高亮,由于左右对应,所以i就是左侧数据的对应项
activeIndex.value = i
//使左侧滚动到相应元素,scrollToElement是BS方法,滚动到目标指定元素
//而已经指定为left_.value.children[0].children[activeIndex.value],
//所以会自动滚动到视口最上方
bs.scrollToElement(
left_.value.children[0].children[activeIndex.value],
1000,
false,
false
)
}
}
}
}
}
//左侧点击事件
const left_Click = (e:MouseEvent) => {
let dom = e.target as HTMLParagraphElement //用来存放点击的元素
const Lp = left_.value.children[0].children //获取左边所有的p标签
flag.value = false //修改开关变量
if(flag.value) {
for(let i = 0;i < Lp.length; i++){ //循环p标签,如果我点击的p标签
if(e.target == Lp[i]){
bs1.scrollToElement( //那么就让右边滚动到对应元素
right_.value.children[0].children[i],
1000,
false,
false
)
activeIndex.value = i //设置左侧高亮
}
}
} else {
//实现效果为,点击左侧,左侧高亮项会到视口最上方
activeIndex.value = dom.getAttribute("data-index")! //否则直接设置高亮为当前元素
bs1.scrollToElement( //让右侧滚动到对应元素
right_.value.children[0].children[activeIndex.value],
1000,
false,
false
)
bs.scrollToElement( //让左侧也滚动到对应元素
left_.value.children[0].children[activeIndex.value],
1000,
false,
false
)
}
}
//在组件挂载后获取dom元素
onMounted(()=>{
init() //调用bs实例,用于初始化BScroll
})
</script>