原生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头部样式

<Meta charset="UTF-8"> <Meta name="viewport" content="width=device-width,initial-scale=.5,maximum-scale=.5"> 移动端轮播图

HTML结构

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

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

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

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,vv,duration);
this.style.transform = 'translate3d(' + dis + 'px,0)';
if (vv > 0 && parseInt(this.style.transform.slice(12)) >= end) {
this.style.transform = 'translate3d(' + parseInt(dis) + 'px,0)';
clearInterval(this.timer);
callback && callback();
}
if (vv < 0 && parseInt(this.style.transform.slice(12)) <= end) {
this.style.transform = 'translate3d(' + parseInt(dis) + 'px,0)';
clearInterval(this.timer);
callback && callback();
}
}.bind(this),4);
}

touch事件部分

~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)';
    movex = 0;
    }
    if (tIndex === -1) {
    imgWrap.style.transform = 'translate3d(' + width (1 - imgSize) + 'px,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)';
    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',false);
imgWrap.addEventListener('touchend',false);
imgWrap.addEventListener('touchcancel',false);

}();

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

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

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

本文中使用的 tween算法来实现 ease-out效果,也可以使用 transtion动画实现,代码更加简洁,参见轮播图优化篇:

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对编程之家的支持

原文链接:https://www.f2er.com/js/36530.html

猜你在找的JavaScript相关文章