IScroll那些事_当内容不足时下拉刷新的解决方法


Posted in Javascript onJuly 18, 2017

之前项目中的列表是采用的IScroll,但是在使用IScroll有一个问题就是:当内容不足全屏的时候,是木有办法往下拉的,这样就达不到刷新的目的了。【这是本人工作中遇到的,具体例子具体分析,这里只作一个参考】

大致的例子是这样的:

<style>
 * {
 margin: 0;
 padding: 0;
 }
 html,body,.container {
 width: 100%;
 height: 100%;
 }
 .container>ul>li {
 padding: 15px 20px;
 text-align: center;
 border-bottom: 1px solid #ccc;
 }
</style>

<div id="container" class="container">
 <ul class="scroller">
 <li>item1</li>
 <li>item2</li>
 <li>item3</li>
 <li>item4</li>
 <li>item5</li>
 </ul>
</div>

<script src="https://cdn.bootcss.com/iScroll/5.2.0/iscroll.min.js"></script>
<script>
 var myScroll = null;
 function onLoad() {
 myScroll = new IScroll('container');
 }
 window.addEventListener('DOMContentLoaded', onLoad, false);
</script>

那么,既然超过一屏是可以刷新的,那我们就来逛逛代码吧。在github上搜索iscroll,打开第一个,找到src下面的core.js。

1. 思路

首先既然要下拉,肯定会触发touchstart、touchmove以及touchend事件。搜索touchmove,很好,在_initEvents中的注册了这个事件。

_initEvents: function (remove) {
 // ...
 // 这里省略若干代码

 if ( utils.hasTouch && !this.options.disableTouch ) {
  eventType(this.wrapper, 'touchstart', this);
  eventType(target, 'touchmove', this);
  eventType(target, 'touchcancel', this);
  eventType(target, 'touchend', this);
 }

 // ...
},

好吧,看到这里的时候,我表示懵了一下逼,这不就是个绑定事件么?this又是一个什么鬼,然后我去查了一下文档,发现了这么一个东西。文档地址

target.addEventListener(type, listener[, options]);
target.addEventListener(type, listener[, useCapture]);
target.addEventListener(type, listener[, useCapture, wantsUntrusted ]); 
// 
// Gecko/Mozilla only

listener

当所监听的事件类型触发时,会接收到一个事件通知(实现了 Event 接口的对象)对象。listener 必须是一个实现了 EventListener 接口的对象,或者是一个函数

木有看错,listener是一个对象或者是一个函数。前提是这个对象实现了EventListener接口。我们接着往下看,发现了这么一个例子。

var Something = function(element) {
 // |this| is a newly created object
 this.name = 'Something Good';
 this.handleEvent = function(event) {
 console.log(this.name); 
 // 'Something Good', as this is bound to newly created object
 switch(event.type) {
  case 'click':
  // some code here...
  break;
  case 'dblclick':
  // some code here...
  break;
 }
 };

 // Note that the listeners in this case are |this|, not this.handleEvent
 element.addEventListener('click', this, false);
 element.addEventListener('dblclick', this, false);

 // You can properly remove the listeners
 element.removeEventListener('click', this, false);
 element.removeEventListener('dblclick', this, false);
}
var s = new Something(document.body);

然后在去IScroll的源码去找,发现了同样的实现方式。在default文件夹中有一个handleEvent.js。

好了,这个梗先告一段落。还是继续看源码。在handleEvent.js中,有这么一段东西。

handleEvent: function (e) {
 switch ( e.type ) {
  case 'touchstart':
  case 'pointerdown':
  case 'MSPointerDown':
  case 'mousedown':
  this._start(e);
  break;
  case 'touchmove':
  case 'pointermove':
  case 'MSPointerMove':
  case 'mousemove':
  this._move(e);
  break;
  case 'touchend':
  case 'pointerup':
  case 'MSPointerUp':
  case 'mouseup':
  case 'touchcancel':
  case 'pointercancel':
  case 'MSPointerCancel':
  case 'mousecancel':
  this._end(e);
  break;
  // ...
 }
 }
};

发现在start/move/end分别调用了内部方法_start/_move/_end方法。去看看这三个方法,看其中可能会引起不会滑动的点。

在_start方法中,看到这样的几行代码,会不会是直接返回了呢?分析分析:

if ( !this.enabled || (this.initiated && utils.eventType[e.type] !== this.initiated) {
 return;
}

// ...

var point = e.touches ? e.touches[0] : e,
 pos;

this.initiated = utils.eventType[e.type];
this.moved = false;
initiated属性在最开始肯定是没有的,而enabled默认是true,所以在最开始执行这个方法的时候是不会返回的,而是会给initiated这个属性设置当前的eventType值,这个值会在_move方法中用到。重点来看看_move方法。

if ( !this.enabled || utils.eventType[e.type] !== this.initiated ) {
 return;
}

首先来进行类型判断,因为在_start方法中已经定义了这个值,所以这里也不会返回。接着往下看:

if ( timestamp - this.endTime > 300 && (absDistX < 10 && absDistY < 10) ) {
 return;
}

【实际上是两次click事件的模拟】如果两次滑动的时间大于了300ms,并且只要一个方向上的位移少于10像素,那么也是会返回的。那么会不会呢,打个断点测试一下就知道了。这里就不贴图了,实际中的测试结果是,每一次移动肯定是在300ms以内的,这里之所以判断300ms,主要是click事件执行会有一个300ms的延迟。而每一次移动,由于手指的触点比较大,还是会大于10像素的,即使两次不大于10像素,也是不影响的。所以这点不会返回。那么继续接着看:

// If you are scrolling in one direction lock the other
if ( !this.directionLocked && !this.options.freeScroll ) {
 if ( absDistX > absDistY + this.options.directionLockThreshold ) {
 this.directionLocked = 'h'; // lock horizontally
 } else if ( absDistY >= absDistX + this.options.directionLockThreshold ) {
 this.directionLocked = 'v'; // lock vertically
 } else {
 this.directionLocked = 'n'; // no lock
 }
}

if ( this.directionLocked == 'h' ) {
 if ( this.options.eventPassthrough == 'vertical' ) {
 e.preventDefault();
 } else if ( this.options.eventPassthrough == 'horizontal' ) {
 this.initiated = false;
 return;
 }

 deltaY = 0;
} else if ( this.directionLocked == 'v' ) {
 if ( this.options.eventPassthrough == 'horizontal' ) {
 e.preventDefault();
 } else if ( this.options.eventPassthrough == 'vertical' ) {
 this.initiated = false;
 return;
 }

 deltaX = 0;
}

第一个条件判断只要是定义了这次滑动的方向是什么。h表示水平方向,v表示竖直方向。我们是要向下滑动,所以我们关注的是竖直方向。看第二个条件判断,如果是竖直方向,那么将水平方向的deltaX值变为0。这样做的目的是保持绝对的竖直方向。因为移动实际还是根据元素的位移值来的。当probe的版本为2以下的时候,是根据css3的transform属性来移动位移的,为3版本的时候是根据决定对位来移动的。所以这里只要不把我们的deltaY置为0就说明木有什么问题。继续往下看代码:

deltaX = this.hasHorizontalScroll ? deltaX : 0;
deltaY = this.hasVerticalScroll ? deltaY : 0;

newX = this.x + deltaX;
newY = this.y + deltaY;
// ...

// 这里是移动
this._translate(newX, newY);

测试中发现,这个hasVerticalScroll一直是false,那么deltaY一直就是0,也就是移动了也白移动。找到问题原因。那么,这个hasVerticalScroll是从哪里来的?全局找呀找,在refresh中找到这样几行代码:

this.wrapperWidth = this.wrapper.clientWidth;
this.wrapperHeight = this.wrapper.clientHeight;

var rect = utils.getRect(this.scroller);
/* REPLACE START: refresh */

this.scrollerWidth = rect.width;
this.scrollerHeight = rect.height;

this.maxScrollX = this.wrapperWidth - this.scrollerWidth;
this.maxScrollY = this.wrapperHeight - this.scrollerHeight;

/* REPLACE END: refresh */

this.hasHorizontalScroll = this.options.scrollX && this.maxScrollX < 0;
this.hasVerticalScroll = this.options.scrollY && this.maxScrollY < 0;

refresh方法会在IScroll实例化的时候调用一次。粗略一看,scrollY内置为true,所以只有maxScrollY会大于0。往上看。this.wrapperHeight - this.scrollerHeight肯定是大于0的呀,这就是问题所在。

那么看看我们最开始代码,这里的wrapperHeight为文档高度,scrollerHeight为内容高度,所以wrapperHeight高度始终大于scrollHeight。但是,手机端页面夹杂的列表,一般都有头部、底部,而中间部分一般都会采用padding的形式来使得列表在全局滚动,这样就不需要每次都要特定地计算列表的高度。

2. 解决方案

针对以上问题,只要我们能够使内部的滚动部分高度大于容器高度,那么就能触发滚动。

2.1 粗略做法

可以设置一个min-height属性为900px(900只是一个示例,只要够大就可以),这样就可以保证可以滑动。

2.2 精准做法

计算当前的容器高度,然后比容器高度多一个像素即可。

以上这篇IScroll那些事_当内容不足时下拉刷新的解决方法就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
JQuery SELECT单选模拟jQuery.select.js
Nov 12 Javascript
图片轮换效果实现代码(点击按钮停止执行)
Apr 12 Javascript
JavaScript中访问节点对象的方法有哪些如何使用
Sep 24 Javascript
JQuery实现当鼠标停留在某区域3秒后自动执行
Sep 09 Javascript
TypeError document.getElementById(...) is null错误原因
May 18 Javascript
Svg.js实例教程及使用手册详解(一)
May 16 Javascript
JS集成fckeditor及判断内容是否为空的方法
May 27 Javascript
微信小程序实现登录遮罩效果
Nov 01 Javascript
一些可能会用到的Node.js面试题
Jun 15 Javascript
微信小程序从注册账号到上架(图文详解)
Jul 17 Javascript
vue路由拦截器和请求拦截器知识点总结
Nov 08 Javascript
js实现单元格拖拽效果
Feb 10 Javascript
angular使用bootstrap方法手动启动的实例代码
Jul 18 #Javascript
详解jQuery中关于Ajax的几个常用的函数
Jul 17 #jQuery
js实现首屏延迟加载实现方法 js实现多屏单张图片延迟加载效果
Jul 17 #Javascript
node.js中grunt和gulp的区别详解
Jul 17 #Javascript
js实现多张图片延迟加载效果
Jul 17 #Javascript
js指定步长实现单方向匀速运动
Jul 17 #Javascript
webpack构建vue项目的详细教程(配置篇)
Jul 17 #Javascript
You might like
使用PHP制作新闻系统的思路
2006/10/09 PHP
用PHP实现的随机广告显示代码
2007/06/14 PHP
PHP连接SQLServer2005方法及代码
2013/12/26 PHP
将php数组输出html表格的方法
2014/02/24 PHP
PHP用mb_string函数库处理与windows相关中文字符及Win环境下开启PHP Mb_String方法
2015/11/11 PHP
thinkphp分页实现效果
2016/10/13 PHP
php原生数据库分页的代码实例
2019/02/18 PHP
thinkphp框架类库扩展操作示例
2019/11/26 PHP
Jquery优化效率 提升性能解决方案
2010/09/06 Javascript
使用JavaScript动态设置样式实现代码(2)
2013/01/25 Javascript
输入自动提示搜索提示功能的使用说明:sugggestion.txt
2013/09/02 Javascript
基于jquery扩展漂亮的下拉框可以二次修改
2013/11/19 Javascript
jquery新的绑定事件机制on方法的使用方法
2014/04/15 Javascript
javascript中关于&amp;&amp; 和 || 表达式的小技巧分享
2015/04/10 Javascript
Javascript实现可旋转的圆圈实例代码
2015/08/04 Javascript
JavaScript驾驭网页-DOM
2016/03/24 Javascript
Bootstrap输入框组件简单实现代码
2017/03/06 Javascript
jQuery实现下拉菜单动态添加数据点击滑出收起其他功能
2018/06/14 jQuery
layui输入框只允许输入中文且判断长度的例子
2019/09/18 Javascript
[05:15]DOTA2英雄梦之声_第16期_灰烬之灵
2014/06/21 DOTA
Android模拟器无法启动,报错:Cannot set up guest memory ‘android_arm’ Invalid argument的解决方法
2016/07/01 Python
Python正规则表达式学习指南
2016/08/02 Python
pandas数据清洗,排序,索引设置,数据选取方法
2018/05/18 Python
利用python对Excel中的特定数据提取并写入新表的方法
2018/06/14 Python
Python应用领域和就业形势分析总结
2019/05/14 Python
TensorFlow车牌识别完整版代码(含车牌数据集)
2019/08/05 Python
使用Python串口实时显示数据并绘图的例子
2019/12/26 Python
Html5新标签解释及用法
2012/02/17 HTML / CSS
寻找迷宫的一条出路,o通路;X:障碍
2016/07/10 面试题
成人继续教育实施方案
2014/03/01 职场文书
乔迁之喜主持词
2014/03/27 职场文书
群众路线领导干部个人对照检查材料(集锦)
2014/09/23 职场文书
私用公车造成事故检讨书
2014/11/16 职场文书
医学生自荐信范文(2016精选篇)
2016/01/28 职场文书
Redis做数据持久化的解决方案及底层原理
2021/07/15 Redis
maven 解包依赖项中的文件的解决方法
2022/07/15 Java/Android