当前位置:首页 > 日记 > 正文

移动端效果之Swiper详解

移动端效果之Swiper详解

写在前面

最近在做移动端方面运用到了饿了么的vue前端组件库,因为不想单纯用组件而使用它,故想深入了解一下实现原理。后续将会继续研究一下其他的组件实现原理,有兴趣的可以关注下。

代码在这里:戳我

1. 说明

父容器overflow:hidden;,子页面transform:translateX(-100%);width:100%;

2. 核心解析

2.1 页面初始化

由于所有页面都在手机屏幕左侧一个屏幕宽度的位置,因此最开始的情况是页面中看不到任何一个子页面,所以第一步应该设置应该显示的子页面,默认情况下defaultIndex:0

function reInitPages() {  // 得出页面是否能够被滑动  // 1. 子页面只有一个  // 2. 用户手动设置不能滑动 noDragWhenSingle = true  noDrag = children.length === 1 && noDragWhenSingle;  var aPages = [];  var intDefaultIndex = Math.floor(defaultIndex);  var defaultIndex = (intDefaultIndex >= 0 && intDefaultIndex < children.length)     ? intDefaultIndex : 0;    // 得到当前被激活的子页面索引  index = defaultIndex;  children.forEach(function(child, index) {    aPages.push(child);    // 所有页面移除激活class    child.classList.remove('is-active');    if (index === defaultIndex) {      // 给激活的子页面加上激活class      child.classList.add('is-active');    }  });  pages = aPages;}

2.2 容器滑动开始(onTouchStart)

在低版本的android手机上,设置event.preventDefault()会起到一定的性能提升作用,使得滑动起来不是那么卡。

前置工作:

  • 如果用户设置了 prevent:true, 滑动时阻止默认行为
  • 如果用户设置了stopPropagation:true, 滑动时阻止事件向上传播
  • 如果动画尚未结束,阻止滑动
  • 设置dragging:true,滑动开始
  • 设置用户滚动为false

滑动开始:

使用一个全局对象记录信息,这些信息包括:

dragState = {  startTime      // 开始时间  startLeft      // 开始的X坐标  startTop      // 开始的Y坐标(相对于整个页面viewport pageY)  startTopAbsolute  // 绝对Y坐标(相对于文档顶部 clientY)  pageWidth      // 一个页面宽度  pageHeight     // 一个页面的高度  prevPage      // 上一个页面  dragPage      // 当前页面  nextPage      // 下一个页面};

2.3 容器滑动(onTouchMove)

套用全局dragState,记录新的信息

dragState = {  currentLeft     // 开始的X坐标  currentTop     // 开始的Y坐标(相对于整个页面viewport pageY)  currentTopAbsolute // 绝对Y坐标(相对于文档顶部 clientY)};

那么我们就可以通过开始和滑动中的信息来计算出一些东西:

滑动的水平位移(offsetLeft = currentLeft - startLeft)

滑动的垂直位移(offsetTop = currentTopAbsolute - startTopAbsolute)

是否是用户的自然滚动,这里的自然滚动说的是用户并不是想滑动swiper,而是想滑动页面

// 条件// distanceX = Math.abs(offsetLeft);// distanceY = Math.abs(offsetTop);distanceX < 5 || ( distanceY >= 5 && distanceY >= 1.73 * distanceX )

判断是左移还是右移(offsetLeft < 0 左移,反之,右移)

重置位移

// 如果存在上一个页面并且是左移if (dragState.prevPage && towards === 'prev') {  // 重置上一个页面的水平位移为 offsetLeft - dragState.pageWidth  // 由于 offsetLeft 一直在变化,并且 >0  // 那么也就是说 offsetLeft - dragState.pageWidth 的值一直在变大,但是仍未负数  // 这就是为什么当连续属性存在的时候左滑会看到上一个页面会跟着滑动的原因  // 这里的 translate 方法其实很简单,在滑动的时候去除了动画效果`transition`,单纯改变位移  // 而在滑动结束的时候,加上`transition`,使得滑动到最后释放的过渡更加自然  translate(dragState.prevPage, offsetLeft - dragState.pageWidth);}// 当前页面跟着滑动translate(dragState.dragPage, offsetLeft);// 后一个页面同理if (dragState.nextPage && towards === 'next') {  translate(dragState.nextPage, offsetLeft + dragState.pageWidth);}

2.4 滑动结束(onTouchEnd)

前置工作:

在滑动中,我们是可以实时地来判断到底是不是用户的自然滚动userScrolling,如果是用户自然滚动,那么swiper的滑动信息就不算数,因此要做一些清除操作:

dragging = false;dragState = {};

当然如果userScrolling:false,那么就是滑动子页面,执行doOnTouchEnd方法

判断是否是tap事件

// 时间小于300ms,click事件延迟300ms触发// 水平位移和垂直位移栋小于5像素if (dragDuration < 300) {  var fireTap = Math.abs(offsetLeft) < 5 && Math.abs(offsetTop < 5);  if (isNaN(offsetLeft) || isNaN(offsetTop)) {    fireTap = true;  }  if (fireTap) {    console.log('tap');  }}

判断方向

// 如果事件间隔小于300ms但是滑出屏幕,直接返回if (dragDuration < 300 && dragState.currentLeft === undefined) return;// 如果事件间隔小于300ms 或者 滑动位移超过屏幕宽度 1/2, 根据位移判断方向if (dragDuration < 300 || Math.abs(offsetLeft) > pageWidth / 2) {  towards = offsetLeft < 0 ? 'next' : 'prev';}// 如果非连续,当处于第一页,不会出现,当处于最后一页,不会出现if (!continuous) {  if ((index === 0 && towards === 'prev')     || (index === pageCount - 1 && towards === 'next')) {    towards = null;  }}// 子页面数量小于2时,不执行滑动动画if (children.length < 2) {  towards = null;}

执行动画

// 当没有options的时候,为自然滑动,也就是定时器滑动function doAnimate(towards, options) {  if (children.length === 0) return;  if (!options && children.length < 2) return;  var prevPage, nextPage, currentPage, pageWidth, offsetLeft;  var pageCount = pages.length;  // 定时器滑动  if (!options) {    pageWidth = element.clientWidth;    currentPage = pages[index];    prevPage = pages[index - 1];    nextPage = pages[index + 1];    if (continuous && pages.length > 1) {      if (!prevPage) {        prevPage = pages[pages.length - 1];      }      if (!nextPage) {        nextPage = pages[0];      }    }    // 计算与之后    // 重置位移    // 参看doOnTouchMove    // 其实这里的options 传与不传也就是获取信息与信息    if (prevPage) {      prevPage.style.display = 'block';      translate(prevPage, -pageWidth);    }    if (nextPage) {      nextPage.style.display = 'block';      translate(nextPage, pageWidth);    }  } else {    prevPage = options.prevPage;    currentPage = options.currentPage;    nextPage = options.nextPage;    pageWidth = options.pageWidth;    offsetLeft = options.offsetLeft;  }  var newIndex;  var oldPage = children[index];  // 得到滑动之后的新的索引  if (towards === 'prev') {    if (index > 0) {      newIndex = index - 1;    }    if (continuous && index === 0) {      newIndex = pageCount - 1;    }  } else if (towards === 'next') {    if (index < pageCount - 1) {      newIndex = index + 1;    }    if (continuous && index === pageCount - 1) {      newIndex = 0;    }  }  // 动画完成之后的回调  var callback = function() {    // 得到滑动之后的激活页面,添加激活class    // 重新赋值索引    if (newIndex !== undefined) {      var newPage = children[newIndex];      oldPage.classList.remove('is-active');      newPage.classList.add('is-active');      index = newIndex    }    if (isDone) {      end();    }    if (prevPage) {      prevPage.style.display = '';    }    if (nextPage) {      nextPage.style.display = '';    }  }  setTimeout(function() {    // 向后滑动    if (towards === 'next') {      isDone = true;      before(currentPage);      // 当前页执行动画,完成后执行callback      translate(currentPage, -pageWidth, speed, callback);      if (nextPage) {        // 下一面移动视野中        translate(nextPage, 0, speed)      }    } else if (towards === 'prev') {      isDone = true;      before(currentPage);      translate(currentPage, pageWidth, speed, callback);      if (prevPage) {        translate(prevPage, 0, speed);      }    } else {     // 如果既不是左滑也不是右滑     isDone = true;     // 当前页面依旧处于视野中     // 和滑出     translate(currentPage, 0, speed, callback);     if (typeof offsetLeft !== 'undefined') {       if (prevPage && offsetLeft > 0) {          translate(prevPage, pageWidth * -1, speed);       }       if (nextPage && offsetLeft < 0) {          translate(nextPage, pageWidth, speed);       }     } else {      if (prevPage) {       translate(prevPage, pageWidth * -1, speed);      }      if (nextPage) {       translate(nextPage, pageWidth, speed);      }     }    }  }, 10);}?

后置工作:

清除一次滑动周期中保存的状态信息

dragging = false;dragState = {};

总结

整体来说实现原理还是比较简单的,滑动开始记录初始位置,计算与的应该展示的页面;滑动中计算位移,计算的位移;滑动结束根据位移结果执行相应的动画。

有一个细节就是,在滑动中transition的效果置为空,是为了防止在滑动中与因为过渡存在而位移得不自然,在滑动结束后再给他们加上动画效果。

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

相关文章

JS 判断某变量是否为某数组中的一

JS 判断某变量是否为某数组中的一

变量,种方法,组中,电脑软件,JS,1.正则表达式js 中判断某个元素是否存在于某个 js 数组中,相当于 PHP 语言中的 in_array 函数。Array.prototype.in_array=function(e){var r=new RegExp(','+e+',');return (r.test(','+this.join(this…

怎么使用快速表格库中的表格向Word

怎么使用快速表格库中的表格向Word

表格,库中,快速,电脑软件,Word,  Word 2013提供了一个&ldquo;快速表格&rdquo;库,用户可以将经常使用的表格添加到这个库中,从而使已经完成样式设定的表格能够重复使用,而不必每次重新建立。以下是小编为您带来的关于使用快速表格库中的表格…

简单实现jQuery手风琴效果

简单实现jQuery手风琴效果

简单实现,手风琴,效果,电脑软件,jQuery,本文实例为大家分享了jQuery实现手风琴效果的具体代码,供大家参考,具体内容如下js代码:<script type="text/javascript" src="jquery-3.0.0.js"></script><script type="text/javascript"> $…

jquery网页加载进度条的实现

jquery网页加载进度条的实现

网页,进度条,加载,电脑软件,jquery,本次主要介绍一下网页加载进度的实现。如下图,在页面加载的时候,上方红色的进度条网页加载进度的好处是能够更好的反应当前网页的加载进度情况,loading进度条可用动画的形式从开始0%到100%完成网页加载这一…

怎么在ppt中设置播放间隔时间在ppt

怎么在ppt中设置播放间隔时间在ppt

设置,方法,步骤,间隔时间,电脑软件,  经常在使用PPT时会播放一些图片,如果要让他们自动播放而不是点击一次播放一张幻灯片,只需要设置一下就能实现,下面小编教你怎么在ppt中设置播放间隔时间。ppt中设置播放间隔时间的步骤首先我们先打开ppt…

JS二分查找算法详解

JS二分查找算法详解

二分查找算法,详解,电脑软件,JS,二分法查找,也称折半查找,是一种在有序数组中查找特定元素的搜索算法。查找过程可以分为以下步骤:(1)首先,从有序数组的中间的元素开始搜索,如果该元素正好是目标元素(即要查找的元素),则搜索过程结束,否则进行下一步。…

wps文字如何设置双面打印

wps文字如何设置双面打印

文字,方法,设置,秘诀,如何设置,  在日常办公中我们做一份文件经常需要对编辑好让后进行打印,那么WPS文字打印到底如何设置呢?下面给大家分享wps深圳双面打印的方法,希望能帮到大家。wps文字双面打印设置的方法1、在左上角&ldquo;WPS文字&rd…

Word2007如何将文档中的文字对齐

Word2007如何将文档中的文字对齐

文字,对齐,文档,如何将,电脑软件,  在一篇Word文档完成后,如果不进行文字对齐,就会影响美观,对于完美主义者来说,这是很难容忍的。默认的情况下我们会采用空格对齐文字,但是对齐的结果也不太理想。以下是小编为您带来的关于Word2007将文档中的…

PS怎么画一个可爱的小黑猫?

PS怎么画一个可爱的小黑猫?

画一,可爱,小黑猫,电脑软件,PS,想要画小黑猫首先要画黑色的身体,蓝色的耳朵,还有白色的脚与尾巴,该怎么画呢?下面我们就来看看详细的教程。软件名称:Adobe Photoshop 8.0 中文完整绿色破解版软件大小:150.1MB更新时间:2015-11-041、用钢笔来描绘出…

PHP编程获取的主色调的方法【基于I

PHP编程获取的主色调的方法【基于I

编程,扩展,方法,主色调,电脑软件,本文实例讲述了PHP编程获取图片的主色调的方法。分享给大家供大家参考,具体如下:代码中用到了php的图片扩展,所以使用之前,需要先安装PHP的Imagick扩展,具体安装如下(window下): windows7下安装php的imagick和imag…

Photoshop制作超酷的传奇游戏宣传

Photoshop制作超酷的传奇游戏宣传

传奇,超酷,宣传海报,游戏,电脑软件,相信很多人看过经典的传奇游戏网页配色,红红火火愰愰惚惚,颜色非常的跳跃。做成这种风格,确实很传奇,也能满足需求方的要求。不过个人希望尝试一下不用的风格,带来不一样的视觉感受。最终效果一、简单思路。二…

ReactNative列表ListView的用法

ReactNative列表ListView的用法

列表,电脑软件,ReactNative,ListView,最近在学习ReactNative,本文介绍了ReactNative列表ListView的用法,分享给大家,也给自己留个笔记ListView在Android中,如果我们需要显示一个ListView,有两项是比不可少的,首先是ListView的数据源,其次是ListVie…