UNPKG

react-touch-scroll-loader

Version:

React component for web pull to refresh and load more, 下拉刷新, 加载更多

223 lines (199 loc) 7.57 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _react = require('react'); var _react2 = _interopRequireDefault(_react); require('./touch-loader.less'); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } var STATS = { init: '', pulling: 'pulling', enough: 'pulling enough', refreshing: 'refreshing', refreshed: 'refreshed', reset: 'reset', loading: 'loading' // loading more }; // pull to refresh // tap bottom to load more exports.default = _react2.default.createClass({ displayName: 'react-touch-loader', getInitialState: function getInitialState() { return { loaderState: STATS.init, pullHeight: 0, progressed: 0 }; }, getDefaultProps: function getDefaultProps() { return { distanceToRefresh: 60 }; }, setInitialTouch: function setInitialTouch(touch) { this._initialTouch = { clientY: touch.clientY }; }, calculateDistance: function calculateDistance(touch) { return touch.clientY - this._initialTouch.clientY; }, // 拖拽的缓动公式 - easeOutSine easing: function easing(distance) { // t: current time, b: begInnIng value, c: change In value, d: duration var t = distance; var b = 0; var d = screen.availHeight; // 允许拖拽的最大距离 var c = d / 2.5; // 提示标签最大有效拖拽距离 return c * Math.sin(t / d * (Math.PI / 2)) + b; }, canRefresh: function canRefresh() { return this.props.onRefresh && [STATS.refreshing, STATS.loading].indexOf(this.state.loaderState) < 0; }, touchStart: function touchStart(e) { if (!this.canRefresh()) return; if (e.touches.length == 1) this._initialTouch = { clientY: e.touches[0].clientY, scrollTop: this.refs.panel.scrollTop }; }, touchMove: function touchMove(e) { if (!this.canRefresh()) return; var scrollTop = this.refs.panel.scrollTop; var distance = this.calculateDistance(e.touches[0]); if (distance > 0 && scrollTop <= 0) { var pullDistance = distance - this._initialTouch.scrollTop; if (pullDistance < 0) { // 修复webview滚动过程中touchstart时计算panel.scrollTop不准 pullDistance = 0; this._initialTouch.scrollTop = distance; } var pullHeight = this.easing(pullDistance); if (pullHeight) e.preventDefault(); // 减弱滚动 this.setState({ loaderState: pullHeight > this.props.distanceToRefresh ? STATS.enough : STATS.pulling, pullHeight: pullHeight }); } }, touchEnd: function touchEnd() { if (!this.canRefresh()) return; var endState = { loaderState: STATS.reset, pullHeight: 0 }; if (this.state.loaderState == STATS.enough) { // refreshing this.setState({ loaderState: STATS.refreshing, pullHeight: 0 }); // trigger refresh action this.props.onRefresh(function () { // resove this.setState({ loaderState: STATS.refreshed, pullHeight: 0 }); }.bind(this), function () { // reject this.setState(endState); // reset }.bind(this)); } else this.setState(endState); // reset }, loadMore: function loadMore() { this.setState({ loaderState: STATS.loading }); this.props.onLoadMore(function () { // resolve this.setState({ loaderState: STATS.init }); }.bind(this)); }, componentWillReceiveProps: function componentWillReceiveProps(nextProps) { if (nextProps.initializing < 2) this.setState({ progressed: 0 // reset progress animation state }); }, animationEnd: function animationEnd() { var newState = {}; if (this.state.loaderState == STATS.refreshed) newState.loaderState = STATS.init; if (this.props.initializing > 1) newState.progressed = 1; this.setState(newState); }, scrollLoadMore: function scrollLoadMore(event) { var footer = void 0, currentTarget = void 0; if (this.state.loaderState == STATS.refreshing || this.state.loaderState == STATS.loading) return; currentTarget = event.currentTarget; footer = this.refs.footer; if (!footer) return; var hasLoad = footer.offsetTop - currentTarget.offsetHeight - currentTarget.scrollTop - 30 <= 0; this.refs.panel.removeEventListener('scroll', function () { return false; }); if (hasLoad) { this.loadMore(); this.refs.panel.addEventListener('scroll', this.scrollLoadMore); } }, componentDidMount: function componentDidMount() { this.refs.panel.addEventListener('scroll', this.scrollLoadMore); }, render: function render() { var _props = this.props, className = _props.className, hasMore = _props.hasMore, initializing = _props.initializing; var _state = this.state, loaderState = _state.loaderState, pullHeight = _state.pullHeight, progressed = _state.progressed; var footer = hasMore ? _react2.default.createElement( 'div', { className: 'tloader-footer', ref: 'footer' }, _react2.default.createElement('div', { className: 'tloader-btn', onClick: this.loadMore }), _react2.default.createElement( 'div', { className: 'tloader-loading' }, _react2.default.createElement('i', { className: 'ui-loading' }) ) ) : null; var style = pullHeight ? { WebkitTransform: 'translate3d(0,' + pullHeight + 'px,0)' } : null; var progressClassName = ''; if (!progressed) { if (initializing > 0) progressClassName += ' tloader-progress'; if (initializing > 1) progressClassName += ' ed'; } return _react2.default.createElement( 'div', { ref: 'panel', className: 'tloader state-' + loaderState + ' ' + className + progressClassName, onTouchStart: this.touchStart, onTouchMove: this.touchMove, onTouchEnd: this.touchEnd, onAnimationEnd: this.animationEnd }, _react2.default.createElement( 'div', { className: 'tloader-symbol' }, _react2.default.createElement( 'div', { className: 'tloader-msg' }, _react2.default.createElement('i', null) ), _react2.default.createElement( 'div', { className: 'tloader-loading' }, _react2.default.createElement('i', { className: 'ui-loading' }) ) ), _react2.default.createElement( 'div', { className: 'tloader-body', style: style }, this.props.children ), footer ); } });