提交 e8391332 编写于 作者: richard_1015's avatar richard_1015

Merge branch 'next' of https://github.com/jdf2e/nutui into next

...@@ -160,7 +160,7 @@ ...@@ -160,7 +160,7 @@
"version": "3.0.0", "version": "3.0.0",
"name": "Notify", "name": "Notify",
"type": "component", "type": "component",
"cName": "methods", "cName": "消息通知",
"desc": "在页面顶部展示消息提示,支持函数调用和组件调用两种方式", "desc": "在页面顶部展示消息提示,支持函数调用和组件调用两种方式",
"sort": 4, "sort": 4,
"show": true, "show": true,
......
...@@ -4,10 +4,10 @@ ...@@ -4,10 +4,10 @@
<nut-cell> <nut-cell>
<ul class="infiniteUl" id="scroll"> <ul class="infiniteUl" id="scroll">
<nut-infiniteloading <nut-infiniteloading
containerId="scroll" container-id="scroll"
:useWindow="false" :use-window="false"
:hasMore="hasMore" :has-more="hasMore"
@loadMore="loadMore" @load-more="loadMore"
> >
<li <li
class="infiniteLi" class="infiniteLi"
...@@ -23,11 +23,12 @@ ...@@ -23,11 +23,12 @@
<nut-cell> <nut-cell>
<ul class="infiniteUl" id="refreshScroll"> <ul class="infiniteUl" id="refreshScroll">
<nut-infiniteloading <nut-infiniteloading
containerId="refreshScroll" pull-icon="JD"
:useWindow="false" container-id="refreshScroll"
:isOpenRefresh="true" :use-window="false"
:hasMore="refreshHasMore" :is-open-refresh="true"
@loadMore="refreshLoadMore" :has-more="refreshHasMore"
@load-more="refreshLoadMore"
@refresh="refresh" @refresh="refresh"
> >
<li <li
...@@ -44,10 +45,12 @@ ...@@ -44,10 +45,12 @@
<nut-cell> <nut-cell>
<ul class="infiniteUl" id="customScroll"> <ul class="infiniteUl" id="customScroll">
<nut-infiniteloading <nut-infiniteloading
containerId="customScroll" load-txt="loading"
:useWindow="false" load-more-txt="没有啦~"
:hasMore="customHasMore" container-id="customScroll"
@loadMore="customLoadMore" :use-window="false"
:has-more="customHasMore"
@load-more="customLoadMore"
> >
<li <li
class="infiniteLi" class="infiniteLi"
...@@ -55,15 +58,6 @@ ...@@ -55,15 +58,6 @@
:key="index" :key="index"
>{{ item }}</li >{{ 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> </nut-infiniteloading>
</ul> </ul>
</nut-cell> </nut-cell>
......
...@@ -22,9 +22,9 @@ ...@@ -22,9 +22,9 @@
<ul class="infiniteUl" id="scroll"> <ul class="infiniteUl" id="scroll">
<nut-infiniteloading <nut-infiniteloading
containerId = 'scroll' containerId = 'scroll'
:useWindow='false' :use-window='false'
:hasMore="hasMore" :has-more="hasMore"
@loadMore="loadMore" @load-more="loadMore"
> >
<li class="infiniteLi" v-for="(item, index) in defultList" :key="index">{{item}}</li> <li class="infiniteLi" v-for="(item, index) in defultList" :key="index">{{item}}</li>
</nut-infiniteloading> </nut-infiniteloading>
...@@ -62,11 +62,12 @@ setup() { ...@@ -62,11 +62,12 @@ setup() {
```html ```html
<ul class="infiniteUl" id="refreshScroll"> <ul class="infiniteUl" id="refreshScroll">
<nut-infiniteloading <nut-infiniteloading
containerId="refreshScroll" pull-icon="JD"
:useWindow="false" container-id="refreshScroll"
:isOpenRefresh="true" :use-window="false"
:hasMore="refreshHasMore" :is-open-refresh="true"
@loadMore="refreshLoadMore" :has-more="refreshHasMore"
@load-more="refreshLoadMore"
@refresh="refresh" @refresh="refresh"
> >
<li <li
...@@ -119,10 +120,10 @@ setup() { ...@@ -119,10 +120,10 @@ setup() {
```html ```html
<ul class="infiniteUl" id="customScroll"> <ul class="infiniteUl" id="customScroll">
<nut-infiniteloading <nut-infiniteloading
containerId = 'customScroll' container-id = 'customScroll'
:useWindow='false' :use-window='false'
:hasMore="customHasMore" :has-more="customHasMore"
@loadMore="customLoadMore" @load-more="customLoadMore"
> >
<li class="infiniteLi" v-for="(item, index) in customList" :key="index">{{item}}</li> <li class="infiniteLi" v-for="(item, index) in customList" :key="index">{{item}}</li>
<template v-slot:loading> <template v-slot:loading>
...@@ -170,27 +171,23 @@ setup() { ...@@ -170,27 +171,23 @@ setup() {
| 参数 | 说明 | 类型 | 默认值 | | 参数 | 说明 | 类型 | 默认值 |
|--------------|----------------------------------|--------|------------------| |--------------|----------------------------------|--------|------------------|
| hasMore | 是否还有更多数据 | Boolean | true | | has-more | 是否还有更多数据 | Boolean | `true` |
| threshold | 距离底部多远加载 | Number | 200 | | threshold | 距离底部多远加载 | Number | `200` |
| useWindow | 将滚动侦听器添加到 window 否则侦听组件的父节点 | Boolean | true | | use-window | 将滚动侦听器添加到 window 否则侦听组件的父节点 | Boolean | `true` |
| useCapture | 是否使用捕获模式 true 捕获 false 冒泡 | Boolean | false | | use-capture | 是否使用捕获模式 true 捕获 false 冒泡 | Boolean | `false` |
| containerId | 在 useWindow 属性为 false 的时候,自定义设置节点ID | String | '' | | container-id | 在 useWindow 属性为 false 的时候,自定义设置节点ID | String | `''` |
| unloadMoreTxt | “没有更多数”据展示文案 | String | '哎呀,这里是底部了啦' | | load-more-txt | “没有更多数”据展示文案 | String | `'哎呀,这里是底部了啦'` |
| isOpenRefresh | 是否开启下拉刷新 | Boolean | false | | is-open-refresh | 是否开启下拉刷新 | Boolean | `false` |
| pull-icon | 下拉刷新[图标名称](#/icon) | String | `https://img10.360buyimg.com/imagetools/jfs/t1/169863/6/4565/6306/60125948E7e92774e/40b3a0cf42852bcb.png` |
### Slot | pull-txt | 下拉刷新提示文案 | String | `松手刷新` |
| load-icon | 上拉加载[图标名称](#/icon) | Boolean | `https://img10.360buyimg.com/imagetools/jfs/t1/169863/6/4565/6306/60125948E7e92774e/40b3a0cf42852bcb.png` |
| name | 说明 | | load-txt | 上拉加载提示文案 | String | `加载中...` |
|--------|----------------|
| loading | 自定义“加载中”的展示形式 |
| unloadMore | 自定义“没有更多数据”的展示形式 |
| refreshLoading | 自定义下拉刷新中“加载中”的展示形式 |
### Events ### Events
| 事件名 | 说明 | 回调参数 | | 事件名 | 说明 | 回调参数 |
|--------|----------------|--------------| |--------|----------------|--------------|
| loadMore | 继续加载的回调函数 | done 函数,用于关闭加载中状态 | | load-more | 继续加载的回调函数 | done 函数,用于关闭加载中状态 |
| scrollChange | 实时监听滚动高度 | 滚动高度 | | scroll-change | 实时监听滚动高度 | 滚动高度 |
| refresh | 下拉刷新事件回调 | done 函数,用于关闭加载中状态 | | refresh | 下拉刷新事件回调 | done 函数,用于关闭加载中状态 |
\ No newline at end of file
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
} }
.top-text { .top-text {
font-size: 10px; font-size: 10px;
color: rgba(128, 128, 128, 1); color: $text-color;
} }
} }
} }
...@@ -28,8 +28,8 @@ ...@@ -28,8 +28,8 @@
display: block; display: block;
width: 100%; width: 100%;
padding-top: 16px; padding-top: 16px;
font-size: 12px; font-size: $font-size-small;
color: rgba(200, 200, 200, 1); color: $infinite-bottom-color;
text-align: center; text-align: center;
.bottom-box { .bottom-box {
...@@ -40,7 +40,7 @@ ...@@ -40,7 +40,7 @@
} }
.bottom-text { .bottom-text {
font-size: 10px; font-size: 10px;
color: rgba(128, 128, 128, 1); color: $text-color;
} }
} }
} }
......
<template> <template>
<view-block <view-block
class="nut-infiniteloading" :class="classes"
ref="scroller" ref="scroller"
@touchstart="touchStart" @touchstart="touchStart"
@touchmove="touchMove" @touchmove="touchMove"
@touchend="touchEnd" @touchend="touchEnd"
> >
<view-block class="nut-infinite-top" ref="refreshTop" :style="getStyle"> <view-block class="nut-infinite-top" ref="refreshTop" :style="getStyle">
<view-block class="top-box" v-if="!slotRefreshLoading"> <view-block class="top-box">
<nut-icon <nut-icon class="top-img" :name="pullIcon"></nut-icon>
class="top-img" <view-block class="top-text">{{ pullTxt }}</view-block>
name="https://img10.360buyimg.com/imagetools/jfs/t1/169863/6/4565/6306/60125948E7e92774e/40b3a0cf42852bcb.png"
></nut-icon>
<view-block class="top-text">松开刷新</view-block>
</view-block> </view-block>
<slot name="refreshLoading" v-else></slot>
</view-block> </view-block>
<view-block class="nut-infinite-container"><slot></slot></view-block> <view-block class="nut-infinite-container">
<slot></slot>
</view-block>
<view-block class="nut-infinite-bottom"> <view-block class="nut-infinite-bottom">
<template v-if="isInfiniting"> <template v-if="isInfiniting">
<view-block v-if="!slotLoading" class="bottom-box"> <view-block class="bottom-box">
<nut-icon <nut-icon class="bottom-img" :name="loadIcon"></nut-icon>
class="bottom-img" <view-block class="bottom-text">{{ loadTxt }}</view-block>
name="https://img10.360buyimg.com/imagetools/jfs/t1/169863/6/4565/6306/60125948E7e92774e/40b3a0cf42852bcb.png"
></nut-icon>
<view-block class="bottom-text">加载中···</view-block>
</view-block> </view-block>
<slot name="loading" v-else></slot>
</template> </template>
<template v-else-if="!hasMore"> <template v-else-if="!hasMore">
<view-block class="tips" v-if="!slotUnloadMore">{{ <view-block class="tips">{{ loadMoreTxt }}</view-block>
unloadMoreTxt
}}</view-block>
<slot name="unloadMore" v-else></slot>
</template> </template>
</view-block> </view-block>
</view-block> </view-block>
</template> </template>
<script lang="ts"> <script lang="ts">
import { import {
ref,
toRefs, toRefs,
onMounted, onMounted,
onUnmounted, onUnmounted,
...@@ -63,7 +51,25 @@ export default create({ ...@@ -63,7 +51,25 @@ export default create({
type: Number, type: Number,
default: 200 default: 200
}, },
unloadMoreTxt: { pullIcon: {
type: String,
default:
'https://img10.360buyimg.com/imagetools/jfs/t1/169863/6/4565/6306/60125948E7e92774e/40b3a0cf42852bcb.png'
},
pullTxt: {
type: String,
default: '松开刷新'
},
loadIcon: {
type: String,
default:
'https://img10.360buyimg.com/imagetools/jfs/t1/169863/6/4565/6306/60125948E7e92774e/40b3a0cf42852bcb.png'
},
loadTxt: {
type: String,
default: '加载中···'
},
loadMoreTxt: {
type: String, type: String,
default: '哎呀,这里是底部了啦' default: '哎呀,这里是底部了啦'
}, },
...@@ -84,63 +90,43 @@ export default create({ ...@@ -84,63 +90,43 @@ export default create({
default: false default: false
} }
}, },
emits: ['scrollChange', 'loadMore', 'refresh'], emits: ['scroll-change', 'load-more', 'refresh'],
setup(props, { emit, slots }) { setup(props, { emit, slots }) {
console.log('componentName', componentName); const state = reactive({
scrollEl: window as Window | HTMLElement | (Node & ParentNode),
const { scroller: null as null | HTMLElement,
hasMore, refreshTop: null as null | HTMLElement,
threshold, beforeScrollTop: 0,
containerId, isTouching: false,
useWindow, isInfiniting: false,
useCapture, refreshMaxH: 0,
isOpenRefresh
} = toRefs(props);
let scrollEl: Window | HTMLElement = window;
const scroller = ref<null | HTMLElement>(null);
const refreshTop = ref<null | HTMLElement>(null);
const beforeScrollTop = ref(0);
const isTouching = ref(false);
const isInfiniting = ref(false);
const refreshMaxH = ref(0);
const slot = reactive({
slotLoading: false,
slotUnloadMore: false,
slotRefreshLoading: false
});
const pageStart = reactive({
y: 0, y: 0,
x: 0, x: 0,
distance: 0 distance: 0
}); });
const classes = computed(() => {
const prefixCls = componentName;
return {
[prefixCls]: true
};
});
const getStyle = computed(() => { const getStyle = computed(() => {
const style: CSSProperties = {}; const style: CSSProperties = {};
return {
if (pageStart.distance < 0) { height: state.distance < 0 ? `0px` : `${state.distance}px`,
style.height = 0 + 'px'; transition: state.isTouching
} else { ? `height 0s cubic-bezier(0.25,0.1,0.25,1)`
style.height = pageStart.distance + 'px'; : `height 0.2s cubic-bezier(0.25,0.1,0.25,1)`
} };
if (isTouching.value) {
style.transition = `height 0s cubic-bezier(0.25,0.1,0.25,1)`;
} else {
style.transition = `height 0.2s cubic-bezier(0.25,0.1,0.25,1)`;
}
return style;
}); });
/** 获取监听自定义滚动节点 */ const getParentElement = (el: HTMLElement) => {
const getParentElement = el => { return !!props.containerId
if (containerId.value != '') { ? document.querySelector(`#${props.containerId}`)
return document.querySelector(`#${containerId.value}`); : el && el.parentNode;
}
return el && el.parentNode;
}; };
const requestAniFrame = () => { const requestAniFrame = () => {
...@@ -152,7 +138,7 @@ export default create({ ...@@ -152,7 +138,7 @@ export default create({
} }
); );
}; };
/** 获取滚动条高度 */
const getWindowScrollTop = () => { const getWindowScrollTop = () => {
return window.pageYOffset !== undefined return window.pageYOffset !== undefined
? window.pageYOffset ? window.pageYOffset
...@@ -163,24 +149,22 @@ export default create({ ...@@ -163,24 +149,22 @@ export default create({
).scrollTop; ).scrollTop;
}; };
const calculateTopPosition = el => { const calculateTopPosition = (el: HTMLElement): number => {
if (!el) { return !el
return 0; ? 0
} : el.offsetTop + calculateTopPosition(el.offsetParent as HTMLElement);
return el.offsetTop + calculateTopPosition(el.offsetParent);
}; };
/** 判断是否滚动到底部 */
const isScrollAtBottom = () => { const isScrollAtBottom = () => {
let offsetDistance = 0; let offsetDistance = 0;
let resScrollTop = 0; let resScrollTop = 0;
let direction = 'down'; // 滚动的方向 let direction = 'down';
const windowScrollTop = getWindowScrollTop(); const windowScrollTop = getWindowScrollTop();
if (useWindow.value) { if (props.useWindow) {
if (scroller.value) { if (state.scroller) {
offsetDistance = offsetDistance =
calculateTopPosition(scroller.value) + calculateTopPosition(state.scroller) +
scroller.value.offsetHeight - state.scroller.offsetHeight -
windowScrollTop - windowScrollTop -
window.innerHeight; window.innerHeight;
} }
...@@ -190,119 +174,109 @@ export default create({ ...@@ -190,119 +174,109 @@ export default create({
scrollHeight, scrollHeight,
clientHeight, clientHeight,
scrollTop scrollTop
} = scrollEl as HTMLElement; } = state.scrollEl as HTMLElement;
offsetDistance = scrollHeight - clientHeight - scrollTop; offsetDistance = scrollHeight - clientHeight - scrollTop;
resScrollTop = scrollTop; resScrollTop = scrollTop;
} }
if (beforeScrollTop.value > resScrollTop) { if (state.beforeScrollTop > resScrollTop) {
direction = 'up'; direction = 'up';
} else { } else {
direction = 'down'; direction = 'down';
} }
beforeScrollTop.value = resScrollTop; state.beforeScrollTop = resScrollTop;
emit('scrollChange', resScrollTop); emit('scroll-change', resScrollTop);
return offsetDistance <= threshold.value && direction == 'down'; return offsetDistance <= props.threshold && direction == 'down';
}; };
const infiniteDone = () => { const infiniteDone = () => {
isInfiniting.value = false; state.isInfiniting = false;
}; };
/** 滚动函数 */
const handleScroll = () => { const handleScroll = () => {
requestAniFrame()(() => { requestAniFrame()(() => {
if (!isScrollAtBottom() || !hasMore.value || isInfiniting.value) { if (!isScrollAtBottom() || !props.hasMore || state.isInfiniting) {
return false; return false;
} else { } else {
console.log('无限加载更多'); state.isInfiniting = true;
isInfiniting.value = true; emit('load-more', infiniteDone);
emit('loadMore', infiniteDone);
} }
}); });
}; };
/** 滚动监听 */
const scrollListener = () => { const scrollListener = () => {
scrollEl.addEventListener('scroll', handleScroll, useCapture.value); state.scrollEl.addEventListener('scroll', handleScroll, props.useCapture);
}; };
/** 下拉加载完成回到初始状态 */
const refreshDone = () => { const refreshDone = () => {
pageStart.distance = 0; state.distance = 0;
isTouching.value = false; state.isTouching = false;
}; };
const touchStart = event => { const touchStart = (event: TouchEvent) => {
if ( if (
beforeScrollTop.value == 0 && state.beforeScrollTop == 0 &&
!isTouching.value && !state.isTouching &&
isOpenRefresh.value props.isOpenRefresh
) { ) {
pageStart.y = event.touches[0].pageY; state.y = event.touches[0].pageY;
isTouching.value = true; state.isTouching = true;
const childHeight = ((refreshTop.value as HTMLElement) const childHeight = ((state.refreshTop as HTMLElement)
.firstElementChild as HTMLElement).offsetHeight; .firstElementChild as HTMLElement).offsetHeight;
refreshMaxH.value = Math.floor(childHeight * 1 + 10); state.refreshMaxH = Math.floor(childHeight * 1 + 10);
} }
}; };
const touchMove = event => { const touchMove = (event: TouchEvent) => {
pageStart.distance = event.touches[0].pageY - pageStart.y; state.distance = event.touches[0].pageY - state.y;
if (pageStart.distance > 0 && isTouching.value) { if (state.distance > 0 && state.isTouching) {
event.preventDefault(); event.preventDefault();
if (pageStart.distance >= refreshMaxH.value) if (state.distance >= state.refreshMaxH)
pageStart.distance = refreshMaxH.value; state.distance = state.refreshMaxH;
} else { } else {
pageStart.distance = 0; state.distance = 0;
isTouching.value = false; state.isTouching = false;
} }
}; };
const touchEnd = () => { const touchEnd = () => {
if (pageStart.distance < refreshMaxH.value) { if (state.distance < state.refreshMaxH) {
pageStart.distance = 0; state.distance = 0;
} else { } else {
emit('refresh', refreshDone); emit('refresh', refreshDone);
} }
}; };
/** 生命周期 首次加载 */
onMounted(() => { onMounted(() => {
const parentElement = getParentElement(scroller); const parentElement = getParentElement(
state.scroller as HTMLElement
let scrollElCopy = window; ) as Node & ParentNode;
if (useWindow.value === false) { state.scrollEl = props.useWindow ? window : parentElement;
scrollElCopy = parentElement;
}
scrollEl = scrollElCopy;
scrollListener(); scrollListener();
slot.slotUnloadMore = slots.unloadMore ? true : false;
slot.slotLoading = slots.loading ? true : false;
slot.slotRefreshLoading = slots.refreshLoading ? true : false;
}); });
/** 移除监听 */
onUnmounted(() => { onUnmounted(() => {
scrollEl.removeEventListener('scroll', handleScroll, useCapture.value); state.scrollEl.removeEventListener(
'scroll',
handleScroll,
props.useCapture
);
}); });
return { return {
scroller, classes,
refreshTop, ...toRefs(state),
touchStart, touchStart,
touchMove, touchMove,
touchEnd, touchEnd,
getStyle, getStyle
isInfiniting,
...toRefs(slot)
}; };
} }
}); });
......
...@@ -215,6 +215,10 @@ $notify-warning-background-color: linear-gradient( ...@@ -215,6 +215,10 @@ $notify-warning-background-color: linear-gradient(
$tabbar-active-color: $primary-color; $tabbar-active-color: $primary-color;
//infiniteloading
$infinite-bottom-color: #c8c8c8;
view-block { view-block {
display: block; display: block;
} }
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册