首 页 行业热点 新车 试驾评测 养车用车 车型库
当前位置:首页详解如何使用原生JS实现移动端web轮播图效果

详解如何使用原生JS实现移动端web轮播图效果

2020-11-27 来源:好土汽车网
导读 详解如何使用原生JS实现移动端web轮播图效果:在做移动端开发的时候,必不可少的是轮播图,下面这篇文章主要给大家介绍了关于利用纯JS实现移动端web轮播图的相关资料,重要的是结合Tween算法造轮子,文中通过示例代码介绍的非常详细,需要的朋友可以参考借鉴,下面来一起看看吧。前言相信大家应该都知道,
在做移动端开发的时候,必不可少的是轮播图,下面这篇文章主要给大家介绍了关于利用纯JS实现移动端web轮播图的相关资料,重要的是结合Tween算法造轮子,文中通过示例代码介绍的非常详细,需要的朋友可以参考借鉴,下面来一起看看吧。

前言

相信大家应该都知道,移动端的轮播图是我们比较常见的需求, 我们最快的实现方式往往是 使用第三方的代码, 例如 swiper , 但当遇到一些比较复杂的轮播图需求时, 往往是束手无策,不知道怎么改.

所以我们要尝试去自己造一些轮子, 以适应各种复杂多变的需求; 另外一点, 自己写的代码如果有bug是很容易修复的, 对自身的提高也很大.

在没有阅读swiper源码的过程下,我尝试自己实现一个简易而不失实用的移动端轮播图, 经过几个小时的思考和实践终于还是实现了(如图):


实现移动端的轮播图要比pc复杂一些,主要表现在以下几个方面:

1.轮播图要适应不同宽度/dpr的屏幕

2.需要使用 touch相关的事件

3.不同机型对 touch事件支持的不太一样,可能会有一些兼容性问题

4.手指移动图片一部分距离,剩下的距离需要自动完成

5.自动完成距离需要有 ease 时间曲线

但编程解决问题的思路都是差不多的,

我们在使用轮播图的时候可以仔细观察,通过现象看到本质:

  • 我们在使用轮播图的时候可以仔细观察,通过现象看到本质:

  • 手指放在图片上, 手指向左或者向右移动, 图片也随之移动;

  • 手指移动的距离少时,图片自动复原位置;手指移动的距离多时,自动切换到下一张;

  • 手指向左或者向右移动的快时,会切换到下一张;

  • 图片轮播是无限循环的, 我们需要采用 3 1 2 3 1的方式来实现, 即 N+2张图来实现N张图的无限循环轮播

  • 我们通过分析现象,可以提出一个基本实现方案:

    1. 手指触摸事件可以通过 touchstart touchmove touchend 3个事件来实现

    2.在手指 touchstart的时候我们需要记录 手指的x坐标, 可以使用 touch的pageX属性; 还有 这个时间点,

    3.手指touchmove的时候我们也需要记录pageX,并且记录累计移动的距离 moveX

    4.手指离开的时候,记录时间点, 根据前两步计算的 x方向移动的距离,时间点之差

    5.通过比较x方向移动距离来判断移动方向, 以及是否应该切换到下一张图; 根据时间判断用户是否进行了左右扫动的操作

    6.移动图片可以使用 translate3d来实现,开启硬件加速

    7.移动一段距离需要 easeOut效果,我们可以使用 Tween算法中的easeOut来实现我们每次移动的距离; 当然也可以使用 js设置 transition动画

    实现源码(仅供参考):

    head头部样式


    <head> 
     <meta charset="UTF-8"> 
     <meta name="viewport" content="width=device-width,initial-scale=.5,maximum-scale=.5"> 
     <title>移动端轮播图</title> 
     <style> 
     * { 
     box-sizing: border-box; 
     margin: 0; 
     padding: 0 
     } 
     .banner { 
     overflow: hidden; 
     width: 100%; 
     height: 300px 
     } 
     .banner .img-wrap { 
     position: relative; 
     height: 100% 
     } 
     .banner img { 
     display: block; 
     position: absolute; 
     top: 0; 
     width: 100%; 
     height: 100% 
     } 
     </style> 
    </head>

    HTML结构


    <p class="banner"> 
     <p class="img-wrap" id="imgWrap"> 
     <img src="images/banner_3.jpg" data-index="-1"> 
     <img src="images/banner_1.jpg" data-index="0"> 
     <img src="images/banner_2.jpg" data-index="1"> 
     <img src="images/banner_3.jpg" data-index="2"> 
     <img src="images/banner_1.jpg" data-index="3"> 
     </p> 
    </p>

    JS代码1, easeOut动画式移动,

    这里的 HTMLElement.prototype.tweenTranslateXAnimate ,是给所有的HTML元素类扩展的tweenTranslateXAnimate方法

    移动一段距离我们需要使用定时器来帮助我们完成,这个重复的操作


    <script> 
     HTMLElement.prototype.tweenTranslateXAnimate = function (start, end, callback) { 
     var duration = 50; 
     var t = 0; 
     var vv = end - start; 
     var Tween = { 
     Quad: { 
     easeOut: function (t, b, c, d) { 
     return -c * (t /= d) * (t - 2) + b; 
     } 
     } 
     }; 
     
     this.timer = setInterval(function () { 
     var dis = start + Tween.Quad.easeOut(++t, 0, vv, duration); 
     this.style.transform = 'translate3d(' + dis + 'px, 0, 0)'; 
     if (vv > 0 && parseInt(this.style.transform.slice(12)) >= end) { 
     this.style.transform = 'translate3d(' + parseInt(dis) + 'px, 0, 0)'; 
     clearInterval(this.timer); 
     callback && callback(); 
     } 
     if (vv < 0 && parseInt(this.style.transform.slice(12)) <= end) { 
     this.style.transform = 'translate3d(' + parseInt(dis) + 'px, 0, 0)'; 
     clearInterval(this.timer); 
     callback && callback(); 
     } 
     }.bind(this), 4); 
     } 
    </script>

    touch事件部分


    <script> 
     ~function () { 
     var lastPX = 0; // 上一次触摸的位置x坐标, 需要计算出手指每次移动的一点点距离 
     var movex = 0; // 记录手指move的x方向值 
     var imgWrap = document.getElementById('imgWrap'); 
     var startX = 0; // 开始触摸时手指所在x坐标 
     var endX = 0; // 触摸结束时手指所在的x坐标位置 
     var imgSize = imgWrap.children.length - 2; // 图片个数 
     var t1 = 0; // 记录开始触摸的时刻 
     var t2 = 0; // 记录结束触摸的时刻 
     var width = window.innerWidth; // 当前窗口宽度 
     var nodeList = document.querySelectorAll('#imgWrap img'); // 所有轮播图节点数组 NodeList 
     
     // 给图片设置合适的left值, 注意 querySelectorAll返回 NodeList, 具有 forEach方法 
     nodeList.forEach(function (node, index) { 
     node.style.left = (index - 1) * width + 'px'; 
     }); 
     
     /** 
     * 移动图片到当前的 tIndex索引所在位置 
     * @param {number} tIndex 要显示的图片的索引 
     * */ 
     function toIndex(tIndex) { 
     var dis = -(tIndex * width); 
     var start = parseInt(imgWrap.style.transform.slice(12)); 
     // 动画移动 
     imgWrap.tweenTranslateXAnimate(start, dis, function () { 
     setTimeout(function () { 
     movex = dis; 
     if (tIndex === imgSize) { 
     imgWrap.style.transform = 'translate3d(0, 0, 0)'; 
     movex = 0; 
     } 
     if (tIndex === -1) { 
     imgWrap.style.transform = 'translate3d(' + width * (1 - imgSize) + 'px, 0, 0)'; 
     movex = -width * (imgSize - 1); 
     } 
     }, 0); 
     }); 
     } 
     
     /** 
     * 处理各种触摸事件 ,包括 touchstart, touchend, touchmove, touchcancel 
     * @param {Event} evt 回调函数中系统传回的 js 事件对象 
     * */ 
     function touch(evt) { 
     var touch = evt.targetTouches[0]; 
     var tar = evt.target; 
     var index = parseInt(tar.getAttribute('data-index')); 
     if (evt.type === 'touchmove') { 
     var di = parseInt(touch.pageX - lastPX); 
     endX = touch.pageX; 
     movex += di; 
     imgWrap.style.webkitTransform = 'translate3d(' + movex + 'px, 0, 0)'; 
     lastPX = touch.pageX; 
     } 
     if (evt.type === 'touchend') { 
     var minus = endX - startX; 
     t2 = new Date().getTime() - t1; 
     if (Math.abs(minus) > 0) { // 有拖动操作 
     if (Math.abs(minus) < width * 0.4 && t2 > 500) { // 拖动距离不够,返回! 
     toIndex(index); 
     } else { // 超过一半,看方向 
     console.log(minus); 
     if (Math.abs(minus) < 20) { 
     console.log('距离很短' + minus); 
     toIndex(index); 
     return; 
     } 
     if (minus < 0) { // endX < startX,向左滑动,是下一张 
     toIndex(index + 1) 
     } else { // endX > startX ,向右滑动, 是上一张 
     toIndex(index - 1) 
     } 
     } 
     } else { //没有拖动操作 
     
     } 
     } 
     if (evt.type === 'touchstart') { 
     lastPX = touch.pageX; 
     startX = lastPX; 
     endX = startX; 
     t1 = new Date().getTime(); 
     } 
     return false; 
     } 
     
     imgWrap.addEventListener('touchstart', touch, false); 
     imgWrap.addEventListener('touchmove', touch, false); 
     imgWrap.addEventListener('touchend', touch, false); 
     imgWrap.addEventListener('touchcancel', touch, false); 
     
     }(); 
     
    </script>

    在触摸事件中最关键的参数是 pageX参数, 记录x的位置.

    当然这只是一个demo,还需要进一步的优化和封装, 以便于我们用在真实的项目.

    本demo仅仅是提供了一个解决问题的思路, 有了这个思路,相信各种复杂的需求也得以解决...

    显示全文