使用Vue实现一个滑动选择器,并支持懒加载

1,289 阅读5分钟

前情提要


最近接受到一个新需求,就是如下图所示的滑动选择器:

QQ录屏20220627124824 00_00_00-00_00_30.gif

一般来说,这种滑动选择器是在移动端使用的,所以Element UI上并没有。那么在不想要导入其它库的基础上,我们就需要自己实现一个,下面就让我们看看怎么实现吧。

项目地址:github.com/zhtzhtx/scr…

基础版


样式设计

我们先将基础的HTML结构搭建好:

image.png

加上CSS样式:

.divide-bar-stage {
  position: relative;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}

.divide-bar-container {
  position: relative;
  width: 100%;
  transform-style: preserve-3d;
}

.divide-bar-container.easein {
  transition: transform 0.5s ease;
}

.divide-bar-item {
  position: absolute;
  width: 100%;
  border-bottom: 1px solid #d3d3d3;
  text-align: center;
  backface-visibility: hidden;
  color: #a4a4a4;
  height: 35px;
  line-height: 35px;
  user-select: none;
}

.divide-bar-item.active {
  color: #3a72ed;
}

我们会发现页面上的展示效果是这样的:

image.png

这是由于我们还没有为每个滑片添加上轴距和旋转,我们希望每个滑片是一个围绕滚轴旋转的

image.png

旋转角度的计算公式为:旋转角度 = 180 / 滑片数量,当然你想要360°环绕也可以。

由上图可以知道:tan(旋转角度 / 2) = (滑片长度 / 2) / 轴距,也就说 轴距 = (滑片长度 / 2) / tan(旋转角度 / 2)。在JavaScript中,我们可以通过Math.tan计算正切值,但该函数接受的参数是弧度,所以我们还要计算旋转角度 / 2 的弧度。

1°的弧度 = π / 180,所以旋转角度 / 2 的弧度 = 旋转角度 * ( π / 360 )。那么轴距的计算公式为: 轴距 = 滑片长度 / ( Math.tan ( 旋转角度 * ( π / 360 )) * 2。

让我们将HTML的结构修改一下:

image.png

在data中添加监听变量,并在mounted生命周期中初始化数据:

image.png

在init函数中计算旋转角度和轴距:

image.png

好了,这样样式方面我们就完成了,看下效果:

image.png

功能实现

下面我们来实现功能,首先我们来看一下HTML的结构:

image.png

所有的滑片都是放在父容器中,所以只要旋转父容器相当于所有滑片跟着一起旋转,而旋转的角度则是根据鼠标按下后滑动的距离来设置的。

我们先添加上鼠标按下,鼠标移动和鼠标松开的事件

image.png

在data中添加监听数据:

image.png

当鼠标按下时,记录鼠标位置:

image.png

当鼠标长按时,根据其移动距离改变父容器的旋转角度:

image.png

当鼠标松开,判断当前旋转角度是否超过滑片一半,如果超过则自动进一格:

image.png

打印的index就是当前选中的滑片对应的index。

当然,如果仅仅支持滑动功能会有些不方便,一般滑动选择器还支持通过鼠标滚轮来上下滑动。

首先,在HTML上添加监听鼠标滚轮的事件:

image.png

每次滚轮滚动一下,则滑动选择器则旋转一格:

image.png

好了,这样一个基础版的滑动选择器就完成了。

懒加载版

基础版问题

当我完成基础版滑动选择器时,不禁思考起一个问题,现在是选项(滑片)不多的情况,如果碰上几十甚至上百选项时呢?

我们都浏览器渲染DOM元素是非常消耗性能,一次性渲染上百个滑片非常容易出现卡顿,同时我们滑动选择器一次展示的滑片是有限的,这无疑是中性能浪费。

针对这个,我开始构思支持懒加载的滑动选择器。

设计思路

我的思路是这样的,假设父组件传入了包含100个数据的数组,滑动选择器一开始只渲染5个选项(滑片),当我们向下滑动滑到第4个选项时,我们加载第4到第9的选项,并且删除之前的选项,保证在页面上只渲染5个选项DOM元素。

代码编写

首先,我们需要在data中新增几个监听变量:

image.png

在 init 函数中,先遍历父组件传入的数据,为每个数据添加 _ index 的属性,这样方便我们懒加载时不会渲染出错。

再根据父组件传入的 limit 数据,设置触发懒加载的最大值。

image.png

在 mousedown 函数中,和之前基础版不同的是,我们不再需要记录最后一次转动角度,因为在懒加载的过程中我们通过 _ index 进行标记。

在 mousemove 函数中,我们将旋转角度改成了旋转周期,方便我们计算 index ,将当前旋转的 index 传入 lazyLoad 函数中。

image.png

接下来,我们看懒加载的关键函数 lazyLoad 函数,首先判断当前是否转到了开头或结尾,如果是则不再转动:

image.png

然后,判断当前是否需要懒加载。如果当前展示数组不包含所有数据中顶端或尾端数据,说明当前并没有转动到头部或底部。同时当前转动超过了触发懒加载的最大值或小于触发懒加载的最小值,说明需要懒加载,否则不需要懒加载。

image.png

当需要懒加载时,根据当前转动的 index 和父组件传入的 limit在 data 数组中截取新的展示数组,并且更新当前转动的 index 、触发最大值和触发最小值。

如果不需要懒加载,则和之前逻辑一样。

image.png

在 mouseup 函数中,获取的是当前展示数组中选中数据的 _ index

image.png

在 wheel 中,逻辑和之前差不多,根据滚轮向上滑还是向下滑判断 index 是加一还是减一

image.png

OK,大功告成!