美文网首页
React组建实现新闻下拉刷新加载

React组建实现新闻下拉刷新加载

作者: 朝槿123 | 来源:发表于2017-05-07 10:40 被阅读0次
新闻列表格式

整体布局:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>ListView 2</title>
    <meta name="viewport" content="width=device-width,
               user-scalable=no, initial-scale=1.0, maximum-scale=1.0, 
                          minimum-scale=1.0">
    <script src="https://cdn.bootcss.com/react/15.4.2/react.min.js"></script>
    <script src="https://cdn.bootcss.com/react/15.4.2/react-dom.min.js"></script>
    <script src="https://cdn.bootcss.com/babel-standalone/6.22.1/babel.min.js">
    </script>
    <script src="zepto.js"></script>
</head>
<body>
    <div id="app"></div>
    <script type="text/babel">
    //....
    </script>
</body>
</html>  

首先需要引入React基础库,dom库,jsx解析库和移动端Jquery库(用于动态请求异步加载数据),然后创建一个Div,引入自己的组建。
整个应用组件

var App = React.createClass({
    getInitialState: function () {
        return {
            page: 1,
            article: [],//文章列表
        };
    },
    componentDidMount: function () {//组件被加载之后,默认加载第一页数据
        this.getData(1);
    },
    onRefresh: function () {//下拉刷新函数
        this.setState({page: 1});
        this.getData(this.state.page);
    },
    onLoadMore: function () {//加载更多函数
        var page = this.state.page + 1;
        console.log(page)
        this.setState({page: page});
        this.getData(page);
    },
    getItem: function (article) {
        return <Item article={article}/>;
    },
    getData: function (page) {//获取数据的函数
        var self = this;
        $.ajax({
            url: "http://wangyi.butterfly.mopaasapp.com/news/api?
                  type=travel&page=" + page + "&limit=5",
            type: 'GET',
            success: function (data) {
                // 4.对data进行处理,并进行对应的dom渲染
                if (page == 1) {//如果是第一页,直接覆盖之前的数据
                    self.setState({article: data.list})
                  //父组件的setState  改变的自己的状态的同时触发了自组件
                      的componentWillReceiveProps
                   // 子组件可以在componentWillReceiveProps里接受新的参
                      数,改变自己的state会自动触发render渲染
                } else {
                    self.setState({//否则累加数组
                        article: self.state.article.concat(data.list)
                    })
                }
            },
            error: function () {
                // 4.错误处理
            }
        })
    },
    render: function () {
        return (
            <ListView
               onRefresh={this.onRefresh}  //从外面传进去的下拉刷新回调函数
               onLoadMore={this.onLoadMore}//从外面传进去的加载更过回调函数
               article={this.state.article}//从外面传进去的文章列表数据数组
               getItem={this.getItem} //从外面传进去的获取列表子项的回调函数
                />
        );
    }
});

解析:
1、首先对于组建进行初始化状态设置,当组建被加载后,默认加载第一页数据;
2、当进行下拉刷新时,设置状态为第一页并获取第一页数据;
3、当上拉加载更多时,状态为下一页,并获取下一页的数据。
通过Ajax获取新闻数据,对Data进行相应的处理,并进行对应的dom渲染。
** 渲染整个app**

  ReactDOM.render(
        <App />,
    document.getElementById('app')
);

** 静态常量**

var XLJZ = '下拉加载';
var SKJZ = '松开加载';
var JZ = '加载中...'
var dropDownRefreshText = XLJZ;
var dragValve = 40; // 下拉加载阀值
var scrollValve = 40; // 滚动加载阀值

子列表项组件,只负责渲染外面传递给他的数据(css设计样式)

var Item = React.createClass({
    render: function () {
        return (<li className="left_four_ul_li">
                    <img src={this.props.article.imgurl}/>
                    <div className="left_four_ul_li_para">
                        <h1>{this.props.article.title}</h1>
                        <p>{this.props.article.time}</p>
                        <span>{this.props.article.docurl}</span>
                    </div>
                </li>
        )
    }
});

** 列表组件**

var ListView = React.createClass({
    getInitialState: function () {//初始化状态
        return {
            translate: 0,//位移
            dragLoading: false,//是否在下拉刷新中
            scrollerLoading: false,//是否在加载更多中
            openDragLoading: true,//是否开启下拉刷新
            openScrollLoading: true,//是否开启下拉刷新
            data: []//默认的列表空数据
        };
    },
    componentDidMount: function () {//组建加载完毕,进行初始化赋值
        this.setState(
            {
                translate: 0,
                openDragLoading: this.props.openDragLoading || true,//根据外面设置的开关改变自己的状态
                openScrollLoading: this.props.openScrollLoading || true
            }
        );

        this.initRefresh();//初始化下拉刷新
        this.initScroll();//初始化滚动加载更多
    },
    initRefresh: function (defaults, options) {
        var self = this;//对象转存,防止闭包函数内无法访问
        var isTouchStart = false; // 是否已经触发下拉条件
        var isDragStart = false; // 是否已经开始下拉
        var startX, startY;        // 下拉方向,touchstart 时的点坐标
        var hasTouch = 'ontouchstart' in window;//判断是否是在移动端手机上
        // 监听下拉加载,兼容电脑端
        if (self.state.openDragLoading) {
            self.refs.scroller.addEventListener('touchstart', touchStart, false);
            self.refs.scroller.addEventListener('touchmove', touchMove, false);
            self.refs.scroller.addEventListener('touchend', touchEnd, false);
            self.refs.scroller.addEventListener('mousedown', touchStart, false);
            self.refs.scroller.addEventListener('mousemove', touchMove, false);
            self.refs.scroller.addEventListener('mouseup', touchEnd, false);
        }
        function touchStart(event) {
            if (self.refs.scroller.scrollTop <= 0) {
                isTouchStart = true;
                startY = hasTouch ? event.changedTouches[0].pageY : event.pageY;
                startX = hasTouch ? event.changedTouches[0].pageX : event.pageX;
            }
        }

        function touchMove(event) {
            if (!isTouchStart) return;
            var distanceY = (hasTouch ? event.changedTouches[0].pageY : event.pageY) - startY;
            var distanceX = (hasTouch ? event.changedTouches[0].pageX : event.pageX) - startX;
            //如果X方向上的位移大于Y方向,则认为是左右滑动
            if (Math.abs(distanceX) > Math.abs(distanceY))return;
            if (distanceY > 0) {
                self.setState({
                    translate: Math.pow((hasTouch ? event.changedTouches[0].pageY : event.pageY) - startY, 0.85)
                });
            } else {
                if (self.state.translate !== 0) {
                    self.setState({translate: 0});
                    self.transformScroller(0, self.state.translate);
                }
            }

            if (distanceY > 0) {
                if (!isDragStart) {
                    isDragStart = true;
                }
                if (self.state.translate <= dragValve) {// 下拉中,但还没到刷新阀值
                    if (dropDownRefreshText !== XLJZ)
                        self.refs.dropDownRefreshText.innerHTML = (dropDownRefreshText = XLJZ);
                } else { // 下拉中,已经达到刷新阀值
                    if (dropDownRefreshText !== SKJZ)
                        self.refs.dropDownRefreshText.innerHTML = (dropDownRefreshText = SKJZ);
                }
                self.transformScroller(0, self.state.translate);
            }
        }
        function touchEnd(event) {
            isDragStart = false;
            if (!isTouchStart) return;
            isTouchStart = false;
            if (self.state.translate <= dragValve) {
                self.transformScroller(0.3, 0);
            } else {
                self.setState({dragLoading: true});//设置在下拉刷新状态中
                self.transformScroller(0.1, dragValve);
                self.refs.dropDownRefreshText.innerHTML = (dropDownRefreshText = JZ);
                self.props.onRefresh();//触发冲外面传进来的刷新回调函数
            }
        }
    },
    initScroll: function () {
        var self = this;
        // 监听滚动加载
        if (this.state.openScrollLoading) {
            this.refs.scroller.addEventListener('scroll', scrolling, false);
        }

        function scrolling() {
            if (self.state.scrollerLoading) return;
            var scrollerscrollHeight = self.refs.scroller.scrollHeight; // 容器滚动总高度
            var scrollerHeight = self.refs.scroller.getBoundingClientRect().height;// 容器滚动可见高度
            var scrollerTop = self.refs.scroller.scrollTop;//滚过的高度
            // 达到滚动加载阀值
            if (scrollerscrollHeight - scrollerHeight - scrollerTop <= scrollValve) {
                self.setState({scrollerLoading: true});
                self.props.onLoadMore();
            }
        }
    },
    /**
     * 利用 transition 和transform  改变位移
     * @param time 时间
     * @param translate  距离
     */
    transformScroller: function (time, translate) {
        this.setState({translate: translate});
        var elStyle = this.refs.scroller.style;
        elStyle.webkitTransition = elStyle.MozTransition = elStyle.transition = 'all ' + time + 's ease-in-out';
        elStyle.webkitTransform = elStyle.MozTransform = elStyle.transform = 'translate3d(0, ' + translate + 'px, 0)';
    },
    /**
     * 下拉刷新完毕
     */
    dragLoadingDone: function () {
        this.setState({dragLoading: false});
        this.transformScroller(0.1, 0);
    },
    /**
     * 滚动加载完毕
     */
    scrollLoadingDone: function () {
        this.setState({scrollerLoading: false});
        this.refs.dropDownRefreshText.innerHTML = (dropDownRefreshText = XLJZ);
    },
    /**
     * 当有新的属性需要更新时。也就是网络数据回来之后
     * @param nextProps
     */
    componentWillReceiveProps: function (nextProps) {
        var self = this;
        this.setState({data: nextProps.article,});//把新的数据填进列表
        if (this.state.dragLoading) {//如果之前是下拉刷新状态,恢复
            setTimeout(function () {
                self.dragLoadingDone();
            }, 1000);
        }
        if (this.state.scrollerLoading) {//如果之前是滚动加载状态,恢复
            setTimeout(function () {
                self.scrollLoadingDone();
            }, 1000);
        }
    },
    render: function () {
        var self = this;
        return (
                <div className="scroller" ref="scroller">
                    <div className="drop-down-refresh-layer">
                        <p className="drop-down-refresh-text" ref="dropDownRefreshText">下拉加载</p>
                    </div>
                    <div className="scroller-content">
                        <div className="content">
                            <ul className="left_four_ul" id="list">
                                {self.state.data.map(function (item) {//通过map循环把子列表数据展示出来
                                    return self.props.getItem(item);
                                })
                                }
                            </ul>
                        </div>
                        <div className="scroll-loading">加载中...</div>
                    </div>
                </div>
        );
    }
});

列表组建下拉刷新解析:
1、通过refs找到滚动的容器scroller,给它添加监听事件,为了兼容电脑端和移动端,需要监听触摸事件和鼠标事件;
2、当触摸开始或鼠标按下时,回调touchstart函数,判断是否滚动到容器顶端,如果滚动到顶端,再判断是否是手机触摸事件,是就记录第一个触摸点的X,Y值,不是就记录电脑鼠标按下的位置;
3、当触摸移动或鼠标移动时,回调touchMove函数,判断是否是触摸状态,同时记录下触摸移动的距离(如果X方向上的位移大于Y方向,则认为是左右滑动并返回):

  • 判断Y方向的位移是否大于0,如果大于0,滚动容器的位移取触摸位移的0.85次方,当触摸位移过大时,容器的位移到达一定值就不再跟随;
  • 当Y方向的位移大于0时,确定开始拖拽;滚动容器在下拉中,但还没到刷新阀值时,显示“下拉加载”;已经达到刷新阀值,显示“松开加载”;

4、当触摸结束或鼠标抬起时,回调touchEnd函数。若滚动容器在下拉中,但还没到刷新阀值,经过0.3S位移回到0;若已经达到刷新阀值,经过0.1s位移为刷新阀值,显示“加载”,并触发冲外面传进来的刷新回调函数;
列表组建加载更多解析:
1、监听滚动加载:当滚动容器滚动时,回调滚动加载函数;
2、如果是滚动加载状态则返回;
3、当容器滚动总高度- 容器滚动可见高度-滚过的高度小于滚动加载阀值时,设置滚动加载状态,触发从外面传进来的加载更多回调函数。
列表下拉跟随解析:
transformScroller(time, translate)传入两个参数:时间和距离;
利用 transition 和transform 改变位移,transition 属性设置 'all ' + time + 's ease-in-out'表示过渡阶段慢快慢;
transform 属性设置'translate3d(0, ' + translate + 'px, 0)'位移过程更流畅;

当有新的属性需要更新时,也就是网络数据回来之后,把新的数据填进列表;如果之前是下拉刷新状态,恢复;如果之前是滚动加载状态,恢复。
最后渲染列表组建,通过map循环把子列表数据展示出来。
效果图如下:


下拉加载
松开加载
刷新加载中
上滑加载更多

相关文章

网友评论

      本文标题:React组建实现新闻下拉刷新加载

      本文链接:https://www.haomeiwen.com/subject/wviozttx.html