Skip to content

Commit ad9968d

Browse files
committed
feat: 优化虚拟化渲染的滚动定位逻辑
1 parent 2d712c0 commit ad9968d

2 files changed

Lines changed: 37 additions & 5 deletions

File tree

packages/libro-core/src/components/libro-virtualized-render.tsx

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ export const LibroCellsOutputRender: React.FC<{
4343
const parentRef = useRef(null);
4444
const listRef = useRef<List | null>(null);
4545
const noEditorAreaRef = useRef<HTMLDivElement | null>(null);
46+
const scrollTopRef = useRef(0);
47+
const viewportHeightRef = useRef(0);
4648

4749
const [editorsOffset, setEditorsOffset] = useState<number[]>([]);
4850

@@ -128,13 +130,38 @@ export const LibroCellsOutputRender: React.FC<{
128130
}
129131

130132
libroView.model.onScrollToCellView((params: ScrollParams) => {
131-
// listRef.current!.scrollToRow(index);
132-
listRef.current!.scrollToCellPosition(params.cellIndex, params.cellOffset);
133+
const index = params.cellIndex;
134+
const offset = params.cellOffset || 0;
135+
const top = (editorsOffset[index] || 0) + offset;
136+
const height = (noEditorAreaHeight[index] || 0) + (editorAreaHeight[index] || 0);
137+
const viewportTop = scrollTopRef.current;
138+
const viewportHeight = viewportHeightRef.current;
139+
140+
if (viewportHeight && height) {
141+
if (top > viewportTop && top + height < viewportTop + viewportHeight) {
142+
// 在可视范围内就不需要滚动
143+
return;
144+
}
145+
if (top < viewportTop) {
146+
listRef.current!.scrollToCellPosition(index, offset);
147+
} else {
148+
const prevCellNoEditorHeight = noEditorAreaHeight[index - 1] || 0;
149+
const prevCellEditorHeight = editorAreaHeight[index - 1] || 0;
150+
const centerOffset =
151+
offset -
152+
viewportHeight / 2 -
153+
(prevCellNoEditorHeight - prevCellEditorHeight);
154+
listRef.current!.scrollToCellPosition(index, centerOffset); // 把目标 cell 的顶部放到视窗的中间位置
155+
}
156+
} else {
157+
listRef.current!.scrollToCellPosition(index, offset);
158+
}
133159
});
160+
134161
return () => {
135162
libroView.model.disposeScrollToCellViewEmitter();
136163
};
137-
}, [listRef, libroView]);
164+
}, [listRef, libroView, editorsOffset, noEditorAreaHeight, editorAreaHeight]);
138165

139166
// 在Cell的高度变化时,触发重新计算所有Cell的高度偏移值
140167
useEffect(() => {
@@ -166,6 +193,7 @@ export const LibroCellsOutputRender: React.FC<{
166193
return (
167194
<AutoSizer style={{ height: '100%', width: '100%' }} ref={parentRef}>
168195
{({ width, height }: { width: number; height: number }) => {
196+
viewportHeightRef.current = height;
169197
return (
170198
<List
171199
ref={listRef}
@@ -188,6 +216,8 @@ export const LibroCellsOutputRender: React.FC<{
188216
scrollTop: number;
189217
scrollingContainer: Element;
190218
}) => {
219+
scrollTopRef.current = scrollParams.scrollTop;
220+
viewportHeightRef.current = scrollParams.clientHeight;
191221
libroView.cellScrollEmitter.fire({
192222
scrollingContainer: scrollParams.scrollingContainer,
193223
scrollTop: scrollParams.scrollTop,

packages/libro-core/src/libro-model.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -422,19 +422,21 @@ export class LibroModel implements NotebookModel, DndListModel {
422422
target = target?.offsetParent as HTMLElement;
423423
offsetTop += target?.offsetTop || 0;
424424
}
425+
425426
if (target?.offsetParent?.parentElement && _targetheight) {
426427
const _height = target.offsetParent.parentElement.clientHeight;
427428
const _scrollTop = target.offsetParent.parentElement.scrollTop;
429+
428430
if (offsetTop > _scrollTop && offsetTop + _targetheight < _height + _scrollTop) {
429431
// 在可视范围内就不需要滚动
430432
return;
431433
}
432434
if (offsetTop < _scrollTop) {
433435
target.offsetParent.parentElement.scrollTop = offsetTop;
434436
} else {
435-
//目标cell的高度大于屏幕的高度
437+
// 目标 cell 的高度大于屏幕的高度
436438
if (_targetheight >= _height) {
437-
target.offsetParent.parentElement.scrollTop = offsetTop - _height / 2;
439+
target.offsetParent.parentElement.scrollTop = offsetTop - _height / 2; // 把目标 cell 的顶部放到视窗的中间位置
438440
} else {
439441
target.offsetParent.parentElement.scrollTop =
440442
offsetTop + _targetheight - _height + 30; // 加缓冲向上不贴底部

0 commit comments

Comments
 (0)