Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

useVirtualList 最后一个元素的高度不能超过 container 的高度 #2496

Open
weekbin opened this issue Mar 7, 2024 · 1 comment
Assignees

Comments

@weekbin
Copy link

weekbin commented Mar 7, 2024

当最后一个元素的高度比 Container 还高时,getOffset 的返回值会等于 list.length

  const getOffset = (scrollTop: number) => {
    if (isNumber(itemHeightRef.current)) {
      return Math.floor(scrollTop / itemHeightRef.current) + 1;
    }
    let sum = 0;
    let offset = 0;
    for (let i = 0; i < list.length; i++) {
      const height = itemHeightRef.current(i, list[i]);
      sum += height;
      if (sum >= scrollTop) {
        offset = i;
        break;
      }
    }
    return offset + 1;
  };

在计算 visibleCount 时,会导致 getVisibleCount 的返回值为 -list.length

  const getVisibleCount = (containerHeight: number, fromIndex: number) => {
    if (isNumber(itemHeightRef.current)) {
      return Math.ceil(containerHeight / itemHeightRef.current);
    }

    let sum = 0;
    let endIndex = 0;
    for (let i = fromIndex; i < list.length; i++) {
      const height = itemHeightRef.current(i, list[i]);
      sum += height;
      endIndex = i;
      if (sum >= containerHeight) {
        break;
      }
    }
    return endIndex - fromIndex;
  };

最终会导致 start = list.length,end = overscan,会取不到元素进行渲染。

const start = Math.max(0, offset - overscan); // 起始位置
const end = Math.min(list.length, offset + visibleCount + overscan); // 结束位置

建议在 getVisibleCount 返回时增加判断 endIndex - fromIndex < 0 ? 0 : endIndex - fromIndex,
这样在最后一个元素高度比 Contianer 还大时,仍然可以保证正常的渲染。

@weekbin
Copy link
Author

weekbin commented Mar 7, 2024

用官网的例子改了改就可以复现。

/**
 * title: Dynamic item height
 * desc: Specify item height dynamically.
 *
 * title.zh-CN: 动态元素高度
 * desc.zh-CN: 动态指定每个元素的高度
 */

import React, { useMemo, useRef } from 'react';
import { useVirtualList } from 'ahooks';

export default () => {
  const containerRef = useRef(null);
  const wrapperRef = useRef(null);

  const originalList = useMemo(() => Array.from(Array(200).keys()), []);

  const [value, onChange] = React.useState<number>(0);

  const [list, scrollTo] = useVirtualList(originalList, {
    containerTarget: containerRef,
    wrapperTarget: wrapperRef,
    itemHeight: (i) => (i % 2 === 0 ? 428 : 310),
    overscan: 10,
  });

  return (
    <div>
      <div style={{ textAlign: 'right', marginBottom: 16 }}>
        <input
          style={{ width: 120 }}
          placeholder="line number"
          type="number"
          value={value}
          onChange={(e) => onChange(Number(e.target.value))}
        />
        <button
          style={{ marginLeft: 8 }}
          type="button"
          onClick={() => {
            scrollTo(Number(value));
          }}
        >
          scroll to
        </button>
      </div>
      <div ref={containerRef} style={{ height: '300px', overflow: 'auto', background: 'black' }}>
        <div ref={wrapperRef}>
          {list.map((ele) => (
            <div
              style={{
                height: ele.index % 2 === 0 ? 428 : 310,
                display: 'flex',
                justifyContent: 'center',
                alignItems: 'center',
                border: '1px solid #e8e8e8',
                background: ele.index % 2 === 0 ? 'green' : 'red'
              }}
              key={ele.index}
            >
              Row: {ele.data+1} size: {ele.index % 2 === 0 ? 'small' : 'large'}
            </div>
          ))}
        </div>
      </div>
    </div>
  );
};

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants