提交 b53e028f 编写于 作者: Y yangxiaolu3

feat: infiniteloading 开发完成

上级 a64d79e2
......@@ -2,24 +2,147 @@
<div class="demo">
<h2>基础用法</h2>
<nut-cell>
<nut-temp name="wifi"></nut-temp>
<nut-temp name="mail" txt="test txt"></nut-temp>
<ul class="infiniteUl" id="scroll">
<nut-infiniteloading
containerId="scroll"
:useWindow="false"
:isLoading="isLoading"
:hasMore="hasMore"
@scrollChange="scrollChange"
@loadMore="loadMore"
>
<li
class="infiniteLi"
v-for="(item, index) in defultList"
:key="index"
>{{ item }}</li
>
</nut-infiniteloading>
</ul>
</nut-cell>
<h2>自定义加载文案</h2>
<nut-cell>
<ul class="infiniteUl" id="customScroll">
<nut-infiniteloading
containerId="customScroll"
:useWindow="false"
:isLoading="customIsLoading"
:hasMore="customHasMore"
@loadMore="customLoadMore"
>
<li
class="infiniteLi"
v-for="(item, index) in customList"
:key="index"
>{{ item }}</li
>
<template v-slot:loading>
<div class="loading">
<span>加载中...</span>
</div>
</template>
<template v-slot:unloadMore>
<div class="unload-more">没有数据啦 ~~</div>
</template>
</nut-infiniteloading>
</ul>
</nut-cell>
</div>
</template>
<script lang="ts">
import { onMounted, ref, reactive, toRefs } from 'vue';
import { createComponent } from '@/utils/create';
const { createDemo } = createComponent('infiniteloading');
export default createDemo({
props: {},
setup() {
return {};
const isLoading = ref(false);
const hasMore = ref(true);
const customIsLoading = ref(false);
const customHasMore = ref(true);
const data = reactive({
defultList: [''],
customList: ['']
});
const scrollChange = dis => {
console.log('滚动的距离', dis);
};
const loadMore = () => {
isLoading.value = true;
setTimeout(() => {
const curLen = data.defultList.length;
for (let i = curLen; i < curLen + 10; i++) {
data.defultList.push(
`${i} -- 塑像本来就在石头里,我只是把不要的部分去掉`
);
}
isLoading.value = false;
if (data.defultList.length > 30) hasMore.value = false;
}, 500);
};
const customLoadMore = () => {
customIsLoading.value = true;
setTimeout(() => {
const curLen = data.customList.length;
for (let i = curLen; i < curLen + 10; i++) {
data.customList.push(
`${i} -- 塑像本来就在石头里,我只是把不要的部分去掉`
);
}
customIsLoading.value = false;
if (data.customList.length > 30) customHasMore.value = false;
}, 500);
};
const init = () => {
for (let i = 0; i < 10; i++) {
data.defultList.push(
`${i} -- 塑像本来就在石头里,我只是把不要的部分去掉`
);
data.customList.push(
`${i} -- 塑像本来就在石头里,我只是把不要的部分去掉`
);
}
};
onMounted(() => {
init();
});
return {
scrollChange,
loadMore,
isLoading,
hasMore,
customIsLoading,
customHasMore,
customLoadMore,
...toRefs(data)
};
}
});
</script>
<style lang="scss" scoped>
.nut-temp {
.infiniteUl {
height: 300px;
width: 100%;
overflow-y: auto;
overflow-x: hidden;
}
.infiniteLi {
margin-top: 10px;
font-size: 14px;
color: rgba(100, 100, 100, 1);
}
</style>
# infiniteloading组件
### 介绍
基于 xxxxxxx
### 安装
## 代码演示
### 基础用法1
### 介绍
列表滚动到底部自动加载更多数据。
### 安装
```javascript
import { createApp } from 'vue';
import { InfiniteLoading } from '@nutui/nutui';
const app = createApp();
app.use(InfiniteLoading);
```
## 代码演示
## API
### Props
| 参数 | 说明 | 类型 | 默认值 |
|--------------|----------------------------------|--------|------------------|
| name | 图标名称或图片链接 | String | - |
| color | 图标颜色 | String | - |
| size | 图标大小,如 '20px' '2em' '2rem' | String | - |
| class-prefix | 类名前缀,用于使用自定义图标 | String | 'nutui-iconfont' |
| tag | HTML 标签 | String | 'i' |
### Events
| 事件名 | 说明 | 回调参数 |
|--------|----------------|--------------|
| click | 点击图标时触发 | event: Event |
### 基础用法
```html
<ul class="infiniteUl" id="scroll">
<nut-infiniteloading
containerId = 'scroll'
:useWindow='false'
:isLoading="isLoading"
:hasMore="hasMore"
@scrollChange="scrollChange"
@loadMore="loadMore"
>
<li class="infiniteLi" v-for="(item, index) in defultList" :key="index">{{item}}</li>
</nut-infiniteloading>
</ul>
```
```javascript
setup() {
const isLoading = ref(false);
const hasMore = ref(true);
const data = reactive({
defultList: []
});
const scrollChange = (dis) => {};
const loadMore = () => {
isLoading.value = true;
setTimeout(() => {
const curLen = data.defultList.length;
for (let i = curLen; i < curLen + 10; i++) {
data.defultList.push(`${i} -- 塑像本来就在石头里,我只是把不要的部分去掉`);
}
isLoading.value = false;
if (data.defultList.length > 30) hasMore.value = false;
}, 500);
};
const init = () => {
for (let i = 0; i < 10; i++) {
data.defultList.push(`${i} -- 塑像本来就在石头里,我只是把不要的部分去掉`);
}
}
onMounted(() => {
init()
});
return { scrollChange, loadMore, isLoading, hasMore, ...toRefs(data) };
}
```
### 自定义加载文案
```html
<ul class="infiniteUl" id="customScroll">
<nut-infiniteloading
containerId = 'customScroll'
:useWindow='false'
:isLoading="customIsLoading"
:hasMore="customHasMore"
@loadMore="customLoadMore"
>
<li class="infiniteLi" v-for="(item, index) in customList" :key="index">{{item}}</li>
<template v-slot:loading>
<div class="loading">
<span>加载中...</span>
</div>
</template>
<template v-slot:unloadMore>
<div class="unload-more">没有数据啦 ~~</div>
</template>
</nut-infiniteloading>
</ul>
```
```javascript
setup() {
const customIsLoading = ref(false);
const customHasMore = ref(true);
const data = reactive({
customList: ['']
});
const customLoadMore = () => {
customIsLoading.value = true;
setTimeout(() => {
const curLen = data.customList.length;
for (let i = curLen; i < curLen + 10; i++) {
data.customList.push(`${i} -- 塑像本来就在石头里,我只是把不要的部分去掉`);
}
customIsLoading.value = false;
if (data.customList.length > 30) customHasMore.value = false;
}, 500);
};
const init = () => {
for (let i = 0; i < 10; i++) {
data.customList.push(`${i} -- 塑像本来就在石头里,我只是把不要的部分去掉`);
}
}
onMounted(() => {
init()
});
return { customIsLoading, customHasMore, customLoadMore,...toRefs(data) };
}
```
## API
### Props
| 参数 | 说明 | 类型 | 默认值 |
|--------------|----------------------------------|--------|------------------|
| hasMore | 是否还有更多数据 | Boolean | true |
| isLoading | 是否加载中 | Boolean | false |
| threshold | 距离底部多远加载 | Number | 200 |
| useWindow | 将滚动侦听器添加到 window 否则侦听组件的父节点 | Boolean | true |
| useCapture | 是否使用捕获模式 true 捕获 false 冒泡 | Boolean | false |
| containerId | 在 useWindow 属性为 false 的时候,自定义设置节点ID | String | '' |
| unloadMoreTxt | “没有更多数”据展示文案 | String | '哎呀,这里是底部了啦' |
### Slot
| name | 说明 |
|--------|----------------|
| loading | 自定义“加载中”的展示形式 |
| unloadMore | 自定义“没有更多数据”的展示形式 |
### Events
| 事件名 | 说明 | 回调参数 |
|--------|----------------|--------------|
| loadMore | 继续加载的回调函数 | - |
| scrollChange | 实时监听滚动高度 | 滚动高度 |
\ No newline at end of file
.nut-infiniteloading {
display: block;
width: 100%;
.nut-infinite-bottom {
display: block;
width: 100%;
padding-top: 16px;
font-size: 12px;
color: rgba(200, 200, 200, 1);
text-align: center;
}
}
<template>
<view :class="classes" @click="handleClick">
<view>{{ name }}</view>
<view>{{ txt }}</view>
<view class="nut-infiniteloading" ref="scroller">
<view class="nut-infinite-top"></view>
<view class="nut-infinite-container"><slot></slot></view>
<view class="nut-infinite-bottom">
<template v-if="isLoading">
<nut-icon name="refresh" v-if="!slotLoading"></nut-icon>
<slot name="loading" v-else></slot>
</template>
<template v-else-if="!hasMore">
<view class="tips" v-if="!slotUnloadMore">{{ unloadMoreTxt }}</view>
<slot name="unloadMore" v-else></slot>
</template>
</view>
</view>
</template>
<script lang="ts">
import { toRefs } from 'vue';
import { ref, toRefs, onMounted, onUnmounted } from 'vue';
import { createComponent } from '@/utils/create';
const { componentName, create } = createComponent('infiniteloading');
export default create({
props: {
name: {
hasMore: {
type: Boolean,
default: true
},
isLoading: {
type: Boolean,
default: false
},
threshold: {
type: Number,
default: 200
},
unloadMoreTxt: {
type: String,
default: ''
default: '哎呀,这里是底部了啦'
},
txt: {
useWindow: {
type: Boolean,
default: true
},
containerId: {
type: String,
default: ''
},
useCapture: {
type: Boolean,
default: false
}
},
components: {},
emits: ['click'],
emits: ['scrollChange', 'loadMore'],
setup(props, { emit }) {
setup(props, { emit, slots }) {
console.log('componentName', componentName);
const { name, txt } = toRefs(props);
const {
hasMore,
isLoading,
threshold,
containerId,
useWindow,
useCapture
} = toRefs(props);
const handleClick = (event: Event) => {
emit('click', event);
let scrollEl: Window | HTMLElement = window;
const scroller = ref<null | HTMLElement>(null);
const beforeScrollTop = ref(0);
const slotLoading = ref(false);
const slotUnloadMore = ref(false);
/** 获取监听自定义滚动节点 */
const getParentElement = el => {
if (containerId.value != '') {
return document.querySelector(`#${containerId.value}`);
}
return el && el.parentNode;
};
const requestAniFrame = () => {
return (
window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
function(callback) {
window.setTimeout(callback, 1000 / 60);
}
);
};
/** 获取滚动条高度 */
const getWindowScrollTop = () => {
return window.pageYOffset !== undefined
? window.pageYOffset
: (
document.documentElement ||
document.body.parentNode ||
document.body
).scrollTop;
};
return { name, txt, handleClick };
const calculateTopPosition = el => {
if (!el) {
return 0;
}
return el.offsetTop + calculateTopPosition(el.offsetParent);
};
/** 判断是否滚动到底部 */
const isScrollAtBottom = () => {
let offsetDistance = 0;
let resScrollTop = 0;
let direction = 'down'; // 滚动的方向
const windowScrollTop = getWindowScrollTop();
if (useWindow.value) {
if (scroller.value) {
offsetDistance =
calculateTopPosition(scroller.value) +
scroller.value.offsetHeight -
windowScrollTop -
window.innerHeight;
}
resScrollTop = windowScrollTop;
} else {
const {
scrollHeight,
clientHeight,
scrollTop
} = scrollEl as HTMLElement;
offsetDistance = scrollHeight - clientHeight - scrollTop;
resScrollTop = scrollTop;
}
if (beforeScrollTop.value > resScrollTop) {
direction = 'up';
} else {
direction = 'down';
}
beforeScrollTop.value = resScrollTop;
emit('scrollChange', resScrollTop);
return offsetDistance <= threshold.value && direction == 'down';
};
/** 滚动函数 */
const handleScroll = () => {
requestAniFrame()(() => {
if (!isScrollAtBottom() || !hasMore.value || isLoading.value) {
return false;
} else {
emit('loadMore');
}
});
};
/** 滚动监听 */
const scrollListener = () => {
scrollEl.addEventListener('scroll', handleScroll, useCapture.value);
};
/** 生命周期 首次加载 */
onMounted(() => {
const parentElement = getParentElement(scroller);
let scrollElCopy = window;
if (useWindow.value === false) {
scrollElCopy = parentElement;
}
scrollEl = scrollElCopy;
scrollListener();
slotUnloadMore.value = slots.unloadMore ? true : false;
slotLoading.value = slots.loading ? true : false;
});
/** 移除监听 */
onUnmounted(() => {
scrollEl.removeEventListener('scroll', handleScroll, useCapture.value);
});
return { scroller, slotLoading, slotUnloadMore };
}
});
</script>
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册