使用Vue.directive注册全局指令
Vue.directive('scroll', {})
绑定事件
Vue.directive('scroll', {
bind: (el, binding, vnode) => {
}
})
编写逻辑
Vue.directive('scroll', {
bind: (el, binding, vnode) => {
// 距离底部剩余距离开始触发回调
let distance = 100 // (unit: px)
// 监听滚动事件
el.onscroll = (e) => {
// 获取当前节点可滚动的距离 节点滚动条总高度 - 节点本身高度
let scrollHeight = e.target.scrollHeight - e.target.offsetHeight
// 获取节点剩余可滚动的高度 可滚动距离 - 已经滚动的距离
let residualHeight = scrollHeight - e.target.offsetTop
// 滚动到指定区域执行回调事件
if ((typeof binding.value === 'function') && residualHeight < distance) {
// 执行事件回调函数 如果不明白此处的binding.value的同学请点击上面的链接,自行去官方查看
binding.value()
}
}
}
})
阻止回调多次执行
以上代码以及完全实现了加载更多的功能, 但是有个小问题,就是当滚动到 distance
范围之内后就会不断的执行回调, 而有的时候我们只需要执行一次, 其实很简单,请看下面代码, 注意在代码中出行的 eventAction
变量
Vue.directive('scroll', {
bind: (el, binding, vnode) => {
// 是否执行回调事件
let eventAction = true
// 距离底部剩余距离开始触发回调
let distance = 100 // (unit: px)
// 监听滚动事件
el.onscroll = (e) => {
// 获取当前节点可滚动的距离 节点滚动条总高度 - 节点本身高度
let scrollHeight = e.target.scrollHeight - e.target.offsetHeight
// 获取节点剩余可滚动的高度 可滚动距离 - 已经滚动的距离
let residualHeight = scrollHeight - e.target.offsetTop
// 滚动到指定区域执行回调事件
if ((typeof binding.value === 'function') && residualHeight < distance && eventAction) {
// 执行事件回调函数 如果不明白此处的binding.value的同学请点击上面的链接,自行去官方查看
binding.value()
eventAction = false
} else if (residualHeight > distance) {
eventAction = true
}
}
}
})
以上代码就可完全正常的工作了, 只需要将其放入Vue脚手架的main.js里面就可以全局使用
使用方法:
<template>
<div class="result">
<div class="search" @click="jump({name: 'search', params: {kw: kw}}, false, true)">
<div class="search-box">
<div class="input">
<input type="text" :disabled="true" :value="kw">
</div>
<div class="icon font"></div>
</div>
</div>
<div class="goods" ref="goods" v-scroll="scroll">
<div
v-for="(item,idx) in items"
@click="jump({name: 'product.detail', params: {id: item.id}})"
:style="{width: itemWidth + 'px', height: (itemWidth + 60) + 'px'}"
:key="'goods-item-' + idx" class="item">
<img
v-lazy="imageUrl + item.thumb"
:onerror="'this.src=\'' + require('@assets/banner/1.jpg') + '\''"
:style="{width: itemWidth + 'px', height: itemWidth + 'px'}">
<div class="item-info">
<div class="item-title">{{item.title}}</div>
<div class="price-views">
<div class="price">¥{{(Number(item.price) / 100).toFixed(2)}}</div>
<div class="views">
{{item.views}}<div class="font"></div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
data () {
return {
imageUrl: this.config('imageUrl'),
itemWidth: 0,
items: [],
page: 1,
size: 20,
kw: ''
}
},
methods: {
scroll () {
this.getData()
},
/**
* @purpose 获取数据
*/
getData () {
this.showLoading()
this
.$api
.goods
.search(this.kw, this.page, this.size)
.then(r => {
if (!(r.list instanceof Array) || r.list.length < 0) {
return false
}
this.items = this.items.concat(r.list)
this.page++
})
}
},
mounted () {
this.itemWidth = (this.$refs.goods.clientWidth - 2) / 2
},
created () {
let kw = this.$route.params.kw
if (kw) {
this.kw = kw
this.getData()
} else {
this.toast('缺少搜索关键词!', 2, () => {
this.jump(-1)
})
}
}
}
</script>
<style scoped lang="sass">
.result
width: 100%
height: 100%
.search
width: 100%
height: 50px
background-color: #ffffff
display: flex
align-items: center
justify-content: center
z-index: 100
.search-box
width: calc(100% - 20px)
height: 30px
display: flex
align-items: center
justify-content: center
background-color: #ebebeb
border-radius: 20px
.input
flex: 1
height: 100%
background-color: transparent
input
width: calc(100% - 1rem)
height: 100%
font-size: 1rem
background-color: transparent
margin-left: 1rem
color: #393a3f
.icon
width: 40px
height: 40px
font-size: 1rem
font-weight: bold
color: #393a3f
display: flex
align-items: center
justify-content: center
margin-right: .2rem
.goods
width: 100%
height: calc(100% - 51px)
overflow-x: hidden
overflow-y: auto
overflow-scrolling: touch
.item
background-color: #ffffff
margin-bottom: 2px
float: left
&:nth-child(2n)
margin-left: 2px
.item-info
width: calc(100% - 10px)
height: 60px
padding: 0 5px
.item-title
height: 30px
font-size: .8rem
line-height: 15px
overflow: hidden
text-overflow: ellipsis
display: -webkit-box
-webkit-line-clamp: 2
-webkit-box-orient: vertical
word-break: break-all
.price-views
width: 100%
height: 30px
display: flex
align-items: center
.price
flex: 1
height: 100%
font-size: .8rem
color: #ff4700
display: flex
align-items: center
.views
flex: 1
height: 100%
font-size: .6rem
color: #666666
display: flex
align-items: center
flex-direction: row-reverse
</style>