Skip to content

Commit

Permalink
feat: add part scroll
Browse files Browse the repository at this point in the history
  • Loading branch information
daybrush committed Jun 27, 2024
1 parent 92d7e93 commit 7623d49
Show file tree
Hide file tree
Showing 4 changed files with 294 additions and 114 deletions.
164 changes: 79 additions & 85 deletions packages/infinitegrid/src/Infinite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,16 @@ export interface InfiniteOptions {
defaultDirection?: "start" | "end";
}

export interface InfiniteItemPart {
key: string | number;
pos: number;
size: number;
}
export interface InfiniteItem {
key: string | number;
startOutline: number[];
endOutline: number[];
parts?: InfiniteItemPart[];
isVirtual?: boolean;
}

Expand Down Expand Up @@ -219,79 +225,6 @@ export class Infinite extends Component<InfiniteEvents> {
}
}

/**
* Visible item area according to scrollPos
* @param scrollPos
*/
public getVisibleArea(scrollPos: number): {
start: number;
end: number;
direction?: "end" | "start";
hasVirtualItems?: boolean;
isStart?: boolean;
isEnd?: boolean;
nextVisibleItems?: InfiniteItem[];
} {
const prevStartCursor = this.startCursor;
const prevEndCursor = this.endCursor;
const items = this.items;
const length = items.length;
const size = this.size;
const {
defaultDirection,
threshold,
} = this.options;
const isDirectionEnd = defaultDirection === "end";

if (!length) {
// no items
return {
start: -1,
end: -1,
direction: isDirectionEnd ? "end" : "start",
};
} else if (prevStartCursor === -1 || prevEndCursor === -1) {
const nextCursor = isDirectionEnd ? 0 : length - 1;

return {
start: nextCursor,
end: nextCursor,
direction: isDirectionEnd ? "end" : "start",
};
}

const endScrollPos = scrollPos + size;
const visibles = items.map((item) => {
const {
startOutline,
endOutline,
} = item;

if (!startOutline.length || !endOutline.length || isFlatOutline(startOutline, endOutline)) {
return false;
}
const startPos = Math.min(...startOutline);
const endPos = Math.max(...endOutline);

if (startPos - threshold <= endScrollPos && scrollPos <= endPos + threshold) {
return true;
}
return false;
});
let nextStartCursor = visibles.indexOf(true);
let nextEndCursor = visibles.lastIndexOf(true);

if (nextStartCursor === -1) {
nextStartCursor = prevStartCursor;
nextEndCursor = prevEndCursor;
}

return {
start: nextStartCursor,
end: nextEndCursor,
};
}

/**
* Call the requestAppend or requestPrepend event to fill the virtual items.
* @ko virtual item을 채우기 위해 requestAppend 또는 requestPrepend 이벤트를 호출합니다.
Expand Down Expand Up @@ -444,24 +377,85 @@ export class Infinite extends Component<InfiniteEvents> {
}
return this.items.slice(startCursor, endCursor + 1);
}
public getSize() {
return this.size;
}
public getItemByKey(key: string | number) {
return this.itemKeys[key];
}
/**
*
* @param scrollPos
* @returns
*/
public getRenderedVisibleItems(scrollPos?: number) {
let items = this.getVisibleItems();
public getItemPartByKey(partKey: string | number) {
let itemPart!: InfiniteItemPart;

this.items.forEach((item) => {
item.parts?.forEach((part) => {
if (part.key === partKey) {
itemPart = part;
}
});
});

if (!this.options.useRecycle && scrollPos != null) {
const area = this.getVisibleArea(scrollPos);
return itemPart;
}
public getScrollSize() {
const items = this.items;
const length = items.length;

if (area.start > -1) {
items = this.items.slice(area.start, area.end + 1);
}
if (!length) {
return 0;
}
return Math.max(0, ...items[length - 1].endOutline);
}
public getVisibleArea(scrollPos: number, direction = this.options.defaultDirection) {
const isDirectionEnd = direction === DIRECTION.END;
const visibleItems = this.getRenderedVisibleItems();

if (!visibleItems.length) {
return null;
}
const visibleItem = visibleItems[isDirectionEnd ? 0 : length - 1];
const itemPos = isDirectionEnd
? Math.min(...visibleItem.startOutline)
: Math.max(...visibleItem.endOutline);
let pos = itemPos;
let itemPart!: InfiniteItemPart;

if (isDirectionEnd) {
visibleItems.forEach((item) => {
item.parts?.forEach((part) => {
if (itemPart && itemPart.pos >= part.pos) {
return;
}
if (pos < part.pos && part.pos <= scrollPos) {
itemPart = part;
pos = part.pos;
}
});
});
} else {
visibleItems.forEach((item) => {
item.parts?.forEach((part) => {
const endPos = part.pos + part.size;

if (itemPart && itemPart.pos + itemPart.size <= endPos) {
return;
}

if (pos > endPos && endPos >= scrollPos) {
itemPart = part;
pos = endPos;
}
});
});
}

return {
item: visibleItem,
part: itemPart,
};
}
public getRenderedVisibleItems() {
const items = this.getVisibleItems();

const rendered = items.map(({ startOutline, endOutline }) => {
const length = startOutline.length;

Expand Down
70 changes: 61 additions & 9 deletions packages/infinitegrid/src/InfiniteGrid.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import {
import { GroupManager } from "./GroupManager";
import {
Infinite,
InfiniteItem,
InfiniteItemPart,
OnInfiniteChange,
OnInfiniteRequestAppend,
OnInfiniteRequestPrepend,
Expand Down Expand Up @@ -685,6 +687,13 @@ class InfiniteGrid<Options extends InfiniteGridOptions = InfiniteGridOptions> ex
isVirtual: type === GROUP_TYPE.VIRTUAL,
startOutline: outlines.start,
endOutline: outlines.end,
parts: grid.getItems().map((item) => {
return {
key: item.key,
pos: item.computedContentPos,
size: item.computedContentSize,
};
}),
};
}));
}
Expand Down Expand Up @@ -866,20 +875,63 @@ class InfiniteGrid<Options extends InfiniteGridOptions = InfiniteGridOptions> ex

private _onRenderComplete = ({ isResize, mounted, updated, direction }: OnPickedRenderComplete): void => {
const infinite = this.infinite;
const prevRenderedGroups = infinite.getRenderedVisibleItems(this.scrollManager.getScrollPos()!);
const length = prevRenderedGroups.length;
const scrollManager = this.scrollManager;
const scrollPos = scrollManager.getRelativeScrollPos()!;
const prevScrollSize = infinite.getScrollSize();
const prevContainerSize = infinite.getSize();
const prevVisibleArea = infinite.getVisibleArea(scrollPos, direction);
const isDirectionEnd = direction === DIRECTION.END;



this._syncInfinite();

if (length) {
const prevStandardGroup = prevRenderedGroups[isDirectionEnd ? 0 : length - 1];
const nextStandardGroup = infinite.getItemByKey(prevStandardGroup.key);
const offset = isDirectionEnd
? Math.min(...nextStandardGroup.startOutline) - Math.min(...prevStandardGroup.startOutline)
: Math.max(...nextStandardGroup.endOutline) - Math.max(...prevStandardGroup.endOutline);
if (prevVisibleArea) {
const prevPart = prevVisibleArea.part;
const prevItem = prevVisibleArea.item;
let nextPart!: InfiniteItemPart;
let nextItem!: InfiniteItem;

if (prevPart) {
nextPart = infinite.getItemPartByKey(prevPart.key);
}
if (prevItem) {
nextItem = infinite.getItemByKey(prevItem.key);
}

if (nextPart || nextItem) {
let prevPos = 0;
let nextPos = 0;

if (nextPart) {
nextPos = nextPart.pos + (isDirectionEnd ? 0 : nextPart.size);
prevPos = prevPart.pos + (isDirectionEnd ? 0 : prevPart.size);
} else {
const prevStartPos = Math.min(...prevItem.startOutline);
const prevEndPos = Math.max(...prevItem.endOutline);
const nextStartPos = Math.min(...nextItem.startOutline);
const nextEndPos = Math.max(...nextItem.endOutline);

nextPos = isDirectionEnd ? nextStartPos : nextEndPos;
prevPos = isDirectionEnd ? prevStartPos : prevEndPos;
}
let offset = nextPos - prevPos;

// If reversed, scroll size (case where container size is reduced)
if (offset < 0) {
const nextScrollSize = infinite.getScrollSize();
const nextContainerSize = infinite.getSize();
const endOffset = Math.max(scrollPos - Math.max(0, prevScrollSize - prevContainerSize), 0);
const nextScollPos
= Math.min(scrollPos, Math.max(0, nextScrollSize - nextContainerSize))
+ endOffset;

// The scroll size is restored to the extent that it has been reduced.
offset += scrollPos - nextScollPos;
}

this.scrollManager.scrollBy(offset);
this.scrollManager.scrollBy(offset);
}
}

const completeMounted = (mounted as InfiniteGridItem[]).filter((item) => item.type !== ITEM_TYPE.LOADING);
Expand Down
Loading

0 comments on commit 7623d49

Please sign in to comment.