H5 列表 flex布局横向滚动+弹性左滑松手查看更多关于H5如何实现“列表弹性左滑松手查看更多”的事,我没有找到轮子,
1. 引言
💁♀️👉 什么是“弹性左滑松手查看更多”? 如图⬆️,在横向滑动的列表末端加一个指示滑块,列表滑动到尽头时滑块弹性拉伸,拉伸到一定程度松手可触发事件~效果就像这样↙️↙️ 这个交互在App上算是比较常见了,搜一搜有很多代码可以参照。 而当我拿到这个需求,要在h5中实现时,想找找参考,别说插件了,连代码块都没搜到 ~ 🤦🏻♀️ 或许搜索姿势不对?anyway,找不到轮子只能自己写一写了。
2.方案
2.1 思考
看到这种弹性滑动首先想到的是swiper是否可以实现,翻了翻演示案例,嗯,没有。参考swiper利用translate变换写一个虚拟滚动呢?还要搞平滑滚动动画,想了想略复杂。是不是可以在原生滚动的基础上加上触底拉伸的效果呢?
2.2 flex布局横向滚动
首先利用flex布局实现卡片列表横向滚动。我们在列表的最后,插入一个“查看更多”的卡片。flex布局下,“查看更多”会与列表的其他卡片自动对齐高度,这是使用弹性盒子的好处。
.list{
display: flex;
flex-wrap: nowrap; /* 弹性盒子不折行 */
overflow-x: auto;
}
.item{
/* 内部卡片样式 ... */
}
.item-more{
content: "查看更多";
writing-mode: vertical-lr; /* 竖排文字 */
/* ... */
}
2.3 藏在列表右侧的色块
现在列表搞定,如何实现拖拽拉伸?我的思路是在列表右侧定位一个元素,隐藏在屏幕之外,当列表滚动到最右,继续左滑时,整个列表进行translate
左移,拉出隐藏元素。
具体做法: 监听列表touch事件。判断当列表滚动距离已到达最大,开始实时记录触摸位置pageX,通过计算手指移动距离,改变列表容器的translateX,实现位移变换。当touchend,判断位移达到指定距离,可以触发“松手”事件。
本文中代码仅为示例代码,变量声明、语法逻辑等做了一些简化,理解其意思即可
.list{
/* ...flex */
position: relative;
transition: all 0.1s; /* 使位移平滑过渡 */
}
.after{
position: absolute;
width: 100%;
right: -100%;
top: 0;
height: 100%;
background: #f5f5f5; /* 与“查看更多”同色 */
}
效果如下:
2.4 弹性拉伸
实现到这里已经完成了基本功能:左滑到底拉伸,松手触发事件。在以上的代码中,手指移动和列表的位移是一比一设置,而这个拉动体验很差,接下来考虑交互优化,做一个弹性的拉伸效果,即:随着手指滑动,拉动越来越慢直到锁死。
具体做法是修改上面 trans
和 dragDistance
的函数对应关系。这里就要用到数学公式了。
如上,在fooplot网站生成了一个抛物线方程,一个顶点式:在 0<x<100 的范围内,随x增加,y增加得越来越缓慢。(函数中的62、100分别对应列表滑动和手指滑动的最大距离,数值可以根据实际需要调整)
对应代码:
const DISTANCE_MAX = 100; // 手指移动的最大距离
// 在上图代码18行前插入计算
trans = DRAG_MAX - (DISTANCE_MAX / DRAG_MAX ** 2) * (trans - DRAG_MAX) ** 2;
看下最终效果✅
3. 始料未及之坑
花了3小时完成了功能,长出一口气,
没想到交付给测试同学,马上就喜提bug。
3.1 坑之iPhone兼容
“😏 你这在iPhone上有bug,露白了”。
???,一直用的安卓机,没想到在iPhone中,滚动列表是自带拉伸回弹效果的。
🤷🏻♀️ 在安卓和电脑浏览器模拟器中,列表左滑到最右端时,就无法继续拖动了,而iPhone中,还可以继续拉拉拉,导致“查看更多” 和 .after
元素之间,出现一段断层间隙,露出了列表的底色。(因为没有机器不太方便录图,自行理解)
那么iPhone自带的滚动拉伸有什么特点呢?调试发现当列表拉伸时,上面代码中的 scrollDistance
值出现10、20、30、100...ok,
原来iPhone中,可滚动元素的 offsetWidth + scrollLeft
是可以大于 scrollWidth
的!😳惭愧,开发这么多年,才知道这件事...
那么这样就好办了,在iPhone中就不要再判断位移了,直接在列表底层定位一个背景色元素,使用系统自带的列表回弹,通过露出这个底色元素实现“查看更多”的拉伸效果,而不触发列表形变赋值。
代码修改之后:
// 原图代码11行
if(scrollDistance>0){
trans = 0;
draggingOk = scrollDistance>DRAG_LINE; // 直接用滚动条偏移量判断是否可触发事件
} else if(scrollDistance===0){
// 不变
} else {
// 不变
}
3.2 坑之继续安卓兼容
一顿修改猛如虎,提交之后。。。
“🥺 其中一个安卓测试机,滑到最右后,怎么拖不动了?!”。
迎来了新的bug。。。
拿来了该测试机一番核验之后,原来,安卓机的scrollDistance
也是可以大于0的, 代码走到了错误的路径,导致没有触发trans变化。又get到了新的知识。。。
调试发现,该测试机的滚动条溢出值为0.6666666px
,并且后来在测试其他机器的时候,出现了-0.3333333px
这样的值,溢出在1px以内,最终修改代码为:
if(scrollDistance>1){
// ...
} else if(scrollDistance>-1){
// ...
} else {
// ...
}
get 到这个问题后要注意,以后若是写触底加载之类的功能时,判断触底状态,不可直接用 offsetWidth + scrollLeft - scrollWidth === 0 判断
4. 代码共享
我把组件拆出来做了一个插件,为提高可用性做了一些配置项。一些案例效果:
👉点击查看 Github 地址
代码是React+Typescript写的,需要的可以直接引用,不适合的可以做参考自己写一份啦,代码并不十分复杂。
5. 写在最后
整个实现过程还是比较粗糙,还有很多可优化的地方,欢迎提出宝贵意见~
以及有其他方案的话欢迎交流~
转载自:https://juejin.cn/post/7028149988882382856