1. 需求
– 官方文档 列表的排序过渡
不多说,官方文档已经写得很明白。
项目中需要实现的功能是,根据时间线展示数据,一年的数据分为 12 个月,每个月内不同指标的数据占比排序,有进度条,数值大小,百分比等。
每一个月的数据变化,代表着排序出现变化,所以用到了 Vue 的列表交换位置效果。
2. 表现
一开始,其实没问题,很顺利的交换了位置,只要把每个月份的数据依次填充到 list,数据驱动视图的改变,虽然交换的速度很快。
但是,这样直接展示数据,效果太差,没有逼格,产品觉得还可以做得更好,比如:
- 进度条的长度不要一下子就改变完成,要有一个过渡效果
好的,我加一个过渡效果,{transition-duration: .3s;}
- 数值的变化也需要一个变化效果,不要从 10 直接飙到 100,得 11,12,13,……,100
这是什么操作?一定要做么?能不能不做?好吧,我做!!!
怎么做呢?
月份的切换,设定为 5 秒切换一次。
5 秒之内,不断的修改数据,对应的 value 从上一个月份自增到下一个月份,同样还是数据驱动视图刷新,css{transition-duration: .3s;}
都是多余的,删掉,OK!
然鹅,鬼畜出现了。
数据没问题,过渡效果棒棒的。
但是位置交换的时候,就跟发羊癫疯,又像帕金森,磕多了炫迈,停不下来。
WTF?
还能不能好好的搬砖?
3. 分析
其实没什么好分析的,代码就这几行。
数据驱动视图刷新,肯定是数据更新造成的。
但是为什么数据更新会造成 排序交换位置
出问题呢?
已经设定了 5 秒钟才交换一次位置。
找不到原因,就无法解决这个 bug。
看来,还是得分析 Vue 的交换位置逻辑。
官方文档读了几遍,终于,找到了关键字:FLIP。
在 Vue 中,交换位置的时候,其实不是真正的交换位置,毕竟列表中大部分的 DOM 元素是重用的,只是文字节点内容改变。
而实现交换效果的逻辑是:
假设列表有 4 个元素,依次排列 a,b,c,d。
当排序发生变化的时候,顺序为 b,a,c,d。
通过 el.getBoundingClientRect()
获得 a 元素和 b 元素的位置差,添加特定的 class,这个 class 设定了元素的变形过渡时间:
.flip-list-move {transition: transform 1s;}
然后计算出位置差 invert
,通过设定变形效果el.style.transform = translateY(${invert}px)
,造成 a,b,c,d 依然在旧的位置的效果,然后移除变形设置,el.style = ''
。
因为这时候 a,b,c,d 的数据已经交换完成,元素回归原位置,看起来就像是从旧的位置直接来到新的位置。
过程有 4 个:
- 数据交换,添加缓动样式
flip-list-move
- 排序,通过
el.style.transform = translateY(${invert}px)
把交换后的数据设置成交换前的效果 - 移除变形,
el.style = ''
,元素开始回归原位,但是看起来是跑到新的位置,这是重点 - 交换完成,移除缓动样式
flip-list-move
结语
之前一直有使用 Vue 的过渡效果,但是从来没有分析过实现的逻辑,这次终于踩到了巨坑。
很是不幸!
通过这次的分析,算是掌握了新的知识,原来可以这么玩!
但是,WTF,这个需求该怎么做???
已实现的 github demo