a house in the woods

Hi, nice to meet you.

  1. 1. 需求背景
  2. 2. 实现过程
  3. 3. 展示DEMO

需求背景

在移动实现一个固定头列的表格,效果大概是像element-ui展示的这个一样。

实现过程

最开始的版本,我简单地将第一列,表头和表体拆开。分别监听它们的scroll事件,然后同步它们的scroll位置。这个方法与element-ui的差不多,只是没有使用table元素实现。

但在测试的时候发现,表头和第一列和表体的滚动会有些不同步,这点其实在element-ui的示例也能看出来。

这个问题似乎如果用事件驱动的方式,总可能会出现视图不同步的问题,特别是在移动端的表现千差万别的情况下。

而如果它们是一体的话,那就能保证是同步的了。所以我们换另一种思路,将它们三者合为一体,但又要在滚动的时候将第一列和表头分别固定在左端和顶部。要解决这个矛盾,我们只需要设置第一列和表头的transform让它们作抵消scroll的偏移。

但这样又回到最初的问题带来的结论,我们似乎不能通过事件的回调完全实时地同步,例如上面说的transformscroll,当垂直滑动时,同步表头的transform;当水平滑动时,同步第一列的transform

为了避免滚动时需要同步位置的问题,这里就分别引入多一个第一列和表头,它们分别定位于左端和顶部,悬浮在真正的表格上。使用的逻辑如下:

  1. 水平滚动,显示表头(真),显示第一列(伪)
  2. 垂直滚动,显示第一列(真),显示表头(伪)
  3. 滚动停止时,再分别同步表头(真)和第一列(真)的transform,真伪不同时不存在,一方显示,另一方隐藏

但在移动端可能会同时出现水平和垂直的滚动,这样可能还是不能避免实时同步的问题。为了禁止两种滚动的同时出现,我引入了better-scroll,它默认是只进行单向的滚动。

至此基本就解决了固定头列的不同步的问题。但前面说到,我们需要做一个完全一致的假的第一列和表头,而使用的框架是vue,如果写到template中,就会比较冗余。

能想到的解决方法有:

  1. JSXrender,这样子就能用暂存渲染的逻辑并进行复用
  2. 假的DOM可以直接用API clone出来

2个方法可能会涉及到管理假DOM的内容更新问题,维护比较麻烦。第1个方法是可行的,但由于之前我并没有将表格抽取为一个组件,直接重写会比较麻烦而且本身也应该是作为一个独立组件存在。所以我选择将表格抽取成一个与业务关系不大的组件,提供headercell两个scoped slot去让使用者按业务要求进行渲染。

作为一个独立组件后,即便是写template也不会太冗余。

展示DEMO

这个实现目前我感觉有以下不太完美的地方:

  1. 直接操作DOM
  2. 没有使用table元素
  3. 表格需要使用者指定宽高
  4. 当一轴滚动时,另一轴会同时出现微小的移动

在实现过程中处理了better-scroll的一个bug,当设置是只允许同时出现一个方向的滚动时,它的惯性计算还是会出现两个方向,所以我作了个touchEnd时,让startX等于x或者startY等于y的处理

This article was last updated on days ago, and the information described in the article may have changed.