实现一个 Swiper

设计一个五图的 Swiper,设计稿如下:
插图Swiper 的功能如下:

  1. 左右切换
  2. 无限轮播
  3. 任意图片数

接下来,详细介绍这三个功能的实现过程:

左右切换

这里指触发左右切换的手指交互,目前主要是以下两种:

方案 示意图
手指拖拽 拖拽
手势判断 手势

手指拖拽容易有性能问题并且实现相对麻烦,所以笔者果断采用了手势判断,伪代码如下:

无限轮播

无限轮播要面对的是两个问题:

  • 轮播的数据结构;
  • 前端渲染

数据结构

无限轮播笔者联想到旋转木马。
轮圈圈

在数据结构中有一个叫循环链表的结构,可以完美地模拟旋转木马。
循环链表

javascript 没有指针,链表需要由数组来模拟。分析循环链表的两个重点特征:

  1. 数据项都由头指针访问
  2. 链表头尾有指针串联

笔者用 pop&unshift/shift&push APIs 模拟指针的前后移动,解决了链表头尾串联的问题,然后用数组的第一个元素(Arrayy[0])作为头指针。

循环链表

伪代码如下:

前端渲染

swiper 换个角度来看,它其实是一个金字塔:

梳理好层级问题再把过渡补间写上,swiper 的渲染就已经OK了。以下是伪代码:

任意图片数

图片数可以分成三种情况来讨论:count == 5; count > 5; count 。其中 count == 5 是理想条件,上几节就是围绕它展开的。本节将分析 count > 5 与 count 的解决思路。

count > 5

将循环链表(5节)扩容:
扩容

扩容后的工作过程如下:

  1. 循环链表指针移动;
  2. 渲染节点(1, 2, 3, n-1, n);
  3. 回收节点(4, 5, …, n-2)。

注:这里的回收节点指隐藏节点(display: none/visibility: hidden)

渲染金字塔如下:

渲染金字塔

为了提高性能笔者在循环链表与节点中间创建了一个快照数组 snapshot,snapshot 映射节点上的属性,循环链表每一次变动都会生成一个新的快照数组 nextSnap,通过 nextSnap 来更新 snapshot 与 节点样式。以下是实现的伪代码:

count

count >= 5时,渲染节点是一个稳定的金字塔:

渲染金字塔

count 时,渲染金字塔变得不确定:

count 金字塔
1 渲染金字塔
2 渲染金字塔
3 渲染金字塔
4 渲染金字塔

由于只有 count == 1 ~ 4 四种情况,可以直接用个 swith 把状态列表出来:

上面的伪代码显得很冗长,并不是个好实现方式。不过仍能从上面代码获得启发: 渲染列表(renderList) 与循环链表(queue)的对应关系 —— [shift, pop, shift, pop, shift]。于是伪代码可以简化为:

 

细节优化

笔者实现的 swiper: https://leeenx.github.io/mobile-swiper/v1.html

(count >= 5)运行效果如下:
v1

仔细观察能看到切换效果上的小瑕疵:
v1

造成这个瑕疵是因为同值 z-index 节点的渲染层级与 DOM 树的出现顺序相关: 后出现的节点层级更高。

解决方案很简单,为 swiper 添加一个 translateZ 。如下伪代码:

添加 z-index 后的swiper: https://leeenx.github.io/mobile-swiper/v2.html
v2

再看看 count 的运行效果:

count 效果图 地址
1 https://leeenx.github.io/mobile-swiper/v2.html?count=1
2 https://leeenx.github.io/mobile-swiper/v2.html?count=2
3 https://leeenx.github.io/mobile-swiper/v2.html?count=3
4 https://leeenx.github.io/mobile-swiper/v2.html?count=4

count == 2 / count == 4 时,swiper 向右切换时怪怪的,总感觉有什么不对!!其实问题出在渲染金字塔上,偶数swiper 在视觉在不是一个对称的图形:

渲染金字塔

由于笔者使用定势渲染的原因造成金字塔底被固定在左侧,当向右侧切换时会觉得很奇怪。这里其实只要加一个方向修正即可,以下是修正的伪代码:

修复后的效果如下:

count 效果图 地址
2 https://leeenx.github.io/mobile-swiper/index.html?count=2
4 https://leeenx.github.io/mobile-swiper/index.html?count=4

总结

感谢阅读完本文章的读者。本文最终实现的 swiper 笔者托管在 Github 仓库,有兴趣的读者可以看一下:https://github.com/leeenx/mobile-swiper

希望对你们有帮助。

1 8 收藏 评论

可能感兴趣的话题



直接登录
跳到底部
返回顶部