better-scroll 地址: https://better-scroll.github.io/docs/zh-CN/guide/
效果展示
1.首先安装better-scroll
https://better-scroll.github.io/docs/zh-CN/guide/how-to-install.html#npm
2.左侧菜单代码,首先渲染左侧菜单
- 左侧菜单**
解释:
ref="menuScroll" 左侧菜单ref
currentIndex :计算属性,动态添加样式
注意:
<div class="menu-left" ref="menuScroll">
<ul>
<li :class="{'li-active':currentIndex === index}" v-for="(item,index) in shopInfo.menu" :key="index" @click="handleLi(index)">
<img style="width:.45rem;margin-right:.2rem" v-if="item.icon_url" :src="item.icon_url" alt="" srcset=""> {{item.name}}
</li>
</ul>
</div>
- 右侧菜单代码
解释:
ref="foodScroll" 右侧菜单ref
shopInfo 后台返回的所有数据
<!-- 右侧菜单 -->
<div class="menu-right" ref="foodScroll">
<ul>
<li v-for="(item,index) in shopInfo.menu" :key="index" class="food-list-hook">
<!-- 内容上 -->
<div class="category-title">
<strong>{{item.name}}</strong>
<span>{{item.description}}</span>
</div>
<div v-for="(food,idx) in item.foods" :key="idx" class="menu-right-content">
<div><img :src="food.image_path" alt="" srcset=""></div>
<div class="detail-good">
<div style="font-weight:bold;font-size:.35rem;">{{food.name}}</div>
<div class="description">{{food.description | fontNumber}}</div>
<div class="month"><span>月售{{food.month_sales}}</span><span>好评{{food.satisfy_rate}}</span></div>
<div class="price-count">
<div style="margin-right:auto;color:#ec6206;"> ¥{{food.activity.fixed_price}}</div>
<CartControll :food="food" @decreaseCount="decrease" />
</div>
</div>
</div>
</li>
</ul>
</div>
右侧数据结构
3.js代码
<script>
import BScroll from "better-scroll"; //导入better-scroll
export default {
name: 'goods',
data () {
return {
shopInfo: {}, //所有要用到的数据
menuScroll: {},//左侧商品滚动对象
foodScroll: {},//右侧商品滚动对象
scrollY: 0,//右侧当前滚动的值
listHeight: []//所有 ul>li 每个li的高度
}
},
created () {
this.getData()
},
computed: {
//根据右侧滚动的位置,确定左侧菜单的下标
currentIndex () {
for (let i = 0; i < this.listHeight.length; i++) {
let hight1 = this.listHeight[i]
let hight2 = this.listHeight[i + 1]
//判断是否在两个高度之间 如果是 返回 当前索引
if (this.scrollY >= hight1 && this.scrollY < hight2) {
return i
}
}
return 0
}
},
methods: {
//获取所有数据
getData () {
this.$axios('/api/profile/batch_shop').then((res) => {
res.data.recommend.forEach(recommend => {
recommend.items.forEach(item => {
item.count = 0; // 为每项添加数量
});
});
res.data.menu.forEach(menu => {
menu.foods.forEach(food => {
food.count = 0; // 为每项添加数量
});
});
this.shopInfo = res.data
//当所有DOM更新完在执行下面的方法
this.$nextTick(() => {
// DOM已经更新
this.initScroll();
// 计算12个区的高度
this.calculateHeight();
});
})
},
// 初始化 better-scroll
initScroll () {
//拿到左侧菜单的滚动对象
this.menuScroll = new BScroll(this.$refs.menuScroll, {
click: true
});
//拿到右侧菜单的滚动对象
this.foodScroll = new BScroll(this.$refs.foodScroll, {
probeType: 3, //实时滚动
click: true
});
//监听 scroll 事件
this.foodScroll.on("scroll", pos => {
this.scrollY = Math.abs(Math.round(pos.y)); //取绝对值
console.log(this.scrollY);
});
this.updated()
},
//解决better-scroll因为图片没有下载完导致的滚动条高度不够,无法浏览全部内容的问题。
//原因是better-scroll初始化是在dom加载后执行,此时图片没有下载完成,导致滚动条高度计算不准确。
//利用图片的complete属性进行判断,当所有图片下载完成后再对scroll重新计算。
updated () {
let img = document.getElementsByClassName('menu-right')[0].getElementsByTagName('img')
// console.log(img);
let count = 0
let length = img.length
if (length) {
let timer = setInterval(() => {
if (count == length) {
//如果图片都加载完了,重新刷新一下 scroll的滚动高度
this.foodScroll.refresh()
clearInterval(timer)
} else if (img[count].complete) {
count++
}
}, 0)
}
},
//计算每个li的高度
calculateHeight () {
let foodlist = this.$refs.foodScroll.getElementsByClassName(
"food-list-hook"
);
// 每个区的高度添加到数组中
let height = 0;
this.listHeight.push(height); //从0开始 先push 0到listHeight数组中
//遍历循环 foodlist 将每个 li 的高度添加到数组中
for (let i = 0; i < foodlist.length - 1; i++) {
let item = foodlist[i];
// 累加
height += item.clientHeight;
this.listHeight.push(height);
}
console.log(this.listHeight); //得到的所有每个li 高度的数组
},
//点击左侧列表跳转到右侧相应位置
handleLi (index) {
//获取右侧所有类为food-list-hook节点
let foodList = this.$refs.foodScroll.getElementsByClassName('food-list-hook')
//点击谁就拿到谁
let el = foodList[index]
// 跳转到点击的 food-list-hook 节点 花费时间为 250 毫秒
this.foodScroll.scrollToElement(el, 250)
}
},
components: {
BScroll
}
}
</script>