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


Posted in Javascript onMay 03, 2017

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

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

目标组件分析

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

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

示例中的使用方式是非常简洁的,但我们还漏了一点。如果你用过一些新闻客户端,在某些情况下,此客户端会自动触发下拉刷新操作。比如,刚进入客户端页面或者由于软件推送机制产生的被动列表更新,这都将导致客户端下拉刷新操作的触发。所以如上的 PullRefresh 组件还应该提供一个触发自动刷新的操作接口。好了,下面是加入下拉刷新接口的应用示例。

Example2: {
 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", () => console.log("ready"));
  sys.refresh.on("click", items.example.refresh);
 }
}

基本框架

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

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

外围 div 元素包含两个子组件:其中一个是状态指示条,用于显示“下拉刷新”、“松开刷新”、“加载中...”以及“刷新成功”四个状态提示,这里暂时使用未定义的 Status 组件替代;另一个 div 元素用于容纳下拉刷新组件的包含内容。到现在,大概可以想得出该组件的工作逻辑了,于是我们可以给出下面的一个基本的组件框架。

PullRefresh: {
 css: "#refresh { position: relative; height: 100%;...}",
 xml: `<div id="refresh">
   <Status id="status"/>
   <div id="content"/>
   </div>`,
 map: { appendTo: "content" },
 fun: function (sys, items, opts) {
  sys.content.on("touchstart", e => {
   // 侦听 touchmove 和 touchend事件
  });
  function touchmove(e) {
   // 1 处理状态条与内容内面跟随触点移动
   // 2 根据触点移动的距离显示相当的状态条内容
  }
  function touchend(e) {
   // 1 移除 touchmove 和 touchend 事件
   // 2 根据触点移动的距离决定返回原始状态或者进入刷新状态并派发事件
  }
 }
}

状态条的实现

如前面提到的,状态条组件包含四个状态提示,并且每一时刻仅显示一个状态。对于状态的切换,这里会先用到我们下一章将讲到的路由组件 ViewStack,这里仅需要了解如何使用即可。组件 ViewStack 对外只显示子级的一个子组件,同时侦听一个 switch 事件,该事件的派发者携带了一个切换到的目标对象的名称,也就是 ID。该组件根据这个 ID 来切换到目标视图。下面是状态条组件的完整实现。

Status: {
 css: "#statusbar { height: 2.5em; line-height: 2.5em; text-align: center; }",
 xml: <ViewStack id="statusbar">
   <span id="pull">下拉刷新</span>
   <span id="ready">松开刷新</span>
   <span id="loading">加载中...</span>
   <span id="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 接口用户设置与获取组件的显示状态。父级组件可根据不同的时机调用该接口。

最终实现

有了上面的储备,让我们来填充完下拉刷新组件的细节。下拉刷新过程中会涉及到动画,对于动画目前一般有两种选择,可以使用 JQuery 动画函数,也可以是 css3,这需要看各人喜好了。这里我们选择使用 css3 来实现。为清晰起见,下面的实现仅给出函数部分,其余部分同上。

PullRefresh: {
 fun: function (sys, items, opts) {
  var startY, height = sys.status.height();
  sys.content.on("stouchstart", e => {
   if (items.status.value == "pull") {
    startY = e.y;
    sys.content.on("touchmove", touchmove).on("touchend", touchend);
    sys.content.css("transition", "").prev().css("transition", "");
   }
  });
  function touchmove(e) {
   var offset = e.y - startY;
   if ( offset > 0 ) {
    sys.content.css("top", offset + "px"); 
    sys.status.css("top", (offset - height) + "px");
    items.status(offset > height ? "ready" : "pull");
   }
  }
  function touchend (e) {
   var offset = e.y - startY;
   sys.content.off("touchmove").off("touchend");
   sys.content.css("transition", "all 0.3s ease-in 0s").prev().css("transition", "all 0.3s ease-in 0s");
   if ( offset < height ) {
    sys.content.css("top", "0").prev().css("top", -height + "px");
   } else {
    items.status.value = "release";
    sys.refresh.once("complete", complete);
    sys.content.css("top", height + "px").prev().css("top", "0").trigger("ready");
   }
  }
  function complete() {
   items.status.value = "message";
   setTimeout(() => {
    sys.content.css("top", "0").prev().css("top", -height + "px");
    sys.content.once("webkitTransitionEnd", e => items.status.value = "pull");
   }, 300);
  }
 }
}

对于稍微有点复杂的组件,需要注意组件的组织归类,尽量把具有相近功能的组件放在一起。为了便于叙述,上述所列出的组件示意总把它们视作是同一目录,这一点读者应该能看出来。

本系列文章基于 xmlplus 框架。如果你对 xmlplus 没有多少了解,可以访问 www.xmlplus.cn。这里有详尽的入门文档可供参考。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
jquery键盘事件使用介绍
Nov 01 Javascript
javascript轻量级模板引擎juicer使用指南
Jun 22 Javascript
安装使用Mongoose配合Node.js操作MongoDB的基础教程
Mar 01 Javascript
jquery点击切换背景色的简单实例
Aug 25 Javascript
jstree创建无限分级树的方法【基于ajax动态创建子节点】
Oct 25 Javascript
JS实现淡入淡出图片效果的方法分析
Dec 20 Javascript
jacascript DOM节点——元素节点、属性节点、文本节点
Apr 18 Javascript
详解Vue组件之间的数据通信实例
Jun 17 Javascript
MVVM 双向绑定的实现代码
Jun 21 Javascript
深入理解Node内建模块和对象
Mar 12 Javascript
Vue中UI组件库之Vuex与虚拟服务器初识
May 07 Javascript
用Node写一条配置环境的指令
Nov 14 Javascript
javascript 判断一个对象为数组的方法
May 03 #Javascript
深入理解node.js之path模块
May 03 #Javascript
判断颜色是否合法的正则表达式(详解)
May 03 #Javascript
Vue input控件通过value绑定动态属性及修饰符的方法
May 03 #Javascript
ES6中参数的默认值语法介绍
May 03 #Javascript
Express之get,pos请求参数的获取
May 02 #Javascript
利用prop-types第三方库对组件的props中的变量进行类型检测
May 02 #Javascript
You might like
国产动画《伍六七》原声大碟大卖,啊哈娱乐引领音乐赋能IP的新尝试
2020/03/08 国漫
浅析PHP中Collection 类的设计
2013/06/21 PHP
一个php短网址的生成代码(仿微博短网址)
2014/05/07 PHP
PHP获取文件的MD5值并判断是否被修改的例子
2014/06/19 PHP
详解PHP的Yii框架中自带的前端资源包的使用
2016/03/31 PHP
PHP使用Redis实现防止大并发下二次写入的方法
2017/10/09 PHP
php微信公众号开发之音乐信息
2018/10/20 PHP
基于laravel Request的所有方法详解
2019/09/29 PHP
js中获取事件对象的方法小结
2011/03/13 Javascript
Jsonp 跨域的原理以及Jquery的解决方案
2011/06/27 Javascript
javascript原型链继承用法实例分析
2015/01/28 Javascript
Javascript实现快速排序(Quicksort)的算法详解
2015/09/06 Javascript
jQuery插件ajaxfileupload.js实现上传文件
2020/10/23 Javascript
Angularjs的ng-repeat中去除重复数据的方法
2016/08/05 Javascript
原生js实现吸顶效果
2017/03/13 Javascript
图解Javascript——作用域、作用域链、闭包
2017/03/21 Javascript
Vue2.0表单校验组件vee-validate的使用详解
2017/05/02 Javascript
Vue的移动端多图上传插件vue-easy-uploader的示例代码
2017/11/27 Javascript
bootstrap3中container与container_fluid外层容器的区别讲解
2017/12/04 Javascript
关于Vue组件库开发详析
2018/07/01 Javascript
vue中的自定义分页插件组件的示例
2018/08/18 Javascript
详解vue中axios请求的封装
2019/04/08 Javascript
使用preload预加载页面资源时注意事项
2020/02/03 Javascript
JavaScript获取时区实现过程解析
2020/09/24 Javascript
python中OrderedDict的使用方法详解
2017/05/05 Python
Python爬虫框架scrapy实现的文件下载功能示例
2018/08/04 Python
解决flask接口返回的内容中文乱码的问题
2020/04/03 Python
巴西家用小家电购物网站:Polishop
2016/08/07 全球购物
巴西最大的体育用品商城:Netshoes巴西
2016/11/29 全球购物
加拿大奢华时装品牌:Mackage
2018/01/10 全球购物
美国领先的医疗警报服务:Philips Lifeline
2018/03/12 全球购物
物流经理自我评价
2013/09/23 职场文书
致跳远、跳高运动员广播稿
2014/01/09 职场文书
2014年平安夜寄语
2014/12/08 职场文书
2015年外贸业务员工作总结范文
2015/05/23 职场文书
2015年物流客服工作总结
2015/07/27 职场文书