移动端刷新组件XtnScroll--ReactJS实现

本文介绍如何在React中实现带有下拉刷新和加载更多功能的XtnScroll组件。通过监听触摸事件,实现数据刷新及加载,适用于移动端应用。

XtnScroll 组件,先看一下效果图

下拉的效果
这里写图片描述
上拉的效果
这里写图片描述

  • 以前都是用Vue–XtnScrollAngular4–XtnScroll来实现的刷新组件今天说说React怎么实现。其实这三种框架里用到的方法都是一样的,只是每个里面都有自己的语法特性不一样,所以实现起来大同小意了。

创建组件步骤

目录结构如下图所示

这里写图片描述

  • scss 样式文件
  • svg 加载动画
  • XtnScroll.js真正的文件
XtnScroll.js文件
import React, { Component, PropTypes } from 'react';
import { Utility } from 'components';
const styles = require('./scss/XtnScroll.scss');
/**
 * <XtnScroll 
 *   RefreshComplete={RefreshComplete} 
 *   NextDataComplete={NextDataComplete}
 *   onRefresh={()=>{....}}
 *   onNextData={()=>{....}}
 * />
 */
export default class XtnScroll extends Component {

}
属性
   static propTypes = {
    children: PropTypes.any,                   // 子项
    Percentage: PropTypes.number,              // 百分比
    RefreshComplete: PropTypes.bool,           // 刷新完成
    NextDataComplete: PropTypes.bool,          // 加载更多数据完成
    onRefresh: PropTypes.func,                 // 刷新操作
    onNextData: PropTypes.func,                // 更多数据
   }
render方法
  • 里面三个主要方法 onTouchStart、onTouchEnd、onTouchMove。分别对应的三个事件__HandlerStart、__HandlerEnd、__HandlerMove。
  render() {
    const { RefreshComplete, NextDataComplete } = this.props;
    return (
      <div ref="divXtnScroll" className={styles.refreshCss} style={this.__GetStyle()}
        onTouchStart={this.__HandlerStart.bind(this)}
        onTouchEnd={this.__HandlerEnd.bind(this)}
        onTouchMove={this.__HandlerMove.bind(this)}
        >
        {
          !LoadType ?
            <div className={styles.refreshAnimation + ' ' + (RefreshComplete ? styles.hideRefresh : styles.showRefresh)}>
              <div></div>
            </div>
            :
            <div ref="divOtherAni" className={styles.otherAni}>
              <div>
                <div ref="divLoading"></div>
                <div ref="divOtherAniDesc"></div>
              </div>
            </div>
        }
        <div className={styles.content} ref="divContent">
          {this.props.children}
        </div>
        <div
          className={styles.loadingMoreDataAnimation + ' ' + (NextDataComplete ? styles.hideLoadMore : styles.showLoadMore)}>
          <div className={styles.spinner}>
            <div className={styles.bounce1}></div>
            <div className={styles.bounce2}></div>
            <div className={styles.bounce3}></div>
          </div>
        </div>
      </div>
    );
  }
__HandlerStart 开始移动方法
  • 这主要是记录当前的位置信息
  /**
   * 开始移动
   * 
   * @param {any} event
   * 
   * @memberOf Refresh
   */
  __HandlerStart(event) {
    const { clientX, clientY } = event.touches[0];
    this.state.startX = clientX;
    this.state.startY = clientY;
  }
__HandlerMove 开始移动操作。
  __HandlerMove(event) {
    this.__ProcessAniStart(event);
    // android 版本小 5的话,是没有 end事件的。只有start,和move事件。
    const { version } = Utility.$androidVersion() || {};
    if (!version) {
      return;
    }
    if (version > '5.0') {
      return;
    }
    this.__HandlerEnd(event);
  }
  • __ProcessAniStart 主要是用于判断向下拉显示相应的字信息。
  __ProcessAniStart(event) {
    const { LoadType, RefreshComplete, NextDataComplete } = this.props;
    if (!LoadType || !RefreshComplete || !NextDataComplete) {
      return;
    }
    const { clientY } = event.changedTouches[0];
    this.state.moveY = clientY;
    const { startY } = this.state;
    const abs = clientY - startY;
    const { divContent, divOtherAni, divOtherAniDesc, divLoading } = this.refs;
    const __MaxValue = 200;
    if (abs > 0) {
      divLoading.classList.remove(styles.loading);
      divOtherAni.classList.remove(styles.hide);
      if (abs <= __MaxValue) {
        const __3d = 'translate3d(0px, ' + abs + 'px, 0px)';
        divContent.style.transform = __3d;
        divContent.style.webkitTransform = __3d;
        divOtherAni.style.transform = __3d;
        divOtherAni.style.webkitTransform = __3d;
        divOtherAniDesc.innerHTML = '下拉刷新';
      }
      if (abs > __MaxValue) {
        divOtherAniDesc.innerHTML = '释放立即更新';
      }
    }
  }
__HandlerEnd 移动结束事件
  • 移动结束这个时候要判断移动的方向,向上或下拉,还是向左右滑动。判断好相应的方法后,通知父级组件,调用相应的方法了。

  /**
   * 移动结束
   * 
   * @param {any} event
   * @returns
   * 
   * @memberOf Refresh
   */
  __HandlerEnd(event) {
    this.__ProcessSlideEnd();
    const { RefreshComplete, NextDataComplete } = this.props;
    if (!RefreshComplete || !NextDataComplete) {
      return;
    }
    const { startX, startY } = this.state;
    const { clientY, clientX } = event.changedTouches[0];
    const xes = clientX - startX;
    const yes = clientY - startY;
    const absXes = Math.abs(xes);
    const absYes = Math.abs(yes);
    if (absXes < 10 && absYes < 10) {
      return;
    }
    this.__ProcessAniEnd();
    if (xes > 0) { // 右
      if (yes > 0) {// 向下
        // 判断主向
        if (absXes > absYes) {// 向右。
          this.__HandlerSlideRight();
        } else {// 向下。
          this.__HandlerRefresh();
        }
      } else {// 向上
        if (absXes > absYes) {// 向右。
          this.__HandlerSlideRight();
        } else {// 向上。
          this.__HandlerNextData();
        }
      }
    } else {// 左边
      if (yes > 0) {// 向下
        if (absXes > absYes) {// 向左。
          this.__HandlerSlideLeft();
        } else {// 向下。
          this.__HandlerRefresh();
        }
      } else {// 向上
        if (absXes > absYes) {
          this.__HandlerSlideLeft();
        } else {
          this.__HandlerNextData();
        }
      }
    }
  }
  • __HandlerNextData 事件,判断是否要通知父级组件调用获取下一页数据的接口。

  /**
   * 下一页数据
   * 
   * @returns
   * 
   * @memberOf Refresh
   */
  __HandlerNextData() {
    const { onNextData, NextDataComplete } = this.props;
    if (!Utility.isFunction(onNextData) || NextDataComplete === false) {
      return;
    }
    const Percentage = this.props.Percentage || 1;
    const __bodyScrollTop = document.body.scrollTop || document.documentElement.scrollTop || window.pageYOffset;
    const __bodyScrollHeight = document.body.scrollHeight || document.documentElement.scrollHeight;
    const __differValue = ((__bodyScrollHeight - __bodyScrollTop - screen.height) / __bodyScrollHeight) * 100;
    if (__differValue > (Percentage < 2 ? 2 : Percentage)) {
      return;
    }
    onNextData();
  }

. __HandlerRefresh 判断是否要告诉父级组件要刷新数据操作

  /**
   * 刷新
   * 
   * @returns
   * 
   * @memberOf Refresh
   */
  __HandlerRefresh() {
    const divXtnScroll = this.refs.divXtnScroll;
    const body = document.body;
    const __differenceValue = body.scrollHeight - divXtnScroll.scrollHeight;
    const __bodyScrollTop = body.scrollTop;
    if (__bodyScrollTop > __differenceValue) {
      return;
    }
    const { onRefresh, RefreshComplete } = this.props;
    if (!Utility.isFunction(onRefresh) || RefreshComplete === false) {
      return;
    }
    onRefresh();
  }

核心代码基本上就这些了,想看完整的代码,可以到 Github上查看。也可以将项目下载下来,然后看运行效果了。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值