原生JS实现移动端Web轮播图:基于Tween算法的完整解析

作者:小圆点

时间:2026-10-22

浏览:151

本文详细解析如何使用原生JavaScript实现移动端Web轮播图。通过结合Touch事件监听与Tween算法中的easeOut缓动函数,解决屏幕适配、手势交互及平滑过渡等核心问题。文章提供了从HTML结构到JS逻辑的完整代码示例,重点讲解如何利用translate3d开启硬件加速,并通过N+2张图的策略实现无限循环滚动,为处理复杂轮播需求提供底层实现思路。

文章目录

移动端轮播图实现概述

前言

在移动端开发中,轮播图是极为常见且核心的功能模块。通常情况下,开发者倾向于直接引入Swiper等成熟的第三方库以快速实现功能。然而,当业务需求涉及高度定制化的交互或特殊的视觉效果时,现有库往往难以满足,导致修改困难。

因此,掌握自行构建基础组件的能力至关重要。这不仅能够灵活应对复杂多变的产品需求,还能在遇到Bug时更快速地定位和修复问题,从而显著提升个人技术能力与代码掌控力。

在未深入研读Swiper源码的前提下,笔者尝试独立实现一个兼具实用性与轻量级的移动端轮播图组件。经过数小时的逻辑推演与代码实践,最终实现了如图所示的效果:

移动端轮播图实现概述

移动端实现难点分析

相较于PC端,移动端轮播图的实现复杂度显著提升,主要体现在以下几个维度:

1. 轮播图必须兼容不同宽度及不同DPR(设备像素比)的屏幕显示。

2. 必须准确处理Touch系列相关事件,以捕捉用户的手势操作。

3. 不同移动设备对Touch事件的支持存在差异,可能引发兼容性问题。

4. 当手指拖动图片移动一定距离后,剩余的距离需要由程序自动完成滑动。

5. 自动滑动的过程必须包含ease(缓动)时间曲线,以提供自然的视觉体验。

尽管实现细节复杂,但解决此类问题的编程思路具有共通性。

交互现象与核心逻辑

我们在日常使用轮播图时,可以通过观察现象来推导其底层逻辑:

  • 具体表现为以下交互行为:
  • 当手指在图片上向左或向右滑动时,图片会跟随手指的移动轨迹进行位移。
  • 若手指移动的距离较短,松开后图片会自动复原至初始位置;若移动距离超过阈值,则自动切换至下一张图片。
  • 若手指左右滑动的速度较快,系统会判定为快速滑动操作,直接切换至下一张图片。
  • 为了实现图片的无限循环播放,我们需要采用“3+1+2”的结构策略,即通过N+2张图的排列方式来实现N张图的无限循环轮播效果。

技术方案与实现步骤

基于上述现象分析,我们可以提出以下基本实现方案:

1. 利用touchstart、touchmove和touchend三个Touch事件来完整捕获用户的触摸交互过程。

2. 在touchstart事件触发时,需记录手指的X坐标(通过Touch对象的pageX属性获取)以及当前的时间戳。

3. 在touchmove事件触发期间,持续记录pageX值,并累计算手指在X方向上的累计移动距离moveX。

4. 在手指离开屏幕(touchend)时,记录结束时间戳,并结合前两步获取的X方向移动距离与时间差进行计算。

5. 通过比较X方向的移动距离来判断滑动方向,并依据是否达到切换阈值决定是否需要切换到下一张图片;同时根据时间差判断用户是否执行了快速扫动操作。

6. 图片的位移操作建议使用translate3d来实现,以开启GPU硬件加速,提升渲染性能。

7. 对于移动一段距离后的缓动效果,可以使用Tween算法中的easeOut函数来计算每次移动的增量;当然,也可以利用CSS transition动画来实现,但Tween算法提供了更精细的控制。

核心代码实现解析

以下是具体的实现源码,供参考学习:

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结构搭建


<div class="banner"> 
 <div 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"> 
 </div> 
</div> 

JS代码第一部分:实现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旨在提供一种解决问题的核心思路,掌握该思路后,相信能够应对各种复杂的轮播图需求。

本文中采用Tween算法来实现ease-out效果,若追求代码简洁,也可使用transition动画实现,具体优化方案可参见轮播图优化篇:https://www.jb51.net/article/123304.htm

总结与扩展

总结

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

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章