乐趣区

Vue列表过渡交换位置的鬼畜表现

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

退出移动版