Vue加载更多节点滚动事件
使用Vue.directive注册全局指令
javascript
Vue.directive("scroll", {});绑定事件
javascript
Vue.directive("scroll", {
bind: (el, binding, vnode) => {},
});对于Vue自定义指令不明白的同学请移步: Vue自定义指令
编写逻辑
javascript
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 变量
javascript
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里面就可以全局使用 使用方法:
html
<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>