@@ -57,8 +57,8 @@ const emit = defineEmits<{
5757 ' approach-end' : []
5858}>()
5959
60- const rowHeight = ref (defaultItemHeight )
61- const colWidth = ref (defaultItemWidth )
60+ const itemHeight = ref (defaultItemHeight )
61+ const itemWidth = ref (defaultItemWidth )
6262const container = ref <HTMLElement | null >(null )
6363const { width, height } = useElementSize (container )
6464const { y : scrollY } = useScroll (container , {
@@ -67,7 +67,7 @@ const { y: scrollY } = useScroll(container, {
6767})
6868
6969const cols = computed (() =>
70- Math .min (Math .floor (width .value / colWidth .value ) || 1 , maxColumns )
70+ Math .min (Math .floor (width .value / itemWidth .value ) || 1 , maxColumns )
7171)
7272
7373const mergedGridStyle = computed <CSSProperties >(() => {
@@ -78,8 +78,8 @@ const mergedGridStyle = computed<CSSProperties>(() => {
7878 }
7979})
8080
81- const viewRows = computed (() => Math .ceil (height .value / rowHeight .value ))
82- const offsetRows = computed (() => Math .floor (scrollY .value / rowHeight .value ))
81+ const viewRows = computed (() => Math .ceil (height .value / itemHeight .value ))
82+ const offsetRows = computed (() => Math .floor (scrollY .value / itemHeight .value ))
8383const isValidGrid = computed (() => height .value && width .value && items ?.length )
8484
8585const state = computed <GridState >(() => {
@@ -101,28 +101,15 @@ const renderedItems = computed(() =>
101101 isValidGrid .value ? items .slice (state .value .start , state .value .end ) : []
102102)
103103
104- function spacerRowsToHeight (rows : number ): string {
105- return ` ${rows * rowHeight .value }px `
104+ function rowsToHeight (rows : number ): string {
105+ return ` ${( rows / cols . value ) * itemHeight .value }px `
106106}
107107
108- const topSpacerRows = computed (() => {
109- if (! isValidGrid .value ) return 0
110- return Math .floor (state .value .start / cols .value )
111- })
112-
113- const bottomSpacerRows = computed (() => {
114- if (! isValidGrid .value ) return 0
115-
116- const totalRows = Math .ceil (items .length / cols .value )
117- const renderedEndRow = Math .ceil (state .value .end / cols .value )
118- return Math .max (0 , totalRows - renderedEndRow )
119- })
120-
121108const topSpacerStyle = computed <CSSProperties >(() => ({
122- height: spacerRowsToHeight ( topSpacerRows .value )
109+ height: rowsToHeight ( state .value . start )
123110}))
124111const bottomSpacerStyle = computed <CSSProperties >(() => ({
125- height: spacerRowsToHeight ( bottomSpacerRows . value )
112+ height: rowsToHeight ( items . length - state . value . end )
126113}))
127114
128115whenever (
@@ -132,53 +119,19 @@ whenever(
132119 }
133120)
134121
135- const ITEM_SIZE_EPSILON_PX = 1
136-
137- /**
138- * Measures the effective grid row/column step (including `gap`) from rendered
139- * items to keep spacer math stable and prevent scroll jitter near the end.
140- */
141122function updateItemSize(): void {
142- if (! container .value ) return
143-
144- const itemElements = Array .from (
145- container .value .querySelectorAll (' [data-virtual-grid-item]' )
146- ).filter ((node ): node is HTMLElement => node instanceof HTMLElement )
147-
148- const firstItem = itemElements [0 ]
149-
150- if (! firstItem ?.clientHeight || ! firstItem ?.clientWidth ) return
151-
152- const nextRowItem = itemElements .find (
153- (item ) => item .offsetTop > firstItem .offsetTop
154- )
155-
156- const measuredRowHeight = nextRowItem
157- ? nextRowItem .offsetTop - firstItem .offsetTop
158- : firstItem .clientHeight
159-
160- const nextColItem = itemElements .find (
161- (item ) =>
162- item .offsetTop === firstItem .offsetTop &&
163- item .offsetLeft > firstItem .offsetLeft
164- )
165-
166- const measuredColWidth = nextColItem
167- ? nextColItem .offsetLeft - firstItem .offsetLeft
168- : firstItem .clientWidth
169-
170- if (
171- measuredRowHeight > 0 &&
172- Math .abs (rowHeight .value - measuredRowHeight ) >= ITEM_SIZE_EPSILON_PX
173- ) {
174- rowHeight .value = measuredRowHeight
175- }
176-
177- if (
178- measuredColWidth > 0 &&
179- Math .abs (colWidth .value - measuredColWidth ) >= ITEM_SIZE_EPSILON_PX
180- ) {
181- colWidth .value = measuredColWidth
123+ if (container .value ) {
124+ const firstItem = container .value .querySelector (' [data-virtual-grid-item]' )
125+
126+ // Don't update item size if the first item is not rendered yet
127+ if (! firstItem ?.clientHeight || ! firstItem ?.clientWidth ) return
128+
129+ if (itemHeight .value !== firstItem .clientHeight ) {
130+ itemHeight .value = firstItem .clientHeight
131+ }
132+ if (itemWidth .value !== firstItem .clientWidth ) {
133+ itemWidth .value = firstItem .clientWidth
134+ }
182135 }
183136}
184137const onResize = debounce (updateItemSize , resizeDebounce )
0 commit comments