arco-design / arco-design-vue

A Vue.js 3 UI Library based on Arco Design
https://arco.design/vue
MIT License
2.69k stars 526 forks source link

Tree滚动错位问题 #758

Closed liupan1890 closed 2 years ago

liupan1890 commented 2 years ago

Basic Info

What is expected?

正确显示

Steps to reproduce

Tree绑定大量数据后,显示时错位,不知道是否和现实的数据有关,显示的是文件名,长度有长有短

err3

 <div style="height: 360px">
      <a-tree
        ref="treeenmpty"
        blockNode
        checkable
        :selectable="false"
        :check-strictly="true"
        :data="TreeData"
        :virtualListProps="{
          height: 360
        }"
        :style="{ height: '360px' }"
      >
        <template #icon>
          <i class="iconfont iconfile-folder" />
        </template>
      </a-tree>
    </div>
liupan1890 commented 2 years ago
<div class="arco-tree arco-tree-checkable arco-tree-size-medium" style="height: 360px">
    <div style="overflow-y: auto; overflow-anchor: none; max-height: 360px">
      <div style="height: 1.17183e6px; position: relative; overflow: hidden; z-index: 0">
        <div style="display: flex; flex-direction: column; transform: translateY(439px); position: absolute; left: 0px; right: 0px; top: 0px">
          <div class="arco-tree-node arco-tree-node-is-leaf arco-tree-node-expanded arco-tree-node-disabled-selectable">...</div>
          <div class="arco-tree-node arco-tree-node-is-leaf arco-tree-node-expanded arco-tree-node-disabled-selectable">...</div>
          <div class="arco-tree-node arco-tree-node-is-leaf arco-tree-node-expanded arco-tree-node-disabled-selectable">...</div>
          <div class="arco-tree-node arco-tree-node-is-leaf arco-tree-node-expanded arco-tree-node-disabled-selectable">...</div>
          ...
        </div>
      </div>
    </div>
  </div>

这是dom树 如截图中,滚动条在顶部时,

<div class="arco-tree arco-tree-checkable arco-tree-size-medium" style="height: 360px">
    <div style="overflow-y: auto; overflow-anchor: none; max-height: 360px">
      <div style="height: 1.17183e6px; position: relative; overflow: hidden; z-index: 0">

外部的3层,都没问题,显示都是正确的。只有

<div style="display: flex; flex-direction: column; transform: translateY(439px); position: absolute; left: 0px; right: 0px; top: 0px">

这一层,有问题,位置/高度不对(或者说里面的<div class="arco-tree-node 数量不对,显示的太少,不足以占满)

感觉应该是arco的BUG

flsion commented 2 years ago

@liupan1890 好的,我们会排查一下

liupan1890 commented 2 years ago

在v2.8.0

web-vue\lib_components\virtual-list\virtual-list.vue_vue&type=script&lang.js

100行:    const visibleCount = vue.computed(() => Math.ceil(viewportHeight.value / itemHeight.value));

web-vue\lib_components\virtual-list\hooks\use-item-height.js

  vue.watch(itemLength, () => {
    if (itemLength.value && !estimatedItemHeight.value) {
      estimatedItemHeight.value = Object.entries(itemHeightCacheMap.value).reduce((sum, [, height]) => sum + height, 0) / itemLength.value;
    }
  });

由以上代码可知: 1.初始化后会计算出itemHeightCacheMap里全部item的平均高度(itemHeight) 2.根据这个平均高度,确定显示多少行记录

这个逻辑明显是错误的

在我的实际使用时,1000行记录,高度不固定(文件名很长时会自动换行显示成2行文字)(不长时显示为一行文字) 因为高度不固定,此时计算出的平均高度,也不准确。进而导致visibleCount太少,最后导致这个Tree滚动错位问题

--

  1. tree 是支持非固定高度的
  2. 应该统计当前要显示的所有行的准确的总高度,少于viewportHeight时加行,一直到占满viewportHeight

平均高度?这个思路不准确, 1.因为行高滚动后可能会变,变动小或不变动时,就能正确显示,当变动大时就不能正确显示 2.因为数据比较多时,如果其中有一半行高度特别高,这种情况下,滚动时,因为高度被平均,必然会错位

举例: 1-100行 高度都是32px 101-200 高度是56px 假设viewportHeight=440px,完整滚动一遍后,计算出平均高度(itemHeight)=44px,visibleCount=10 此时,滚动到1-100行位置时,必然因为10*32px=320px导致无法占满viewportHeight,上面或这下面会有大段空白

--

简单的修正办法,是强制增加visibleCount (+20)

100行:    const visibleCount = vue.computed(() => Math.ceil(viewportHeight.value / itemHeight.value)+20);

正确的修正办法,是重新规划visibleCount的计算思路

--

我就是在等着官方更新期间,简单的翻翻代码,可能理解的不够全面,但在我一个简单的文件树页面,确实发现了这个问题,这个逻辑

@Flsion

xiaosongmao123 commented 2 years ago

因为List控件虚拟化时,也是使用的web-vue\lib_components\virtual-list 由此可见,此BUG也会导致List组件的显示错位。

List / Select / Table / Tree

这4个组件都是基于virtual-list的啊,arco-design-vue已经发布半年了,不可能才发现有这个BUG吧?

平均高度?

kirazxyun commented 2 years ago

你好,计算 visibleCount 确实比较粗暴一些,这个计算的出发点是想要尽量减少计算量,这样的计算方式对于行高没有太大差异的情况运行还是蛮好的,但是像同学提出的这种行高差异较大的情况确实有问题,我们会再优化一波计算方式。

liupan1890 commented 2 years ago

@kirazxyun 为了性能,相对合理的,代码变动最小的,应该是取最小行高,而不是取平均行高

如上例, 最小行高是32px,依此计算出visibleCount =440/32=13.7行=14行 平均行高是44px,依此计算出visibleCount =440/44=10行=显示错误,有空白

采用平均行高时,440px的框,空白120px,空白30%,这个太明显了 采用最小行高,仅仅需要修改一行

estimatedItemHeight.value = Object.entries(itemHeightCacheMap.value).reduce((sum, [, height]) => sum + height, 0) / itemLength.value;
estimatedItemHeight.value = Object.entries(itemHeightCacheMap.value).reduce((sum, [, height]) => Math.min(sum ,height), 0);

逻辑没变。但可以修正BUG

liupan1890 commented 2 years ago

v2.9.0 List 底部空白

list