移动端效果之IndexList详解


Posted in Javascript onOctober 20, 2017

写在前面

接着前面的移动端效果讲,这次讲解的的是IndexList的实现原理。效果如下:

移动端效果之IndexList详解

代码请看这里:github

移动端效果之picker

移动端效果之cellSwiper

1. 核心解析

总体来说的原理就是当点击或者滑动右边的索引条时,通过获取点击的索引值来使左边的内容滑动到相应的位置。其中怎样滑动到具体的位置,看下面分解:

1.1 基本html代码

<div class="indexlist">
 <ul class="indexlist-content" id="content">
  <!-- 需要生成的内容 -->
 </ul>
 <div class="indexlist-nav" id="nav">
  <ul class="indexlist-navlist" id="navList">
   <-- 需要生成的索引条 -->
  </ul>
 </div>
 <div class="indexlist-indicator" style="display: none;" id="indicator"></div>
</div>

1.2 DOM初始化

由于饿了么组件库中的indexList是采用vue组件生成DOM,我这里大致使用javascript来模拟生成DOM。

// 内容填充
function initialDOM() {
 // D.data 获取内容数据
 var data = D.data;
 var contentHtml = '';
 var navHtml = '';
 // 初始化内容和NAV
 data.forEach(function(d) {
  var index = d.index;
  var items = d.items;
  navHtml += '<li class="indexlist-navitem">'+ index +'</li>';
  contentHtml += '<li class="indexsection" data-index="'+ index +'"><p class="indexsection-index">'+ index +'</p><ul>';
  items.forEach(function(item) {
   contentHtml += '<a class="cell"><div class="cell-wrapper"><div class="cell-title"><span class="cell-text">'+ item +'</span></div></div></a>';
  });
  contentHtml += '</ul></li>';
 });

 content.innerHTML = contentHtml;
 navList.innerHTML = navHtml;
}

// 样式初始化
if (!currentHeight) {
 currentHeight = document.documentElement.clientHeight -content.getBoundingClientRect().top;
}
// 右边索引栏的宽度
navWidth = nav.clientWidth;
// 左边内容的初始化高度和右边距
// 高度为当前页面的高度与内容top的差值
content.style.marginRight = navWidth + 'px';
content.style.height = currentHeight + 'px';

1.3 绑定滑动事件

在右边的索引栏上加上滑动事件,当点击或者滑动的时候触发。在源代码中在touchstart事件的结尾处,在window上绑定了touchmove与touchend事件,是为了使得滑动得区域更大,只有在开始的时候在索引栏上触发了touchstart事件时,之后再window上触发滑动和结束事件,这就意味着我们在滑动的过程中可以在左侧的内容区域滑动,同时也能达到index的效果。

function handleTouchstart(e) {
 // 如果不是从索引栏开始滑动,则直接return
 // 保证了左侧内容区域能够正常滑动
 if (e.target.tagName !== 'LI') {
  return;
 }
 
 // 记录开始的clientX值,这个clientX值将在之后的滑动中持续用到,用于定位
 navOffsetX = e.changedTouches[0].clientX;
 
 // 内容滑动到指定区域
 scrollList(e.changedTouches[0].clientY);
 if (indicatorTime) {
  clearTimeout(indicatorTime);
 }
 moving = true;
 
 // 在window区域注册滑动和结束事件
 window.addEventListener('touchmove', handleTouchMove, { passive: false });
 window.addEventListener('touchend', handleTouchEnd);
}

这里面用到了e.changedTouches,这个API可以去MDN查一下。

如果不是用到多点触控,changedTouches和touches的区别并不是特别大,changedTouches在同一点点击两次,第二次将不会有touch值。具体可以看这篇文章

下面看一下如何滑动:

function scrollList(y) {
 // 通过当前的y值以及之前记录的clientX值来获得索引栏中的对应item
 var currentItem = document.elementFromPoint(navOffsetX, y);
 if (!currentItem || !currentItem.classList.contains('indexlist-navitem')) {
  return;
 }
 
 // 显示指示器
 currentIndicator = currentItem.innerText;
 indicator.innerText = currentIndicator;
 indicator.style.display = '';

 // 找到左侧内容的对应section
 var targets = [].slice.call(sections).filter(function(section) { 
  var index = section.getAttribute('data-index');
  return index === currentItem.innerText;
 });
 var targetDOM;
 if (targets.length > 0) {
  targetDOM = targets[0];
  // 通过对比要滑动到的区域的top值与最开始的一个区域的top值
  // 两者的差值即为要滚动的距离
  content.scrollTop = targetDOM.getBoundingClientRect().top - firstSection.getBoundingClientRect().top;
  
  // 或者使用scrollIntoView来达到相同的目的
  // 不过存在兼容性的问题
  // targetDOM.scrollIntoView();
 }
}

关于elementFromPoint的API可以看这里

caniuse.com上关于getBoundingClientRect和scrollIntoView的兼容性

getBoundingClientRect

移动端效果之IndexList详解

scrollIntoView

移动端效果之IndexList详解

最后需要注销window上的滑动事件

window.removeEventListener('touchmove', handleTouchMove);
window.removeEventListener('touchend', handleTouchEnd);

2. 总结
分析就这么多,多看源码能够学到优秀的设计理念。比如如果最开始让我来做的话,我可以就只会在右侧的索引栏上绑定事件,而不会关联左侧的内容,这样滑动的区域将会大大减小。

同时看源码可以学到一些比较偏僻的知识,促使自己去学习。比如文中的changedTouches以及elementFromPoint等API的学习。

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

Javascript 相关文章推荐
基于Jquery的表格隔行换色,移动换色,点击换色插件
Dec 22 Javascript
js中访问html中iframe的文档对象的代码[IE6,IE7,IE8,FF]
Jan 08 Javascript
js获取键盘按键响应事件(兼容各浏览器)
May 16 Javascript
自动设置iframe大小的jQuery代码
Sep 11 Javascript
基于jquery的网站幻灯片切换效果焦点图代码
Sep 15 Javascript
JS实现网站菜单拖拽移位效果的方法
Sep 24 Javascript
微信jssdk在iframe页面失效问题的解决措施
Mar 03 Javascript
JS如何设置iOS中微信浏览器的title
Nov 22 Javascript
使用Angular CLI快速创建Angular项目的一些基本概念和写法小结
Apr 22 Javascript
微信小程序之onLaunch与onload异步问题详解
Mar 28 Javascript
实例讲解JavaScript 计时事件
Jul 04 Javascript
JS+CSS实现过渡特效
Jan 02 Javascript
详解webpack性能优化——DLL
Oct 20 #Javascript
vue利用better-scroll实现轮播图与页面滚动详解
Oct 20 #Javascript
浅谈如何使用 webpack 优化资源
Oct 20 #Javascript
利用jQuery实现简单的拖曳效果实例代码
Oct 20 #jQuery
Js利用prototype自定义数组方法示例
Oct 20 #Javascript
js 中rewrap-ajax.js插件实例代码
Oct 20 #Javascript
jQuery访问浏览器本地存储cookie、localStorage和sessionStorage的基本用法
Oct 20 #jQuery
You might like
PHP CKEditor 上传图片实现代码
2009/11/06 PHP
php中在PDO中使用事务(Transaction)
2011/05/14 PHP
orm获取关联表里的属性值
2016/04/17 PHP
javascript中2个感叹号的用法实例详解
2014/09/04 Javascript
JS实现可直接显示网页代码运行效果的HTML代码预览功能实例
2015/08/06 Javascript
JS操作COOKIE实现备忘记录的方法
2016/04/01 Javascript
为JQuery EasyUI 表单组件增加焦点切换功能的方法
2017/04/13 jQuery
JavaScript中运算符规则和隐式类型转换示例详解
2017/09/06 Javascript
在Vue.js中使用Mixins的方法
2017/09/12 Javascript
JS实现的排列组合算法示例
2019/07/16 Javascript
使用Vue实现简单计算器
2020/02/25 Javascript
Vuejs通过拖动改变元素宽度实现自适应
2020/09/02 Javascript
[00:39]DOTA2上海特级锦标赛 Liquid战队宣传片
2016/03/04 DOTA
Python遍历zip文件输出名称时出现乱码问题的解决方法
2015/04/08 Python
Python极简代码实现杨辉三角示例代码
2016/11/15 Python
Python如何import文件夹下的文件(实现方法)
2017/01/24 Python
如何利用python查找电脑文件
2018/04/27 Python
python爬虫获取百度首页内容教学
2018/12/23 Python
python按键按住不放持续响应的实例代码
2019/07/17 Python
关于阿里云oss获取sts凭证 app直传 python的实例
2019/08/20 Python
python使用turtle库绘制奥运五环
2020/02/24 Python
Python调用接口合并Excel表代码实例
2020/03/31 Python
tensorflow安装成功import tensorflow 出现问题
2020/04/16 Python
Django form表单与请求的生命周期步骤详解
2020/06/07 Python
Python虚拟环境库virtualenvwrapper安装及使用
2020/06/17 Python
如何配置、使用和清除Smarty缓存
2015/12/23 面试题
集团公司人力资源部岗位职责
2014/01/03 职场文书
青春励志演讲稿范文
2014/08/25 职场文书
税务干部群众路线教育实践活动自我剖析材料
2014/09/21 职场文书
违章停车检讨书
2014/10/21 职场文书
拾金不昧表扬稿
2015/01/16 职场文书
飞越疯人院观后感
2015/06/09 职场文书
六一文艺汇演主持词
2015/06/30 职场文书
感恩的心主题班会
2015/08/12 职场文书
历史名人教你十五个读书方法,赶快Get起来!
2019/07/18 职场文书
win10以太网连接不上怎么办?Win10连接以太网详细教程
2022/04/08 数码科技