文档章节

xmlplus 组件设计系列之六 - 下拉刷新(PullRefresh)

qudou
 qudou
发布于 2017/04/27 22:16
字数 1915
阅读 22
收藏 0
点赞 0
评论 0

下拉刷新

“下拉刷新”由著名设计师 Loren Brichter 设计,并应用于 Twitter 第三方应用 Tweetie 中。2010年4月,Twitter 收购 Tweetie 开发商 Atebits 后,该专利归 Twitter 所有。这一章我们就来看看如何实现一个简单的下拉刷新组件。

<img src="http://xmlplus.cn/img/pullrefresh.png" class="img-responsive"/>

目标组件分析

和前面在设计组件时的做法一样,我们先想想看最终的成品组件是如何使用的,这需要点想像力。下拉刷新组件看成一个容器组件是合理的,用户可以对容器的内容进行下拉操作。如果用户完成了完整的下拉触发操作,该组件应该会有下拉完成的事件反馈,假定这个事件名为 ready。根据以上的分析,我们很有可能得到下面的一个该组件的应用示例。

Index: {
    xml: `<PullRefresh id='example'>
             <h1>Twitter</h1>
             <h2>Loren Brichter</h2>
          </PullRefresh>`,
    fun: function (sys, items, opts) {
        sys.example.on("ready", () => console.log("ready"));
    }
}

示例中的使用方式是非常简洁的,但我们还漏了一点。当刷新完毕,数据返回后,还要告知组件对象给出刷新成功的提示并且返回初始状态。好了,下面给出的是加入新接口的应用示例。

// 06-01
Index: {
    xml: `<PullRefresh id='example'>
             <h1>Twitter</h1>
             <h2>Loren Brichter</h2>
             <button id='refresh'>click</button>
          </PullRefresh>`,
    fun: function (sys, items, opts) {
        sys.example.on("ready", () => {
            setTimeout(() => sys.example.trigger("complete"), 3000);
        });
    }
}

该示例通过定时器模拟了下拉刷新完成后给出刷新成功的提示并且返回初始状态。

布局

现在让我们把目光转移到下拉刷新组件的内部,看看该如何去实现。观察文章开始部分的大图,很自然地我们可以将整个组件划分为三个子组件,如下面的 XML 文档所示。

<div id="refresh">
    <Status id="status"/>
    <div id="content"></div>
</div>

但为了方便控制,下面的布局可能会好一些。其中组件 page 代表视口,它与其父级 refresh 有相同的宽高尺寸。另外,内容组件 content 与视口组件 page 也具有相同的宽高尺寸。未定义的状态条组件 Status 的高度为 40px,这样在初始状态下,状态条组件与内容组件需要向上便宜 40 个像素。

// 06-01
PullRefresh: {
    css: `#refresh { position: relative; height: 100%; cursor: pointer; overflow-y: hidden; }
          #page { height: 100%; transform: translateY(0); }
          #status, #content { transform: translateY(-40px); } #content { height: 100%; }`,
    xml: `<div id='refresh' xmlns:i='pullrefresh'>
            <div id='page'>
                <i:Status id='status'/>
                <div id='content'></div>
            </div>
          </div>`,
    map: { "appendTo": "content" }
}

状态条的实现

暂且放下 PullRefresh 组件,我们先看看如何实现状态指示条。状态指示条用于显示“下拉刷新”、“松开刷新”、“加载中...”以及“刷新成功”四个状态提示,并且每一时刻仅显示一个状态。对于状态的切换,这里会先用到我们下一章将讲到的路由组件 ViewStack,这里仅需要了解如何使用即可。组件 ViewStack 对外只显示子级的一个子组件,同时侦听一个 switch 事件,该事件的派发者携带了一个切换到的目标对象的名称,也就是 ID。该组件根据这个 ID 来切换到目标视图。下面是状态条组件的完整实现。

// 06-01
Status: {
    css: "#statusbar { height: 2.5em; line-height: 2.5em; text-align: center; }",
    xml: <ViewStack id="statusbar">
            <span id="pull">Pull to refresh...</span>
            <span id="release">Release to refresh...</span>
            <span id="loading">Loading...</span>
            <span id="success">Loading success</span>
         </ViewStack>,
    fun: function (sys, items, opts) {
        var stat = "pull";
        function getValue() {
            return stat;
        }
        function setValue(value) {
            sys.statusbar.trigger("switch", stat = value);
        }
        return Object.defineProperty({}, "value", { get: getValue, set: setValue });
    }
}

该组件提供一个 value 接口用户设置与获取组件的显示状态。父级组件可根据不同的时机调用该接口。

事件响应

现在让我们来考虑下拉刷新组件操作实现的具体细节。我们需要考虑的事件主要有三个:stouchstarttouchmove 以及 touchend。下面是一个实现框架:

// 06-01
PullRefresh: {
    fun: function (sys, items, opts) {
        var startY, translateY;
        sys.page.on("touchstart", function(e) {
            // 1 记录下当前触点的坐标以及 page 的偏移
            // 2 侦听 touchmove 和 touchend事件
        });
        function touchmove(e) {
            // 1 计算出垂直方向上的偏移
            // 2 处理状态条与内容内面跟随触点移动
            // 3 根据触点移动的距离显示相当的状态条内容
        }
        function touchend(e) {
            // 1 移除 touchmove 和 touchend 事件
            // 2 根据触点移动的距离决定返回原始状态或者进入刷新状态并派发事件
        }
    }
}

现在我们一个个地来实现上面的三个侦听器。首先是 touchstart 侦听器:

// 06-01
sys.page.on("touchstart", function (e) {
    startY = e.targetTouches[0].pageY;
    translateY = parseInt(sys.page.css("transform").match(/\d+/)[0]);
    sys.page.on("touchmove", touchmove).on("touchend", touchend).css("transition", "");
});

下拉刷新过程中会涉及到动画,对于动画目前一般有两种选择,可以使用 JQuery 动画函数,也可以是 css3,这需要看各人喜好了。这里我们选择使用 css3 来实现。如上所示在下拉开始时需要把动画给禁用掉,否则会对后续造成干扰。

其次是 touchmove 侦听器。该侦听器必需判断出偏移的正负值,当偏移为正时才允许移动页面。

// 06-01
function touchmove(e) {
    var offset = e.targetTouches[0].pageY - startY;
    if ( offset > 0 ) {
        sys.page.css("transform", "translateY(" + (offset + translateY) + "px)");
        if (items.status.value != "loading")
            items.status.value = offset > 40 ? "release" : "pull";
    }
}

最后是 touchend 侦听器。该处理器需要处理三种情况。情况一,如果状态条处理等待数据返回状态,则回弹页面使状态条还处于该状态。情况二,如果用户下拉幅度未超过 40px,则回弹页面使状态条处于隐藏状态。情况三,如果用户下拉幅度超过 40px,则派发一个 ready 事件,并切换状态条至等待数据返回状态。

// 06-01
function touchend(e) {
    var offset = e.changedTouches[0].pageY - startY;
    sys.page.off("touchmove").off("touchend").css("transition", "all 0.3s ease-in 0s");
    if ( items.status.value == "release" ) {
        sys.page.css("transform", "translateY(40px)");
    } else if ( offset < 40 ) {
        sys.page.css("transform", "translateY(0)");
    } else {
        release();
    }
}

由于情况三的处理较复杂,所以独立封装成一个函数处理。请看下面的 release 函数。

// 06-01
function release() {
    items.status.value = "release";
    sys.refresh.once("complete", () => {
        items.status.value = "message";
        setTimeout(e => {
            sys.page.css("transform", "translateY(0)").once("webkitTransitionEnd", e => items.status.value = "pull");
        }, 300);
    });
    sys.page.css("transform", "translateY(40px)").trigger("ready");
}

此函数主要完成两件事,其一是派发 ready 事件,提醒上级组件发送数据请求,其二是侦听 complete 事件,一旦接收到来自上级派发的 complete 事件则显示完成数据请求的提示并返回初始状态。

状态条的改进

上面我们实现的状态条是纯文字的,这一节让我们把 加载中... 替换成一个动画,从而给用户带来更好的体验。下面实现的动画组件 Release 包含一个旋转的类似菊花一样的东西,同时还包含文本。

// 06-02
Release: {
    css: `#loader { display: inline-block; position: relative; height: 2.5em; line-height: 2.5em; }
          #spinner { width: 1.2em; height: 1.2em; position: absolute; top: .7em; }
          #label { display: inline-block; font-size: 0.75em; margin: 0 0 0 2em; }`,
    xml: `<div id='loader'>
            <Spinner id='spinner'/><span id='label'/>
          </div>`,
    map: { appendTo: "label" }
},
Spinner: {
    css: `#loader { width: 1.5em; height: 1.5em; animation: spin 1s linear infinite;... }
          @keyframes $spin { 0% {transform: rotate(0deg);} 100% {transform: rotate(360deg); } }
          @-webkit-keyframes $spin {0% {-webkit-transform: rotate(0deg);}... }`,
    xml: `<svg id='loader' width='48' height='48' viewBox='0 0 1024 1024'>
            <path d='M512.151961 3.978614l-0.308015 0c-21.655206 0-39.162952...'/>
            ...
          </svg>`
}

你只需要在状态条组件 Status 中把名为 release 的组件替换成上面新实现的 Release,其余地方不用改,示例就能很好的工作了。

// 06-02
Status: {
    css: "#statusbar { height: 2.5em; line-height: 2.5em; text-align: center; }",
    xml: `<ViewStack id='statusbar'>
            <span id='pull'>Pull to refresh...</span>
            <span id='release'>Release to refresh...</span>
            <Release id='loading'>Loading...</Release>
            <span id='success'>Loading success</span>
          </ViewStack>`,
    fun: function (sys, items, opts) {
        var status = "pull";
        function getValue() {
            return status;
        }
        function setValue(value) {
            sys.statusbar.trigger("switch", status = value);
        }
        return Object.defineProperty({}, "value", {get: getValue, set: setValue});
    }
}

© 著作权归作者所有

共有 人打赏支持
qudou

qudou

粉丝 5
博文 11
码字总数 14933
作品 2
福州
程序员
MUI重置上拉加载完美解决方案

简述:mui混合开发遇到这样的问题解决好就,一个列表进行分页加载数据下拉刷新请求前10条数据,上拉加载如果大于10条提示上拉加载更多,如果小于10条提示没有更多数据,当你上拉加载没有更多...

全村的希望iOS ⋅ 01/13 ⋅ 0

全栈 JavaScript 框架 xmlplus 1.5.9 发布

该版本主要对全局函数 clearLibrary 作了简化,另外对文档的一些文字错误进行了修正,同时保持了 gitHub 与 npm 版本之间的同步。 xmlplus 是一个设计非常独特 JavaScript 框架,用于快速开发...

qudou ⋅ 2017/04/30 ⋅ 16

全栈 JavaScript 框架--xmlplus

xmlplus 是一个设计非常独特 JavaScript 框架,用于快速开发前后端项目。 基于组件设计 在 xmlplus 中,组件是基本的构造块。评价组件设计好坏的一个重要标准是封装度。基于 xmlplus 设计的组...

qudou ⋅ 2017/04/21 ⋅ 1

xmlplus v1.5.8 正式发布 - 全栈 JavaScript 框架

全栈 JavaScript 框架 xmlplus v1.5.8 正式发布 xmlplus 是一个设计非常独特 JavaScript 框架,用于快速开发前后端项目。 基于组件设计 在 xmlplus 中,组件是基本的构造块。评价组件设计好坏...

qudou ⋅ 2017/04/27 ⋅ 2

JavaScript 框架 xmlplus 1.5.12 发布

JavaScript 框架 xmlplus 1.5.12 发布了。xmlplus 是一个设计非常独特 JavaScript 框架,用于快速开发前后端项目。 这个版本主要添加了一个全局接口 create。该函数是一个轻量的用于创建组件...

qudou ⋅ 2017/05/24 ⋅ 0

Weex中调用原生下拉刷新

Weex官方文档提供了下拉刷新和上拉刷新的内建组件和。但是功能单一且和App中原生的下拉刷新样式不一样,只能抛弃了。而前端自己写组件,效果也很难和原生保持一致。 组件在iOS的WeexSDK源码中...

HJaycee ⋅ 2017/08/15 ⋅ 0

《React-Native系列》React-Native实战系列博客汇总

从2016年7月份开始,坚持写ReactNative系列博客,记录工作中遇到的点滴。 今天把博客汇总如下: 《React-Native系列》1、初探React-Native 《React-Native系列》2、RN与native交互与数据传递...

hsbirenjie ⋅ 2016/11/07 ⋅ 0

关于 Ext.plugin.PullRefresh 没有数据后的问题

在Ext.List中使用 Ext.plugin.PullRefresh,在store没有数据的情况下,如果没有设置 emptyText ,页面空白,但是可以继续下拉尝试更新,如果设置之后,就一直显示 emptyText设置的文字,无法...

myplease ⋅ 2012/12/18 ⋅ 0

PullRefresh下拉的时候,总是先清除了目前的数据,而不是追加新的数据

在sencha touch 2 中PullRefresh下拉的时候,总是先清除了目前的数据,而不是追加新的数据,求解决思路。

罗盛力 ⋅ 2012/11/01 ⋅ 2

Android学习系列(15)--App列表之下拉刷新

Android的ListView是应用最广的一个组件,功能强大,扩展性灵活(不局限于ListView本身一个类),前面的文章有介绍分组,拖拽,3D立体,游标,圆角,而今天我们要介绍的是另外一个扩展ListVie...

chengche ⋅ 2014/01/08 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

如何将S/4HANA系统存储的图片文件用Java程序保存到本地

我在S/4HANA的事务码MM02里为Material维护图片文件作为附件: 通过如下简单的ABAP代码即可将图片文件的二进制内容读取出来: REPORT zgos_api.DATA ls_appl_object TYPE gos_s_obj.DA...

JerryWang_SAP ⋅ 33分钟前 ⋅ 0

Cube的构建过程

Cube 的构建方式有两种:全量构建和增量构建。两者的构建过程完全一样,区别在于构建时读取的数据源是全集还是子集。 Cube的构建步骤: 1.创建临时的 Hive 平表(从 Hive 读取数据)。 2.计算...

无精疯 ⋅ 37分钟前 ⋅ 0

云计算的选择悖论如何对待?

导读 人们都希望在工作和生活中有所选择。但心理学家的调查研究表明,在多种选项中进行选择并不一定会使人们更快乐,甚至不会产生更好的决策。心理学家Barry Schwartz称之为“选择悖论”。云...

问题终结者 ⋅ 41分钟前 ⋅ 0

637. Average of Levels in Binary Tree - LeetCode

Question 637. Average of Levels in Binary Tree Solution 思路:定义一个map,层数作为key,value保存每层的元素个数和所有元素的和,遍历这个树,把map里面填值,遍历结束后,再遍历这个map,把每...

yysue ⋅ 55分钟前 ⋅ 0

IDEA配置和使用

版本控制 svn IDEA版本控制工具不能使用 VCS-->Enable Version Control Integration File-->Settings-->Plugins 搜索Subversion,勾选SVN和Git插件 删除.idea文件夹重新生成项目 安装SVN客户......

bithup ⋅ 今天 ⋅ 0

Hive函数

1.函数explode (一转多) create table hive_wc(sentence string); load data local inpath '/home/hadoop/data/hive-wc.txt' into table hive_wc; 结果: hive > select * from hive_wc; ......

GordonNemo ⋅ 今天 ⋅ 0

PE格式第三讲扩展,VA,RVA,FA的概念

作者:IBinary 出处:http://www.cnblogs.com/iBinary/ 版权所有,欢迎保留原文链接进行转载:) 一丶VA概念 VA (virtual Address) 虚拟地址的意思 ,比如随便打开一个PE,找下它的虚拟地址 这边...

simpower ⋅ 今天 ⋅ 0

180623-SpringBoot之logback配置文件

SpringBoot配置logback 项目的日志配置属于比较常见的case了,之前接触和使用的都是Spring结合xml的方式,引入几个依赖,然后写个 logback.xml 配置文件即可,那么在SpringBoot中可以怎么做?...

小灰灰Blog ⋅ 今天 ⋅ 0

冒泡排序

原理:比较两个相邻的元素,将值大的元素交换至右端。 思路:依次比较相邻的两个数,将小数放在前面,大数放在后面。即在第一趟:首先比较第1个和第2个数,将小数放前,大数放后。然后比较第...

人觉非常君 ⋅ 今天 ⋅ 0

Vagrant setup

安装软件 brew cask install virtualboxbrew cask install vagrant 创建project mkdir -p mst/vmcd mst/vmvagrant init hashicorp/precise64vagrant up hashicorp/precise64是一个box......

遥借东风 ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部