解决vue 中 使用better-scroll 时因为图片没有下载完导致滚动条高度不够,无法浏览全部内容的问题
better-scroll 是根据 scrollerHeight 属性决定有多少区域可以滚动的。
scrollerHeight 属性是根据放 better-scrol 的 content 中的子组件的高度来计算的。
better-scroll 初始化是在dom 加载后执行,此时图片没有下载完成,计算 scrollerHeight 属性时,是没有将图片高度计算在内的。所以,计算出来的结果是错误的。
后来图片加载进来之后content 有了新的高度,但是 scrollerHeight 属性并没有进行更新,所以滚动出现了问题
解决方法就是调用better-scroll 的refresh 函数去刷新高度。
假设组件树是这样的:
-
第一种方式
可以看到,需要滚动的组件都会包含在Scroll 组件中。可以在Scroll 组件中的updated 生命周期函数中检测滚动区域内的图片是否加载完成,当全部图片加载完成时,调用refresh 函数重新计算scrollerHeight
mounted() { this.bs = new BScroll(this.$refs.scroll, { click: true }); }, updated() { // 利用图片的complete 属性进行判断,当所有图片下载完成后再对scrollerHeight 重新计算。 let img = this.$refs.scroll.getElementsByTagName("img"); let count = 0; let length = img.length; if (length) { let timer = setInterval(() => { if (count == length) { this.bs.refresh(); clearInterval(timer); } else if (img[count].complete) { count++; } }, 100); } }
-
第二种方式,原生js 监听图片加载事件img.onload = function()(Vue 中提供了@load 事件,效果相同)
从组件树中可以看到,对GoodsListItem 中的img 元素添加load 时想要拿到Scroll 组件中的better-scroll 对象需要一层一层传递下来
而对load 事件的处理最好放在Home 组件的created 钩子函数中,想要把load 事件传递出去也比较麻烦
像这种非父子组件之间的通信,可以使用事件总线$bus 来传递事件// 1. 在mian.js 中添加事件总线 Vue.prototype.$bus = new Vue() // 2. 对GoodsListItem 组件中的img 元素监听load 事件 <img @load="imgLoaded"> // 3. 在GoodsListItem 组件中使用事件总线提交事件 methods: { imgLoaded() { this.$bus.$emit('imgLoaded') } } // 4. 在Home 组件中监听imgLoaded 事件(Home 组件中可以直接拿到Scroll 组件中的better-scroll 对象),并调用better-scroll 的refresh 函数去刷新高度 mounted() { this.$bus.$on('imgLoaded', () => { this.$refs.scroll.refresh() })
注意,第二种方式存在refresh 函数调用频繁的问题,有多少张图片触发load 事件就会调用多少次
解决方法就是添加防抖动函数debounce
methods: {
debounce(func, delay) {
/**
* 防抖动,防止函数频繁调用
*/
let timer = null
return function (...args) {
if (timer) clearTimeout(timer)
timer = setTimeout(() => {
func.apply(this, args)
}, delay)
}
}
mounted() {
const refresh = debounce(this.$refs.scroll.refresh, 50)
this.$bus.$on('imgLoaded', () => {
refresh()
})