动态高度列表的 scrollToRow 结果关闭

scrollToRow result is off for dynamic height list

首先,让我提供一些关于反应虚拟化用​​例的背景知识。我将它与 react-pdf 的 v2.0 beta 版本一起使用,以便构建一个可以更有效地处理 displaying/rendering 包含大量页面的 pdf 文档的 pdf 查看器。一个重要的要求是 pdf 查看器是完全响应的,并且可以处理包含可能具有不同高度的页面的文档。

我已经设法将这两个包结合起来(有几个与 react-pdf 相关的小问题),但是有一些事情并没有像我期望的那样工作。最值得注意的是,滚动到特定行(即页面)并不能很好地工作。举个例子,如果我试图从页面索引 0 滚动到页面索引 81(大约是我的 152 页测试 pdf 的中间),我最终会到达所需页面和下一页之间的某个中间位置。如果我尝试滚动到最后一页索引 (p.i. 151),我会在倒数第二页结束。

我正在使用 WindowScrollerAutoSizerCellMeasurerList 的组合来创建我的查看器(我省略了直接无关紧要的部分) :

class Viewer extends Component {
    constructor(props) {
        super(props);

        this.state = {pdf: null, scale: 1.2};
        this._cache = new CellMeasurerCache({defaultHeight: 768, fixedWidth: true});
    }

    ...

    handleResize() {
        this._cache.clearAll();     // Reset the cached measurements for all cells
    }

    updatePageIndex(index) {
        this._cache.clearAll();
        this._list.scrollToRow(index);
    }

    rowRenderer({key, index, style, parent}) {
        return (
            <CellMeasurer cache={this._cache} columnIndex={0} key={key} parent={parent} rowIndex={index}>
                {
                    ({measure}) => (
                        <div style={style}>
                            <Page
                                onLoadSuccess={measure}
                                renderTextLayer={false}
                                pdf={this.state.pdf}
                                pageNumber={index + 1}
                                scale={this.state.scale} />
                        </div>
                    )
                }
            </CellMeasurer>
        );
    }

    render() {
        ...
        <Document
            file="./some_pdf_document.pdf"
            loading={<Loader />}
            error={this.renderError()}
            onLoadSuccess={this.onDocumentLoadSuccess.bind(this)}
        >
            <WindowScroller onResize={this.handleResize.bind(this)}>
                {
                    ({height, isScrolling, onChildScroll, scrollTop}) => (
                        <AutoSizer disableHeight>
                            {
                                ({width}) => (
                                    <List
                                        autoheight
                                        height={height}
                                        width={width}
                                        isScrolling={isScrolling}
                                        onScroll={onChildScroll}
                                        scrollToAlignment="start"
                                        scrollTop={scrollTop}
                                        overscanRowCount={5}
                                        rowCount={this.state.pdf.numPages}
                                        deferredMeasurementCache={this._cache}
                                        rowHeight={this._cache.rowHeight}
                                        rowRenderer={this.rowRenderer.bind(this)}
                                        style={{outline: 'none'}}
                                        ref={ref => this._list = ref} />
                                )
                            }
                        </AutoSizer>
                    )
                }
            </WindowScroller>
        </Document>
    }

}
...

我在 updatePageIndex() 中所做的是正确的还是还缺少什么?

我觉得上面有一两个误区。首先,调用 cache.clearAll() 将清除所有测量值——需要 CellMeasurer 在下一次渲染时重新计算它们。除非发生某些使这些测量无效的更改(从您的描述来看似乎不是这种情况),否则您不想这样做。仅当由于浏览器宽度调整大小可能影响环绕文本内容的高度等变化导致测量无效时,才应调用此方法。

其次,如果您确实需要调用cache.clearAll(),那么您想要调用list.recomputeRowHeights() . CellMeasurer 缓存 measurements(大小)和 List 缓存 positions(偏移量)。这使得数据可以重新排序(例如排序)而无需重新测量。排序后所需要做的就是 List 重新计算其位置。

查看 this code snippet from a Twitter-like demo app 我使用 react-virtualized 构建的示例,了解如何完成此操作。

如果以上信息不能帮助您解决问题,请在 CodeSandbox 或类似网站上发表评论和重现,我会查看。

我让它正常工作(即滚动到右侧页面)的唯一方法是使用 List 组件的 scrollToIndex 属性。奇怪的是,将其设置为某个行索引确实会滚动到正确的页面。

使用 scrollToIndex 的唯一问题是它不允许您向上滚动超过滚动索引。我的解决方法是在滚动完成后将索引设置回 -1。但是,如果我这样做太快,scrollToIndex 也会滚动到错误的行。我设法解决这个问题的唯一方法是使用 setTimeout() 将索引设置为 -1。非常 hacky,但它可以解决问题。我尝试了使用 componentDidUpdate() 和承诺的其他方法,但其中 none 对我有用。