помещение
网上有很多针对vue封装的移动端UI组件库,但react的移动端UI组件库貌似只有Google的
material UI和阿里的 ant design mobile。阿里的下拉刷新又不符合项目的风格,只能
自己实现了。
采用better-scroll+react实现。
Эффект
Зачем использовать лучшую прокрутку
better-scroll 是一款重点解决移动端(已支持 PC)各种滚动场景需求的插件。它的核心
是借鉴的 iscroll 的实现,它的 API 设计基本兼容 iscroll,在 iscroll 的基础上又
扩展了一些 feature 以及做了一些性能优化。
另外 better-scroll 中已经提供了下拉刷新 上拉加载更多的方法,我要做的也是在其方法
内完善我要的效果
Потяните вниз, чтобы обновить
Параметр PULLDOWNREFRESH используется для настройки новых функций. Если установлено значение True или объект, включите вновь нарисованное, вы можете настроить расстояние верхней тяги вниз (порог), чтобы определить время обновления, и расстояние отскока пребывания (STOP)
options.pullDownRefresh = {
threshold: 50, // 当下拉到超过顶部 50px 时,触发 pullingDown 事件
stop: 20 // 刷新数据的过程中,回弹停留在距离顶部还有 20px 的位置
}
this.scroll = new BScroll(this.$refs.wrapper, options)
Монитор событий вытягивания, обновляйте данные. А затем обновить данные завершены, вызовите FINDPULLDOWN () методом, вернуться к верхней границе
this.scroll.on('pullingDown', () => {
// 刷新数据的过程中,回弹停留在距离顶部还有20px的位置
RefreshData()
.then((newData) => {
this.data = newData
// 在刷新数据完成之后,调用 finishPullDown 方法,回弹到顶部
this.scroll.finishPullDown()
})
})
Потяните вверх, чтобы загрузить больше
Параметр pullUpLoad используется для настройки функции загрузки с подтягиванием. Если установлено значение true или объект, можно включить загрузку с подтягиванием, а пороговое расстояние от дна можно настроить, чтобы определить время начала загрузки.
options.pullUpLoad = {
threshold: -20 // 在上拉到超过底部 20px 时,触发 pullingUp 事件
}
this.scroll = new BScroll(this.$refs.wrapper, options)
Прослушайте событие pullingUp, чтобы загрузить новые данные.
this.scroll.on('pullingUp', () => {
loadData()
.then((newData) => {
this.data.push(newData)
})
})
код напрямую
import React, { Component } from "react";
import PropTypes from "prop-types";
import BScroll from "better-scroll";
import icon_arrow from "@assets/images/other/arr.png";
//样式
import "./betterScroll.less";
let defaultPullDownRefresh = {
threshold: 100,
stop: 50,
stopTime: 600,
txt: {
success: "刷新成功"
}
};
let defaultPullUpLoad = {
threshold: 0,
txt: {
more: "加载更多",
nomore: "我是有底线的"
}
};
class Scroll extends Component {
static defaultProps = {
probeType: 3,
click: false, // https://ustbhuangyi.github.io/better-scroll/doc/options.html#tap
startY: 0,
scrollY: true,
scrollX: false,
freeScroll: true,
scrollbar: true,
pullDownRefresh: false,
pullUpLoad: false,
bounce: true,
preventDefaultException: {
className: /(^|\s)originEvent(\s|$)/,
tagName: /^(INPUT|TEXTAREA|BUTTON|SELECT|TABLE)$/
},
eventPassthrough: "",
isPullUpTipHide: true,
disabled: false,
stopPropagation: true
};
static propTypes = {
children: PropTypes.any,
probeType: PropTypes.number,
startY: PropTypes.number,
click: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]),
scrollY: PropTypes.bool,
scrollX: PropTypes.bool,
freeScroll: PropTypes.bool,
scrollbar: PropTypes.bool,
pullDownRefresh: PropTypes.oneOfType([PropTypes.object, PropTypes.bool]),
pullUpLoad: PropTypes.oneOfType([PropTypes.object, PropTypes.bool]),
pullUpLoadMoreData: PropTypes.func,
canRenderPullUpTip: PropTypes.bool,
doPullDownFresh: PropTypes.func,
doScroll: PropTypes.func,
doScrollStart: PropTypes.func,
doScrollEnd: PropTypes.func,
preventDefaultException: PropTypes.object,
eventPassthrough: PropTypes.string,
isPullUpTipHide: PropTypes.bool,
bounce: PropTypes.bool,
disabled: PropTypes.bool,
stopPropagation: PropTypes.bool
};
constructor(props, context) {
super(props, context);
this.scroll = null; // scroll 实例
this.isRebounding = false;
this.pulling = false;
this.pullDownInitTop = -50;
// this.pullDownInitTop = 0;
this.state = {
isPullUpLoad: false,
beforePullDown: true,
pulling: false,
pullDownStyle: {
top: `${this.pullDownInitTop}px`
},
bubbleY: 0
};
}
createScrollId() {
return Math.random()
.toString(36)
.substr(3, 10);
}
componentDidMount() {
this.initScroll();
}
componentDidUpdate(prevProps) {
if (this.props.children !== prevProps.children) {
if (!this.state.pulling) {
this.scroll.refresh();
}
if (prevProps.disabled !== this.props.disabled) {
this.props.disabled ? this.scroll.disable() : this.scroll.enable();
}
}
}
componentWillUnmount() {
this.scroll.stop();
this.scroll.destroy();
this.scroll = null;
clearTimeout(this.TimerA);
clearTimeout(this.TimerB);
}
initScroll() {
let {
probeType,
click,
startY,
scrollY,
scrollX,
freeScroll,
scrollbar,
pullDownRefresh,
pullUpLoad,
preventDefaultException,
eventPassthrough,
bounce,
stopPropagation
} = this.props;
let _pullDownRefresh =
typeof pullDownRefresh === "object"
? {
...defaultPullDownRefresh,
...pullDownRefresh
}
: pullDownRefresh
? defaultPullDownRefresh
: false;
let _pullUpLoad =
typeof pullUpLoad === "object"
? {
...defaultPullUpLoad,
...pullUpLoad
}
: pullUpLoad
? defaultPullUpLoad
: false;
this.options = {
probeType,
click,
startY,
scrollY,
freeScroll,
scrollX,
scrollbar,
pullDownRefresh: _pullDownRefresh,
pullUpLoad: _pullUpLoad,
preventDefaultException,
eventPassthrough,
bounce: bounce,
stopPropagation: stopPropagation
};
let wrapper = this.refs.$dom;
this.scroll = new BScroll(wrapper, this.options);
this.initEvents();
}
initEvents() {
if (this.options.pullUpLoad) {
this._initPullUpLoad();
}
if (this.options.pullDownRefresh) {
this._initPullDownRefresh();
}
if (this.props.doScrollStart) {
this.scroll.on("scrollStart", pos => {
this.props.doScrollStart(pos);
});
}
if (this.props.doScroll) {
this.scroll.on("scroll", pos => {
this.props.doScroll(pos);
});
}
if (this.props.doScrollEnd) {
this.scroll.on("scrollEnd", pos => {
this.props.doScrollEnd(pos);
});
}
if (this.props.disabled) {
this.scroll.disable();
}
}
getScrollObj = () => {
return this.scroll;
};
_initPullDownRefresh() {
this.scroll.on("pullingDown", () => {
//松开手时
this.setState({
beforePullDown: false,
pulling: true
});
this.props.doPullDownFresh().then(() => {
//刷新方法调用成功
if (!this.scroll) {
return;
}
this.setState({
pulling: false
});
this._reboundPullDown().then(() => {
this._afterPullDown();
});
});
});
this.scroll.on("scroll", pos => {
const { beforePullDown } = this.state;
if (pos.y < 0) {
return;
}
if (beforePullDown) {
this.setState({
bubbleY: Math.max(0, pos.y + this.pullDownInitTop),
pullDownStyle: {
top: `${Math.min(pos.y + this.pullDownInitTop, 10)}px`
}
});
} else {
this.setState({
bubbleY: 0
});
}
if (this.isRebounding) {
this.setState({
pullDownStyle: {
top: `${10 - (defaultPullDownRefresh.stop - pos.y)}px`
}
});
}
});
}
_reboundPullDown = () => {
let { stopTime = 4000 } = this.options.pullDownRefresh;
return new Promise(resolve => {
this.TimerA = setTimeout(() => {
this.isRebounding = true;
this.scroll.finishPullDown();
resolve();
}, stopTime);
});
};
_afterPullDown() {
this.TimerB = setTimeout(() => {
this.setState({
beforePullDown: true,
pullDownStyle: {
top: `${this.pullDownInitTop}px`
}
});
this.isRebounding = false;
this.scroll.refresh();
}, this.scroll.options.bounceTime);
}
_initPullUpLoad = () => {
this.scroll.on("pullingUp", () => {
this.setState({
isPullUpLoad: true
});
this.props.pullUpLoadMoreData().then(() => {
if (!this.scroll) {
return;
}
this.setState({
isPullUpLoad: false
});
this.scroll.finishPullUp();
this.scroll.refresh();
});
});
};
renderPullUpLoad() {
let { pullUpLoad, isPullUpTipHide } = this.props;
if (pullUpLoad && isPullUpTipHide) {
return (
<div className="b-pullup-wrapper">
<div className="after-trigger" style={{ lineHeight: ".32rem" }}>
<span style={{ color: "#999999", fontSize: ".28rem" }}>{""}</span>
</div>
</div>
);
}
if (pullUpLoad && this.state.isPullUpLoad) {
return (
<div className="b-pullup-wrapper">
<div className="after-trigger" style={{ lineHeight: ".32rem" }}>
<i className="loading-icon"></i>
<span style={{ color: "#999999", fontSize: "13px" }}>
{typeof pullUpLoad === "object" ? pullUpLoad.txt.more : "加载中..."}
</span>
</div>
</div>
);
}
if (pullUpLoad && !this.state.isPullUpLoad) {
return (
<div className="b-pullup-wrapper">
<div className="before-trigger">
<span style={{ color: "#999999", fontSize: "13px" }}>
{typeof pullUpLoad === "object" ? pullUpLoad.txt.nomore : "加载完成"}
</span>
</div>
</div>
);
}
}
renderPullUpDown() {
let { pullDownRefresh } = this.props;
let { beforePullDown, pulling, pullDownStyle, bubbleY } = this.state;
let cls = "arrow";
if (pullDownRefresh && beforePullDown) {
if (bubbleY > 50) {
cls += " up";
}
return (
<div className="b-pulldown-wrapper" style={pullDownStyle}>
<div className={"after-trigger"}>
<img src={icon_arrow} className={cls}/>
<span>
{bubbleY > 50 ? "松开立即刷新" : "下拉刷新"}
</span>
</div>
</div>
);
}
if (pullDownRefresh && !beforePullDown && pulling) {
return (
<div className="b-pulldown-wrapper" style={pullDownStyle}>
<div className={"after-trigger"}>
<span>
加载中...
</span>
</div>
</div>
);
}
if (pullDownRefresh && !beforePullDown && !pulling) {
return (
<div className="b-pulldown-wrapper" style={pullDownStyle}>
<div className={"after-trigger"}>
<div>
<span>
{typeof this.options.pullDownRefresh === "object"
? this.options.pullDownRefresh.txt.success
: "刷新完成"}
</span>
</div>
</div>
</div>
);
}
}
render() {
return (
<div className="b-wrapper" ref="$dom">
<div className="b-scroll-content">
{this.props.children}
{this.renderPullUpLoad()}
</div>
{this.renderPullUpDown()}
</div>
);
}
}
export default Scroll;
Суммировать:
那位大佬有更好的实现方式 欢迎支出