From 58c9d76344759d8e72373497d409385d4813c400 Mon Sep 17 00:00:00 2001 From: xuhaojun Date: Sat, 20 Jan 2018 02:43:02 +0800 Subject: [PATCH 01/18] Expose wraper component. --- src/index.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/index.js b/src/index.js index cf0c3c6..7b0bc7f 100644 --- a/src/index.js +++ b/src/index.js @@ -102,14 +102,16 @@ export default class PullRefresh extends Component { color, onRefresh, disabled, + typeName, as, children, ...props } = this.props const PullRefreshComponent = render const Container = as + const Wraper = typeName === null ? React.Fragment : typeName return ( -
+ { render(this.props, this.state) } -
+ ) } } PullRefresh.propTypes = { + typeName: PropTypes.oneOfType([ PropTypes.object, PropTypes.string ]), as: PropTypes.oneOfType([ PropTypes.object, PropTypes.string ]), onRefresh: PropTypes.func, style: PropTypes.object, @@ -141,6 +144,7 @@ PullRefresh.propTypes = { } PullRefresh.defaultProps = { + typeName: 'div', as: 'div', style: {}, disabled: false, From 67b7be644a2be1bf38696ce3eff1c9aa0d9e1e5f Mon Sep 17 00:00:00 2001 From: XuHaoJun Date: Sat, 20 Jan 2018 04:08:04 +0800 Subject: [PATCH 02/18] Fix onMouse... onTouch... not trigger. --- src/index.js | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/index.js b/src/index.js index 7b0bc7f..3a3f6a2 100644 --- a/src/index.js +++ b/src/index.js @@ -105,6 +105,13 @@ export default class PullRefresh extends Component { typeName, as, children, + onScroll, + onMouseDown, + onMouseUp, + onMouseMove, + onTouchStart, + onTouchEnd, + onTouchMove, ...props } = this.props const PullRefreshComponent = render @@ -115,13 +122,13 @@ export default class PullRefresh extends Component { { children } From af7aa7fed4784741e51e5fff0a4b5fe138d835c1 Mon Sep 17 00:00:00 2001 From: XuHaoJun Date: Sat, 20 Jan 2018 06:17:21 +0800 Subject: [PATCH 03/18] update proptypes. --- src/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.js b/src/index.js index 3a3f6a2..4f44fc6 100644 --- a/src/index.js +++ b/src/index.js @@ -140,7 +140,7 @@ export default class PullRefresh extends Component { PullRefresh.propTypes = { typeName: PropTypes.oneOfType([ PropTypes.object, PropTypes.string ]), - as: PropTypes.oneOfType([ PropTypes.object, PropTypes.string ]), + as: PropTypes.oneOfType([ PropTypes.func, PropTypes.string ]), onRefresh: PropTypes.func, style: PropTypes.object, disabled: PropTypes.bool, From fb1dadf44ec1840fed04ec8e68d24f9f79700899 Mon Sep 17 00:00:00 2001 From: XuHaoJun Date: Sun, 21 Jan 2018 09:07:21 +0800 Subject: [PATCH 04/18] Fix onMouse... onTouch... not trigger part2. --- src/index.js | 53 +++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 46 insertions(+), 7 deletions(-) diff --git a/src/index.js b/src/index.js index 4f44fc6..f4c9f1d 100644 --- a/src/index.js +++ b/src/index.js @@ -47,6 +47,21 @@ export default class PullRefresh extends Component { onScroll(evt) { this._scrollTop = evt.currentTarget.scrollTop !== undefined ? evt.currentTarget.scrollTop : evt.nativeEvent.contentOffset.y + if (this.props.scroll) { + this.props.onScroll(evt) + } + } + onMouseDown(evt) { + this.onDown(evt) + if (this.props.onMouseDown) { + this.props.onMouseDown(evt) + } + } + onTouchStart(evt) { + this.onDown(evt) + if (this.props.onTouchStart) { + this.props.onTouchStart(evt) + } } onDown(evt) { const { phase } = this.state @@ -56,12 +71,36 @@ export default class PullRefresh extends Component { const ey = evt.nativeEvent.touches ? evt.nativeEvent.touches[0].pageY : evt.pageY this._py = ey } + async onMouseUp(evt) { + await this.onUp(evt) + if (this.props.onMouseUp) { + this.props.onMouseUp(evt) + } + } + async onTouchEnd(evt) { + await this.onUp(evt) + if (this.props.onTouchEnd) { + this.props.onTouchEnd(evt) + } + } async onUp(evt) { const { phase } = this.state if(phase === 'refreshed' || phase === 'refreshing') return this._down = false await this._refresh() } + onMouseMove(evt) { + this.onMove(evt) + if (this.onMouseMove) { + this.props.onMouseMove(evt) + } + } + onTouchMove(evt) { + this.onMove(evt) + if (this.props.onTouchMove) { + this.props.onTouchMove(evt) + } + } onMove(evt) { const { phase } = this.state if(this._willRefresh || !this._down) return @@ -122,13 +161,13 @@ export default class PullRefresh extends Component { { children } From 1efca89b7d806699c350a53244a674e4d923a0ad Mon Sep 17 00:00:00 2001 From: XuHaoJun Date: Sun, 21 Jan 2018 10:45:07 +0800 Subject: [PATCH 05/18] Fix onMouse... onTouch... not trigger part3. --- src/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.js b/src/index.js index f4c9f1d..5a7cb4d 100644 --- a/src/index.js +++ b/src/index.js @@ -91,7 +91,7 @@ export default class PullRefresh extends Component { } onMouseMove(evt) { this.onMove(evt) - if (this.onMouseMove) { + if (this.props.onMouseMove) { this.props.onMouseMove(evt) } } From 26a59beca0893e89a682029f216c3d51fde0bd8d Mon Sep 17 00:00:00 2001 From: XuHaoJun Date: Sun, 21 Jan 2018 21:38:29 +0800 Subject: [PATCH 06/18] Add disableMouse disableTouch options. --- src/index.js | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/src/index.js b/src/index.js index 5a7cb4d..d442b9f 100644 --- a/src/index.js +++ b/src/index.js @@ -52,13 +52,17 @@ export default class PullRefresh extends Component { } } onMouseDown(evt) { - this.onDown(evt) + if (!this.props.disableMouse) { + this.onDown(evt) + } if (this.props.onMouseDown) { this.props.onMouseDown(evt) } } onTouchStart(evt) { - this.onDown(evt) + if (!this.props.disableTouch) { + this.onDown(evt) + } if (this.props.onTouchStart) { this.props.onTouchStart(evt) } @@ -72,13 +76,17 @@ export default class PullRefresh extends Component { this._py = ey } async onMouseUp(evt) { - await this.onUp(evt) + if (!this.props.disableMouse) { + await this.onUp(evt) + } if (this.props.onMouseUp) { this.props.onMouseUp(evt) } } async onTouchEnd(evt) { - await this.onUp(evt) + if (!this.props.disableTouch) { + await this.onUp(evt) + } if (this.props.onTouchEnd) { this.props.onTouchEnd(evt) } @@ -90,13 +98,17 @@ export default class PullRefresh extends Component { await this._refresh() } onMouseMove(evt) { - this.onMove(evt) + if (!this.props.disableMouse) { + this.onMove(evt) + } if (this.props.onMouseMove) { this.props.onMouseMove(evt) } } onTouchMove(evt) { - this.onMove(evt) + if (!this.props.disableTouch) { + this.onMove(evt) + } if (this.props.onTouchMove) { this.props.onTouchMove(evt) } @@ -183,6 +195,8 @@ PullRefresh.propTypes = { onRefresh: PropTypes.func, style: PropTypes.object, disabled: PropTypes.bool, + disableMouse: PropTypes.bool, + disableTouch: PropTypes.bool, color: PropTypes.string, bgColor: PropTypes.string, render: PropTypes.func, @@ -194,6 +208,8 @@ PullRefresh.defaultProps = { as: 'div', style: {}, disabled: false, + disableMouse: false, + disableTouch: false, color: '#4285f4', bgColor: '#fff', render: renderDefault, From a361e91487ae62671836d245451f3bfa6702c43b Mon Sep 17 00:00:00 2001 From: XuHaoJun Date: Sun, 21 Jan 2018 22:18:30 +0800 Subject: [PATCH 07/18] Fix. --- src/index.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/index.js b/src/index.js index d442b9f..da48a65 100644 --- a/src/index.js +++ b/src/index.js @@ -153,6 +153,8 @@ export default class PullRefresh extends Component { color, onRefresh, disabled, + disableMouse, + disableTouch, typeName, as, children, From 78443128c47526e94cffc79744d23c51622a38e3 Mon Sep 17 00:00:00 2001 From: XuHaoJun Date: Mon, 22 Jan 2018 02:18:57 +0800 Subject: [PATCH 08/18] Improve performance. --- src/index.js | 42 ++++++++++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/src/index.js b/src/index.js index da48a65..fb09e90 100644 --- a/src/index.js +++ b/src/index.js @@ -15,6 +15,14 @@ export default class PullRefresh extends Component { max: MAX, phase: '' } + this.onScroll = this.onScroll.bind(this) + this.onMouseDown = this.onMouseDown.bind(this) + this.onMouseUp = this.onMouseUp.bind(this) + this.onMouseMove = this.onMouseMove.bind(this) + this.onTouchStart = this.onTouchStart.bind(this) + this.onTouchEnd = this.onTouchEnd.bind(this) + this.onTouchMove = this.onTouchMove.bind(this) + this.onSpringUpdate = this.onSpringUpdate.bind(this) } async refresh() { this.setState({ @@ -45,14 +53,16 @@ export default class PullRefresh extends Component { this._spring.endValue = this._y } onScroll(evt) { - this._scrollTop = evt.currentTarget.scrollTop !== undefined - ? evt.currentTarget.scrollTop : evt.nativeEvent.contentOffset.y + if (!this.props.disabled) { + this._scrollTop = evt.currentTarget.scrollTop !== undefined + ? evt.currentTarget.scrollTop : evt.nativeEvent.contentOffset.y + } if (this.props.scroll) { this.props.onScroll(evt) } } onMouseDown(evt) { - if (!this.props.disableMouse) { + if (!this.props.disabled && !this.props.disableMouse) { this.onDown(evt) } if (this.props.onMouseDown) { @@ -60,7 +70,7 @@ export default class PullRefresh extends Component { } } onTouchStart(evt) { - if (!this.props.disableTouch) { + if (!this.props.disabled && !this.props.disableTouch) { this.onDown(evt) } if (this.props.onTouchStart) { @@ -76,7 +86,7 @@ export default class PullRefresh extends Component { this._py = ey } async onMouseUp(evt) { - if (!this.props.disableMouse) { + if (!this.props.disabled && !this.props.disableMouse) { await this.onUp(evt) } if (this.props.onMouseUp) { @@ -84,7 +94,7 @@ export default class PullRefresh extends Component { } } async onTouchEnd(evt) { - if (!this.props.disableTouch) { + if (!this.props.disabled && !this.props.disableTouch) { await this.onUp(evt) } if (this.props.onTouchEnd) { @@ -98,7 +108,7 @@ export default class PullRefresh extends Component { await this._refresh() } onMouseMove(evt) { - if (!this.props.disableMouse) { + if (!this.props.disabled && !this.props.disableMouse) { this.onMove(evt) } if (this.props.onMouseMove) { @@ -106,7 +116,7 @@ export default class PullRefresh extends Component { } } onTouchMove(evt) { - if (!this.props.disableTouch) { + if (!this.props.disabled && !this.props.disableTouch) { this.onMove(evt) } if (this.props.onTouchMove) { @@ -143,7 +153,7 @@ export default class PullRefresh extends Component { this._y = 0 this._scrollTop = 0 this._spring = new Spring(60, 10) - this._spring.onUpdate = ::this.onSpringUpdate + this._spring.onUpdate = this.onSpringUpdate } render() { const { @@ -175,13 +185,13 @@ export default class PullRefresh extends Component { { children } From 32a6ff402f061214f40abf446692fb6dce7a38c5 Mon Sep 17 00:00:00 2001 From: XuHaoJun Date: Mon, 22 Jan 2018 03:33:00 +0800 Subject: [PATCH 09/18] Improve spring. --- src/spring.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/spring.js b/src/spring.js index acd6984..ce2dcea 100644 --- a/src/spring.js +++ b/src/spring.js @@ -1,6 +1,14 @@ import EventEmitter from 'event-emitter' -const sleep = msec => new Promise(resolve => setTimeout(resolve, msec)) +const sleep = msec => { + return new Promise(resolve => { + if (requestAnimationFrame) { + requestAnimationFrame(resolve) + } else { + setTimeout(resolve, msec) + } + }) +} const loop = async promise => { const proc = async () => await promise() && await proc() await proc() From dffb0a2a4bfd82d526de0fda9f6f09ccb82c3c9b Mon Sep 17 00:00:00 2001 From: XuHaoJun Date: Mon, 22 Jan 2018 03:33:20 +0800 Subject: [PATCH 10/18] Fix typo. --- src/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.js b/src/index.js index fb09e90..764440c 100644 --- a/src/index.js +++ b/src/index.js @@ -57,7 +57,7 @@ export default class PullRefresh extends Component { this._scrollTop = evt.currentTarget.scrollTop !== undefined ? evt.currentTarget.scrollTop : evt.nativeEvent.contentOffset.y } - if (this.props.scroll) { + if (this.props.onScroll) { this.props.onScroll(evt) } } From fdf083e51d4b8fa34dad5ff386a5a434b38d3c46 Mon Sep 17 00:00:00 2001 From: XuHaoJun Date: Mon, 22 Jan 2018 04:11:31 +0800 Subject: [PATCH 11/18] Trigger custom event first. --- src/index.js | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/src/index.js b/src/index.js index 764440c..fa701b4 100644 --- a/src/index.js +++ b/src/index.js @@ -53,29 +53,29 @@ export default class PullRefresh extends Component { this._spring.endValue = this._y } onScroll(evt) { + if (this.props.onScroll) { + this.props.onScroll(evt) + } if (!this.props.disabled) { this._scrollTop = evt.currentTarget.scrollTop !== undefined ? evt.currentTarget.scrollTop : evt.nativeEvent.contentOffset.y } - if (this.props.onScroll) { - this.props.onScroll(evt) - } } onMouseDown(evt) { - if (!this.props.disabled && !this.props.disableMouse) { - this.onDown(evt) - } if (this.props.onMouseDown) { this.props.onMouseDown(evt) } - } - onTouchStart(evt) { - if (!this.props.disabled && !this.props.disableTouch) { + if (!this.props.disabled && !this.props.disableMouse) { this.onDown(evt) } + } + onTouchStart(evt) { if (this.props.onTouchStart) { this.props.onTouchStart(evt) } + if (!this.props.disabled && !this.props.disableTouch) { + this.onDown(evt) + } } onDown(evt) { const { phase } = this.state @@ -86,20 +86,20 @@ export default class PullRefresh extends Component { this._py = ey } async onMouseUp(evt) { - if (!this.props.disabled && !this.props.disableMouse) { - await this.onUp(evt) - } if (this.props.onMouseUp) { this.props.onMouseUp(evt) } - } - async onTouchEnd(evt) { - if (!this.props.disabled && !this.props.disableTouch) { + if (!this.props.disabled && !this.props.disableMouse) { await this.onUp(evt) } + } + async onTouchEnd(evt) { if (this.props.onTouchEnd) { this.props.onTouchEnd(evt) } + if (!this.props.disabled && !this.props.disableTouch) { + await this.onUp(evt) + } } async onUp(evt) { const { phase } = this.state @@ -108,20 +108,20 @@ export default class PullRefresh extends Component { await this._refresh() } onMouseMove(evt) { - if (!this.props.disabled && !this.props.disableMouse) { - this.onMove(evt) - } if (this.props.onMouseMove) { this.props.onMouseMove(evt) } - } - onTouchMove(evt) { - if (!this.props.disabled && !this.props.disableTouch) { + if (!this.props.disabled && !this.props.disableMouse) { this.onMove(evt) } + } + onTouchMove(evt) { if (this.props.onTouchMove) { this.props.onTouchMove(evt) } + if (!this.props.disabled && !this.props.disableTouch) { + this.onMove(evt) + } } onMove(evt) { const { phase } = this.state From dc3bdd2cdef22d51ea6e0c8ba667d0af89345b1b Mon Sep 17 00:00:00 2001 From: XuHaoJun Date: Mon, 22 Jan 2018 04:50:27 +0800 Subject: [PATCH 12/18] update. --- src/spring.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/spring.js b/src/spring.js index ce2dcea..de2783e 100644 --- a/src/spring.js +++ b/src/spring.js @@ -67,7 +67,7 @@ export default class Spring { await sleep(1000 / 60) if(this._paused) return true // TODO: dummy -> use tention,friction - const dv = (this._endValue - this._value) / 5 + const dv = (this._endValue - this._value) / 2 this.setValue(this._value + dv) return Math.abs(dv) > 0.2 }) From eb7d4cb5b5851312bf9daf123862eaa8fe0f4ec4 Mon Sep 17 00:00:00 2001 From: XuHaoJun Date: Mon, 22 Jan 2018 12:19:57 +0800 Subject: [PATCH 13/18] Improve performance. --- src/index.js | 172 ++++++++++++++++++++++++++++++++++++++------------ src/spring.js | 37 +++++------ 2 files changed, 148 insertions(+), 61 deletions(-) diff --git a/src/index.js b/src/index.js index fa701b4..801a294 100644 --- a/src/index.js +++ b/src/index.js @@ -6,7 +6,7 @@ import renderDefault from './component' const MAX = 100 const sleep = msec => new Promise(resolve => setTimeout(resolve, msec)) -export default class PullRefresh extends Component { +class PullRefresh extends Component { constructor(props) { super(props) this.state = { @@ -34,7 +34,7 @@ export default class PullRefresh extends Component { async _refresh() { const { max, phase } = this.state const { onRefresh } = this.props - if(phase === 'willRefresh') { + if (phase === 'willRefresh') { this._willRefresh = true await this._spring.to(max) this._spring.pause() @@ -57,8 +57,10 @@ export default class PullRefresh extends Component { this.props.onScroll(evt) } if (!this.props.disabled) { - this._scrollTop = evt.currentTarget.scrollTop !== undefined - ? evt.currentTarget.scrollTop : evt.nativeEvent.contentOffset.y + this._scrollTop = + evt.currentTarget.scrollTop !== undefined + ? evt.currentTarget.scrollTop + : evt.nativeEvent.contentOffset.y } } onMouseDown(evt) { @@ -79,11 +81,17 @@ export default class PullRefresh extends Component { } onDown(evt) { const { phase } = this.state - if(this._willRefresh) return - if(phase === 'refreshed' || phase === 'refreshing') return + if (this._willRefresh) return + if (phase === 'refreshed' || phase === 'refreshing') return this._down = true - const ey = evt.nativeEvent.touches ? evt.nativeEvent.touches[0].pageY : evt.pageY + const ey = evt.nativeEvent.touches + ? evt.nativeEvent.touches[0].pageY + : evt.pageY + const ex = evt.nativeEvent.touches + ? evt.nativeEvent.touches[0].pageX + : evt.pageX this._py = ey + this._px = ex } async onMouseUp(evt) { if (this.props.onMouseUp) { @@ -103,7 +111,7 @@ export default class PullRefresh extends Component { } async onUp(evt) { const { phase } = this.state - if(phase === 'refreshed' || phase === 'refreshing') return + if (phase === 'refreshed' || phase === 'refreshing') return this._down = false await this._refresh() } @@ -125,14 +133,24 @@ export default class PullRefresh extends Component { } onMove(evt) { const { phase } = this.state - if(this._willRefresh || !this._down) return - if(phase === 'refreshed' || phase === 'refreshing') return - const ey = evt.nativeEvent.touches ? evt.nativeEvent.touches[0].pageY : evt.pageY - if(this._scrollTop <= 0) { - this._y = this._y + ey - this._py - this._spring.endValue = this._y + if (this._willRefresh || !this._down) return + if (phase === 'refreshed' || phase === 'refreshing') return + const ey = evt.nativeEvent.touches + ? evt.nativeEvent.touches[0].pageY + : evt.pageY + const ex = evt.nativeEvent.touches + ? evt.nativeEvent.touches[0].pageX + : evt.pageX + const vy = ey - this._py + const vx = ex - this._px + if (this._scrollTop <= 0 && Math.abs(vy) > Math.abs(vx)) { + if (vy >= 10 || vy < 0) { + this._y = this._y + vy + this._spring.endValue = this._y + } } this._py = ey + this._px = ex } onSpringUpdate(spring) { const { max, yRefreshing, phase } = this.state @@ -141,11 +159,11 @@ export default class PullRefresh extends Component { y, yRefreshing: this._willRefresh ? Math.max(y, yRefreshing) : y }) - if(phase !== 'refreshed' && phase !== 'refreshing') { - const newPhase = y >= max ? 'willRefresh' : '' - if(phase !== newPhase) this.setState({ phase: newPhase }) + if (phase !== 'refreshed' && phase !== 'refreshing') { + const newPhase = y >= max ? 'willRefresh' : '' + if (phase !== newPhase) this.setState({ phase: newPhase }) } - if(phase === 'refreshed' && y === 0) { + if (phase === 'refreshed' && y === 0) { this.setState({ phase: '' }) } } @@ -155,6 +173,70 @@ export default class PullRefresh extends Component { this._spring = new Spring(60, 10) this._spring.onUpdate = this.onSpringUpdate } + render() { + return this.props.render(this.props, this.state) + } +} + +PullRefresh.propTypes = { + onRefresh: PropTypes.func, + style: PropTypes.object, + disabled: PropTypes.bool, + disableMouse: PropTypes.bool, + disableTouch: PropTypes.bool, + color: PropTypes.string, + bgColor: PropTypes.string, + render: PropTypes.func, + zIndex: PropTypes.number +} + +PullRefresh.defaultProps = { + style: {}, + disabled: false, + disableMouse: false, + disableTouch: false, + color: '#4285f4', + bgColor: '#fff', + render: renderDefault, + zIndex: undefined +} + +export default class PullRefreshConvertProps extends Component { + constructor(props) { + super(props) + this.setPullRefreshRef = this.setPullRefreshRef.bind(this) + this.onScroll = this.onScroll.bind(this) + this.onMouseDown = this.onMouseDown.bind(this) + this.onMouseUp = this.onMouseUp.bind(this) + this.onMouseMove = this.onMouseMove.bind(this) + this.onTouchStart = this.onTouchStart.bind(this) + this.onTouchEnd = this.onTouchEnd.bind(this) + this.onTouchMove = this.onTouchMove.bind(this) + } + setPullRefreshRef(pr) { + this.pr = pr + } + onScroll(e) { + this.pr.onScroll(e) + } + onMouseDown(e) { + this.pr.onMouseDown(e) + } + onMouseUp(e) { + this.pr.onMouseUp(e) + } + onMouseMove(e) { + this.pr.onMouseMove(e) + } + onTouchStart(e) { + this.pr.onTouchStart(e) + } + onTouchEnd(e) { + this.pr.onTouchEnd(e) + } + onTouchMove(e) { + this.pr.onTouchMove(e) + } render() { const { zIndex, @@ -177,33 +259,48 @@ export default class PullRefresh extends Component { onTouchMove, ...props } = this.props - const PullRefreshComponent = render const Container = as const Wraper = typeName === null ? React.Fragment : typeName return ( - { children } + {children} - { render(this.props, this.state) } + ) } } -PullRefresh.propTypes = { - typeName: PropTypes.oneOfType([ PropTypes.object, PropTypes.string ]), - as: PropTypes.oneOfType([ PropTypes.func, PropTypes.string ]), +PullRefreshConvertProps.propTypes = { + typeName: PropTypes.oneOfType([PropTypes.object, PropTypes.string]), + as: PropTypes.oneOfType([PropTypes.func, PropTypes.string]), onRefresh: PropTypes.func, style: PropTypes.object, disabled: PropTypes.bool, @@ -215,15 +312,10 @@ PullRefresh.propTypes = { zIndex: PropTypes.number } -PullRefresh.defaultProps = { +PullRefreshConvertProps.defaultProps = { typeName: 'div', as: 'div', - style: {}, - disabled: false, - disableMouse: false, - disableTouch: false, - color: '#4285f4', - bgColor: '#fff', - render: renderDefault, - zIndex: undefined + render: renderDefault } + +export { renderDefault } diff --git a/src/spring.js b/src/spring.js index de2783e..4401bbe 100644 --- a/src/spring.js +++ b/src/spring.js @@ -1,16 +1,8 @@ import EventEmitter from 'event-emitter' -const sleep = msec => { - return new Promise(resolve => { - if (requestAnimationFrame) { - requestAnimationFrame(resolve) - } else { - setTimeout(resolve, msec) - } - }) -} +const sleep = msec => new Promise(resolve => setTimeout(resolve, msec)) const loop = async promise => { - const proc = async () => await promise() && await proc() + const proc = async () => (await promise()) && (await proc()) await proc() } @@ -46,7 +38,7 @@ export default class Spring { return this._value } setValue(value) { - if(this._value !== value) { + if (this._value !== value) { this._value = value this._onUpdate(this) } @@ -57,23 +49,26 @@ export default class Spring { async _wait(type) { await new Promise(resolve => this._emitter.once(type, resolve)) } - async loop() { - if(this._loop) return + loop() { + if (this._loop) return this._emit('start') this._loop = true - await loop(async () => { - await sleep(1000 / 60) - if(this._paused) return true + const loop = () => { + if (this._paused) return true // TODO: dummy -> use tention,friction const dv = (this._endValue - this._value) / 2 this.setValue(this._value + dv) - return Math.abs(dv) > 0.2 - }) - this.setValue(this._endValue) + if (Math.abs(dv) > 0.5) { + requestAnimationFrame(loop) + } else { + this.setValue(this._endValue) - this._loop = false - this._emit('end') + this._loop = false + this._emit('end') + } + } + requestAnimationFrame(loop) } } From 3a247255a177c6be4c692b8b2503ecdc7cc473e9 Mon Sep 17 00:00:00 2001 From: XuHaoJun Date: Tue, 23 Jan 2018 04:04:00 +0800 Subject: [PATCH 14/18] Add HOC feature and example. --- src/component/index.js | 46 ++++++------- src/component/index.native.js | 123 +++++++++++++++++----------------- src/index.js | 117 +++++++++++++++++++------------- 3 files changed, 153 insertions(+), 133 deletions(-) diff --git a/src/component/index.js b/src/component/index.js index 9b22c0a..65b48bd 100644 --- a/src/component/index.js +++ b/src/component/index.js @@ -30,7 +30,8 @@ const Component = styled.div` border-radius: 20px; width: 40px; height: 40px; - box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 1px 5px 0 rgba(0, 0, 0, 0.12), 0 3px 1px -2px rgba(0, 0, 0, 0.2); + box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 1px 5px 0 rgba(0, 0, 0, 0.12), + 0 3px 1px -2px rgba(0, 0, 0, 0.2); ` const RotatingSvg = styled.svg` @@ -44,9 +45,8 @@ const DashedCircle = styled.circle` animation: ${dashed} 1.4s ease-in-out infinite; ` -export default (props, state) => { - const { max, yRefreshing, y, phase } = state - const { zIndex, color, bgColor } = props +export default props => { + const { zIndex, color, bgColor, max, yRefreshing, y, phase } = props const p = Math.atan(y / max) const pMax = Math.atan(yRefreshing / max) const r = Math.PI * 10 * 2 @@ -55,47 +55,47 @@ export default (props, state) => { const refreshed = phase === 'refreshed' return ( - { phase !== 'refreshing' && - - } + {phase !== 'refreshing' && ( + + )} ) } - - diff --git a/src/component/index.native.js b/src/component/index.native.js index 650c9d2..871c153 100644 --- a/src/component/index.native.js +++ b/src/component/index.native.js @@ -1,7 +1,11 @@ import React, { Component } from 'react' import styled from 'styled-components/native' import { Easing, Animated, View } from 'react-native' -import { Svg as NativeSvg, Circle as NativeCircle, Path as NativePath } from 'react-native-svg' +import { + Svg as NativeSvg, + Circle as NativeCircle, + Path as NativePath +} from 'react-native-svg' class RotatingSvg extends Component { constructor(props) { @@ -12,14 +16,16 @@ class RotatingSvg extends Component { } } componentDidMount() { - this.state.r.addListener((r) => { + this.state.r.addListener(r => { this.setState({ value: r.value }) }) - this._animated = Animated.loop(Animated.timing(this.state.r, { - easing: Easing.linear, - toValue: 270, - duration: 1400, - })) + this._animated = Animated.loop( + Animated.timing(this.state.r, { + easing: Easing.linear, + toValue: 270, + duration: 1400 + }) + ) this._animated.start() } componentWillUnmount() { @@ -31,11 +37,8 @@ class RotatingSvg extends Component { return ( @@ -48,7 +51,7 @@ class DashedCircle extends Component { super(props) this.state = { rotate: new Animated.Value(0), - dash: new Animated.Value(62), + dash: new Animated.Value(62) } } componentDidMount() { @@ -58,28 +61,30 @@ class DashedCircle extends Component { this.state.dash.addListener(d => { this.setState({ d: d.value }) }) - this._animated = Animated.loop(Animated.parallel([ - Animated.sequence([ - Animated.timing(this.state.rotate, { - toValue: 135, - duration: 700, - }), - Animated.timing(this.state.rotate, { - toValue: 450, - duration: 700 - }), - ]), - Animated.sequence([ - Animated.timing(this.state.dash, { - toValue: 62/4, - duration: 700 - }), - Animated.timing(this.state.dash, { - toValue: 62, - duration: 700 - }), + this._animated = Animated.loop( + Animated.parallel([ + Animated.sequence([ + Animated.timing(this.state.rotate, { + toValue: 135, + duration: 700 + }), + Animated.timing(this.state.rotate, { + toValue: 450, + duration: 700 + }) + ]), + Animated.sequence([ + Animated.timing(this.state.dash, { + toValue: 62 / 4, + duration: 700 + }), + Animated.timing(this.state.dash, { + toValue: 62, + duration: 700 + }) + ]) ]) - ])) + ) this._animated.start() } componentWillUnmount() { @@ -90,15 +95,12 @@ class DashedCircle extends Component { const { strokeDasharray, style, ...props } = this.props return ( ) @@ -117,9 +119,8 @@ const Container = styled(View)` shadow-color: #000; ` -export default (props, state, children) => { - const { max, yRefreshing, y, phase } = state - const { color, bgColor } = props +export default (props, children) => { + const { color, bgColor, max, yRefreshing, y, phase } = props const p = Math.atan(y / max) const pMax = Math.atan(yRefreshing / max) const r = Math.PI * 10 * 2 @@ -129,7 +130,7 @@ export default (props, state, children) => { return [ children, { - { phase !== 'refreshing' && - - } + {phase !== 'refreshing' && ( + + )} ] } - - diff --git a/src/index.js b/src/index.js index 801a294..0027e4c 100644 --- a/src/index.js +++ b/src/index.js @@ -1,7 +1,8 @@ import React, { Component } from 'react' import PropTypes from 'prop-types' + import Spring from './spring' -import renderDefault from './component' +import Indicator from './component' const MAX = 100 const sleep = msec => new Promise(resolve => setTimeout(resolve, msec)) @@ -174,11 +175,29 @@ class PullRefresh extends Component { this._spring.onUpdate = this.onSpringUpdate } render() { - return this.props.render(this.props, this.state) + const { zIndex, color, bgColor } = this.props + const { max, yRefreshing, y, phase } = this.state + const indicatorProps = { + zIndex, + color, + bgColor, + max, + yRefreshing, + y, + phase + } + const { IndicatorComponent } = this.props + return } } PullRefresh.propTypes = { + wraperComponent: PropTypes.oneOfType([ + PropTypes.func, + PropTypes.object, + PropTypes.string + ]), + component: PropTypes.oneOfType([PropTypes.func, PropTypes.string]), onRefresh: PropTypes.func, style: PropTypes.object, disabled: PropTypes.bool, @@ -186,7 +205,7 @@ PullRefresh.propTypes = { disableTouch: PropTypes.bool, color: PropTypes.string, bgColor: PropTypes.string, - render: PropTypes.func, + IndicatorComponent: PropTypes.func, zIndex: PropTypes.number } @@ -197,11 +216,11 @@ PullRefresh.defaultProps = { disableTouch: false, color: '#4285f4', bgColor: '#fff', - render: renderDefault, + IndicatorComponent: Indicator, zIndex: undefined } -export default class PullRefreshConvertProps extends Component { +class PullRefreshConvertProps extends Component { constructor(props) { super(props) this.setPullRefreshRef = this.setPullRefreshRef.bind(this) @@ -239,17 +258,6 @@ export default class PullRefreshConvertProps extends Component { } render() { const { - zIndex, - render, - bgColor, - color, - onRefresh, - disabled, - disableMouse, - disableTouch, - typeName, - as, - children, onScroll, onMouseDown, onMouseUp, @@ -257,14 +265,46 @@ export default class PullRefreshConvertProps extends Component { onTouchStart, onTouchEnd, onTouchMove, - ...props + pullRefreshProps, + ...componentProps + } = this.props + const { + zIndex, + render, + bgColor, + color, + onRefresh, + disabled, + disableMouse, + disableTouch } = this.props - const Container = as - const Wraper = typeName === null ? React.Fragment : typeName + const _pullRefreshProps = + pullRefreshProps !== null && typeof pullRefreshProps === 'object' + ? pullRefreshProps + : { + zIndex, + render, + bgColor, + color, + onRefresh, + disabled, + disableMouse, + disableTouch + } + const { wraperComponent, component } = _pullRefreshProps + const Component = component || 'div' + let Wraper + if (wraperComponent === null) { + Wraper = React.Fragment + } else if (!wraperComponent) { + Wraper = 'div' + } else { + Wraper = wraperComponent + } return ( - - {children} - + /> Date: Tue, 23 Jan 2018 04:05:06 +0800 Subject: [PATCH 15/18] Update README. --- README.md | 102 ++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 80 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 2bbaea6..62bd081 100644 --- a/README.md +++ b/README.md @@ -1,25 +1,24 @@ # react-pullrefresh -Pull to reflesh material design component. +Pull to reflesh material design component.
react-native is supported. ![](/2017_03_06_13_09_14.gif?raw=true) -#### Demo +## Demo -[https://yusukeshibata.github.io/react-pullrefresh/](https://yusukeshibata.github.io/react-pullrefresh/) + +## Install -#### Install - - ```sh - npm install react-pullrefresh - ``` +```sh +npm install react-pullrefresh +``` -#### Usage +## Usage - ```javascript - import PullRefresh from 'react-pullrefresh' +```javascript +import PullRefresh from 'react-pullrefresh' class App extends Component { // onRefresh function canbe async/sync @@ -44,22 +43,81 @@ react-native is supported. export default App ``` + +### HOC (High Order Component) + +```javascript +import PullRefresh, { Indicator } from 'react-pullrefresh' + +const PortalIncidator = PortalHoc(Indicator) +// control props namespace for List component. +// add extra onRefresh and pullFreshProps prop that not conflict with List Component. +function PullRefreshHoc(AnotherComponent) { + return class _PullRefreshHoc extends React.Component { + render() { + // use pullFreshProps props namespace. + const { onRefresh, pullFreshProps, ...otherProps } = this.props; + const defautWraperComponent = React.Fragment + // pullFreshProps never override AnotherComponent. + // change default wraperComponent to React.Fragment. + const _pullFreshProps = Object.assign( + { + wraperComponent: defautWraperComponent + IndicatorComponent: PortalIncidator + }, + pullFreshProps, + { + component: AnotherComponent, + onRefresh + }) + return ( + + ) + } + } +} + +// EnhancedList get extra two prop(onRefresh, pullFreshProps) +// for pull refresh feature. + +const enhance = compose(FlipMoveHoc, LazyLoadHoc, ...OtherFeatureHocs) + +export const EnhancedList = enhance(PullRefreshHoc(List)) + +// List's prop disabled and component not conflict with pullRefresh props. +const list = ( + + {listItems} +) +``` + #### Behaviour difference between v1/v2 TODO: #### Props -##### render +##### render TODO: - -##### color +##### color default: `#787878` -##### bgColor +##### bgColor default: `#ffffff` @@ -91,13 +149,13 @@ default: `undefined` #### Removed props -* size -* offset -* max -* waitingComponent -* pullingComponent -* pulledComponent -* supportDesktop +- size +- offset +- max +- waitingComponent +- pullingComponent +- pulledComponent +- supportDesktop #### License From 0bd5c0fe82f1f4ea681bf71dab6292d982194965 Mon Sep 17 00:00:00 2001 From: XuHaoJun Date: Tue, 23 Jan 2018 04:10:36 +0800 Subject: [PATCH 16/18] Fix. --- src/index.js | 2 +- src/spring.js | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/index.js b/src/index.js index 0027e4c..798d345 100644 --- a/src/index.js +++ b/src/index.js @@ -318,7 +318,7 @@ class PullRefreshConvertProps extends Component { {..._pullRefreshProps} // for check propType component={Component} - wraperComponent={Wraper} + wraperComponent={wraperComponent || null} // end onScroll={onScroll} onMouseDown={onMouseDown} diff --git a/src/spring.js b/src/spring.js index 4401bbe..b512847 100644 --- a/src/spring.js +++ b/src/spring.js @@ -55,13 +55,19 @@ export default class Spring { this._emit('start') this._loop = true + const _rafTimeout = (proc) => { + setTimeout(proc, 1000 / 60) + } + + const raf = requestAnimationFrame ? requestAnimationFrame : _rafTimeout + const loop = () => { if (this._paused) return true // TODO: dummy -> use tention,friction const dv = (this._endValue - this._value) / 2 this.setValue(this._value + dv) if (Math.abs(dv) > 0.5) { - requestAnimationFrame(loop) + raf(loop) } else { this.setValue(this._endValue) @@ -69,6 +75,6 @@ export default class Spring { this._emit('end') } } - requestAnimationFrame(loop) + raf(loop) } } From fa18a83f81230d4b8f8a45142a49941d46ff21f3 Mon Sep 17 00:00:00 2001 From: XuHaoJun Date: Tue, 23 Jan 2018 06:35:34 +0800 Subject: [PATCH 17/18] Improve readme. --- README.md | 61 +++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 43 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 62bd081..77362b9 100644 --- a/README.md +++ b/README.md @@ -51,24 +51,31 @@ import PullRefresh, { Indicator } from 'react-pullrefresh' const PortalIncidator = PortalHoc(Indicator) // control props namespace for List component. -// add extra onRefresh and pullFreshProps prop that not conflict with List Component. +// add extra onRequestMore and pullFreshProps prop that not conflict with List Component. function PullRefreshHoc(AnotherComponent) { return class _PullRefreshHoc extends React.Component { + handleRefresh = () => { + if (typeof this.props.onRequestMore === 'function') { + const reason = "pullRefresh" + return this.props.onRequestMore(reason) + } + } + render() { // use pullFreshProps props namespace. - const { onRefresh, pullFreshProps, ...otherProps } = this.props; + const { pullFreshProps, onRequestMore, ...otherProps } = this.props; const defautWraperComponent = React.Fragment - // pullFreshProps never override AnotherComponent. - // change default wraperComponent to React.Fragment. const _pullFreshProps = Object.assign( + // change default wraperComponent to React.Fragment. { wraperComponent: defautWraperComponent IndicatorComponent: PortalIncidator }, pullFreshProps, + // pullFreshProps should never override AnotherComponent. { component: AnotherComponent, - onRefresh + onRefresh: this.handleRefresh }) return ( ) } } } -// EnhancedList get extra two prop(onRefresh, pullFreshProps) + +// EnhancedList get extra two prop(onRequestMore, pullFreshProps) // for pull refresh feature. +export const EnhancedList = PullRefreshHoc(List) -const enhance = compose(FlipMoveHoc, LazyLoadHoc, ...OtherFeatureHocs) +// or more enhance +const enhance = compose(FlipMoveHoc, InfiniteLoadHoc, ...OtherFeatureHocs) +export const MulitiEnhancedList = enhance(EnhancedList) -export const EnhancedList = enhance(PullRefreshHoc(List)) + +const handleRequestMore = async (reason) => { + if (reason === 'pullRefresh') { + await fetchData({page: 1}) + } else if (reason === 'bottomInfiniteLoad') { + await fetchData({page: getNextPage()}) + } +} // List's prop disabled and component not conflict with pullRefresh props. +const pullRefreshProps = { + color: "#ff0000", + disabled: false, + zIndex: 20 +} const list = ( - - {listItems} -) + + {listItems} + +) ``` #### Behaviour difference between v1/v2 From 8f3759d50cbfd3d360a089315b3fbc248697ef55 Mon Sep 17 00:00:00 2001 From: XuHaoJun Date: Wed, 24 Jan 2018 06:46:02 +0800 Subject: [PATCH 18/18] Improve readme. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 77362b9..31f3d8d 100644 --- a/README.md +++ b/README.md @@ -83,7 +83,7 @@ function PullRefreshHoc(AnotherComponent) { pullFreshProps={_pullFreshProps} // otherProps will pass to AnotherComponent {...otherProps} - // if other HOCs(like infinite load) take onRequestMore prop + // if other HOCs(like infinite load) take onRequestMore prop(you can check by Component.propTypes) // then pass it. // else ignore it onRequestMore={onRequestMore}