美文网首页移动Web开发程序员HTML5
如何用H5实现一个触屏版的轮播器?

如何用H5实现一个触屏版的轮播器?

作者: gzgogo | 来源:发表于2016-04-28 01:31 被阅读2864次

初入前端,分享一下手机上触屏版轮播器的实现过程,大致功能如下:

  1. 支持循环滑动
  2. 宽度可任意设置,不需要与屏幕等宽
  3. 页面可纵向滚动
  4. 可设置回调监听元素的切换
  5. 纯js,不借助任何第三方库

原理

  1. 假设子元素.itemwidth为375px,使用绝对定位将所有子元素放在父元素内
  2. 将父元素.carouselwidth设置为375px,与子元素.item宽度相同
  3. 为父元素.carousel添加触摸事件:touchstart, touchmove, touchend
  4. 手指按下时,保存初始位置(clientX
  5. 手指滑动时,通过滑动距离判断滑动的方向:
  6. 手指向左滑动,则同时移动当前元素和当前元素右边的元素
  7. 手指向右滑动,则同时移动当前元素和当前元素左边的元素
  8. 手指抬起时,通过滑动距离判断是否切换到下一页
  9. 移动距离未超过子元素宽度的50%,将当前页面回滚到初始位置,不切换当前元素。
  10. 移动距离超过子元素宽度的50%,切换当前元素为下一个元素。
  11. 将当前元素的transform属性设置为translate3d(0px, 0px, 0px),并将z-index属性+1
  12. 将下一个子元素的transform属性设置为translate3d(375px, 0px, 0px),并将z-index属性+1
  13. 将上一个子元素的transform属性设置为translate3d(-375px, 0px, 0px),并将z-index属性+1
  14. 将其他所有子元素的z-index属性设置为默认值
  15. 第一个子元素的上一个元素是最后一个元素,最后一个元素的下一个元素是第一个元素,该步骤通过循环链表实现。

移动时设置的是子元素.item的transform属性,而不是父元素.carousel

实现步骤

html&css

//html
<div class="carousel" ontouchstart="" >  
  <div class="item" style="background: #3b76c0" >    
    <h3 >item-1</h3>  
  </div>  
  <div class="item" style="background: #58c03b;">    
    <h3>item-2</h3>  
  </div>  
  <div class="item" style="background: #c03b25;">    
    <h3>item-3</h3>
  </div> 
  <div class="item" style="background: #e0a718;">  
    <h3>item-4</h3>  
  </div>  
  <div class="item" style="background: #c03eac;">    
    <h3>item-5</h3>  
  </div>
</div>

//css
.carousel{
  height: 50%;
  position: relative;
  overflow: hidden;
}

.item {
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
}

js

设置初始状态

首先实现一个双向链表,用于维护轮播组件中的元素。

function Node(data) {
    this.data = data;
    this.prev = null;
    this.next = null;
    this.index = -1;
}

//双向循环列表
function LinkList() {
    var _nodes = [];
    this.head = null;
    this.last = null;

    if (typeof this.append !== "function") {
        LinkList.prototype.append = function (node) {
            if (this.head == null) {
                this.head = node;
                this.last = this.head;
            }
            else {
                this.head.prev = node;
                this.last.next = node;

                node.prev = this.last;
                node.next = this.head;

                this.last = node;
            }

            node.index = _nodes.length; //务必在push前设置node.index
            _nodes.push(node);
        }
    }
}

有了链表之后,创建一个链表实例,将子元素添加进链表内,并设置一些初始状态

var _container = document.querySelector("." + containerClass);
var _items = document.querySelectorAll("." + itemClass);

var list = loop ? new LinkList() : new SingleList();
for(var i = 0; i < _items.length; i++) {
  list.append(new Node(_items[i]));
}

var _prev = null;  //保存之前显示的元素
var _current = list.head;  //保存当前显示的元素,默认为第一个元素

var _normalZIndex = _current.data.style.zIndex;  //未显示元素的z-index值
var _activeZIndex = _normalZIndex + 1;  //当前显示元素的z-index值

var _itemWidth = _current.data.offsetWidth; //子元素宽度

positionItems(); //初始化元素位置
zindexItems(_current, _activeZIndex); //将当前元素及其左右元素的z-index加1

绑定触摸事件

touchstart事件

手指按下时,保存初始位置

_container.addEventListener("touchstart", function(e) {
  // e.preventDefault();//取消此行代码的注释会在该元素内阻止页面纵向滚动
  var touch = e.touches[0];
  startX = touch.clientX;   //保存手指按下时的位置
  startY = touch.clientY;
  _container.style.webkitTransition = ""; //取消动画效果
  startT = new Date().getTime();          //记录手指按下的开始时间
  isMove = false;
  transitionItems(_prev, false);             //取消之前元素的过渡
  transitionItems(_current, false);          //取消当前元素的过渡
}, false);

touchmove事件

手指在屏幕上滑动,页面跟随手指移动

_container.addEventListener("touchmove", function(e) {
    // e.preventDefault();//取消此行代码的注释会在该元素内阻止页面纵向滚动
    var touch = e.touches[0];
    var deltaX = touch.clientX - startX;  //计算手指在X方向滑动的距离
    var deltaY = touch.clientY - startY;  //计算手指在Y方向滑动的距离
    //如果X方向上的位移大于Y方向,则认为是左右滑动
    if (Math.abs(deltaX) > Math.abs(deltaY)){
        translate = deltaX > _itemWidth ? _itemWidth : deltaX;
        translate = deltaX < -_itemWidth ? -_itemWidth : deltaX;

        //同时移动当前元素及其左右元素
        moveItems(translate); 

        isMove = true;
    }
}, false);

touchend事件

手指离开屏幕时,计算最终需要停留在哪一页

_container.addEventListener("touchend",function(e) {
    // e.preventDefault();//取消此行代码的注释会在该元素内阻止页面纵向滚动

    //是否会滚
    var isRollback = false;

    //计算手指在屏幕上停留的时间
    var deltaT = new Date().getTime() - startT;
    if (isMove) { //发生了左右滑动
        //如果停留时间小于300ms,则认为是快速滑动,无论滑动距离是多少,都停留到下一页
        if(deltaT < 300){
            translate = translate < 0 ? -_itemWidth : _itemWidth;
        }else {
            //如果滑动距离小于屏幕的50%,则退回到上一页
            if (Math.abs(translate) / _itemWidth < 0.5){
                isRollback = true;
            }else{
                //如果滑动距离大于屏幕的50%,则滑动到下一页
                translate = translate < 0 ? -_itemWidth : _itemWidth;
            }
        }

        moveTo(translate, isRollback);
    }
}, false);

Carousel库

为了方便使用,我将整个实现过程封装成了一个库,并添加了prev()next()方法,使用非常简单:

<script src="lib/carousel.js"></script>

CreateCarousel("carousel", "item", true)
  .bindTouchEvent()
  .setItemChangedHandler(onPageChanged);

//参数"carousel"为容器的类名
//参数"item"为子元素的类名
//第三个参数设置是否需要循环播放,true为循环播放

该库可到github下载

参考

H5单页面手势滑屏切换原理

good night!

相关文章

  • 纯js实现支持触摸的3D照片墙

    继上一篇如何用H5实现一个触屏版的轮播器?之后,又写了一个具有3D效果的照片墙,支持触摸操作,在手机上也可以使用。...

  • 如何用H5实现一个触屏版的轮播器?

    初入前端,分享一下手机上触屏版轮播器的实现过程,大致功能如下: 支持循环滑动 宽度可任意设置,不需要与屏幕等宽 页...

  • 前端手机端常用MATA标签及解释

    (转载自CSDN,移动终端H5页面meta标签的设置案例)一、天猫 天猫触屏版 二、淘宝 淘宝网触屏版 三、京东 ...

  • 后端开发实用的插件整理

    目录 触屏滚动 视差滚动插件 日期选择器 图片 布局 轮播图 弹出层 音频视频 图表 幻灯/切换 动画 相册 取色...

  • JS移动客户端--触屏滑动事件

    移动端触屏滑动的效果其实就是图片轮播,在PC的页面上很好实现,绑定click和mouseover等事件来完成。但是...

  • 原生实现一个手势视差翻页器

    前言 原文:原生实现一个手势视差翻页器 #147 一句话需求 移动端实现一个轮播器,在轮播器上层有一个静止图层,不...

  • 轮播

    轮播: UIScrollview轮播UICollectionView轮播 实现步骤: (1)添加并设置定时器(2)...

  • 用vue写一个轮播图效果

    轮播图在网站中几乎无处不在,占用地方少,交互性好。今天就来聊聊如何用vue实现一个轮播效果。 一、原理在轮播图数组...

  • 仿造天猫、京东、凡客等,轮播图的开发(jQuery)

    轮播图思路:1、轮播图布局2、定时器实现轮播效果3、指示器的鼠标移入(mouseenter)、移出(mousele...

  • 移动端

    主流移动web站点 淘宝触屏版 京东商城手机版 移动端浏览器 基于webkit内核:QQ、百度、UC 适配问题 百...

网友评论

  • 笑谈红尘乱离人:感谢博主分享,Github上的demo中,

    _carousel.moveTo = function (dstIndex) {
    var offset = dstIndex - _current.index;
    if (offset !== 0) {
    var move = offset > 0 ? this.next : this.prev;
    offset = Math.abs(offset);
    while(offset > 0) {
    move();
    offset--;
    }
    }
    };

    这里没有实现啊,move()?
  • c104d2a4baec:赞一个,用链表来存储item,学习借鉴了!
  • 爱阿爸的阿龙龙:mark了 慢慢学习
  • aijian:能不能加入自动播放功能
    gzgogo:@aijian 很容易的,提供了prev(),next()方法,你可以自己设置个定时器调用这两个方法
  • e6b1605ad863:请问兼容性如何?
    gzgogo: @可可魏紫 chrome,safari,微信,安卓原生浏览器测试无问题

本文标题:如何用H5实现一个触屏版的轮播器?

本文链接:https://www.haomeiwen.com/subject/koacrttx.html