Skip to content

Commit b2c5bb3

Browse files
committed
Optimizations
1 parent dcce0de commit b2c5bb3

File tree

1 file changed

+156
-141
lines changed

1 file changed

+156
-141
lines changed

src/components/VirtualScroller.vue

Lines changed: 156 additions & 141 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,41 @@ export default {
185185
itemHeight: 'setDirty',
186186
},
187187
188+
created () {
189+
this.$_ready = false
190+
this.$_startIndex = 0
191+
this.$_oldScrollTop = null
192+
this.$_oldScrollBottom = null
193+
this.$_offsetTop = 0
194+
this.$_height = 0
195+
this.$_scrollDirty = false
196+
this.$_updateDirty = false
197+
198+
const prerender = parseInt(this.prerender)
199+
if (prerender > 0) {
200+
this.visibleItems = this.items.slice(0, prerender)
201+
this.$_length = this.visibleItems.length
202+
this.$_endIndex = this.$_length - 1
203+
this.$_skip = true
204+
} else {
205+
this.$_endIndex = 0
206+
this.$_length = 0
207+
this.$_skip = false
208+
}
209+
},
210+
211+
mounted () {
212+
this.applyPageMode()
213+
this.$nextTick(() => {
214+
this.updateVisibleItems(true)
215+
this.$_ready = true
216+
})
217+
},
218+
219+
beforeDestroy () {
220+
this.removeWindowScroll()
221+
},
222+
188223
methods: {
189224
getScroll () {
190225
const el = this.$el
@@ -220,113 +255,120 @@ export default {
220255
},
221256
222257
updateVisibleItems (force = false) {
223-
const l = this.items.length
224-
const scroll = this.getScroll()
225-
const items = this.items
226-
const itemHeight = this.itemHeight
227-
let containerHeight, offsetTop
228-
if (scroll) {
229-
let startIndex = -1
230-
let endIndex = -1
231-
232-
const buffer = parseInt(this.buffer)
233-
const poolSize = parseInt(this.poolSize)
234-
const scrollTop = ~~(scroll.top / poolSize) * poolSize - buffer
235-
const scrollBottom = Math.ceil(scroll.bottom / poolSize) * poolSize + buffer
236-
237-
if (!force && ((scrollTop === this._oldScrollTop && scrollBottom === this._oldScrollBottom) || this._skip)) {
238-
this._skip = false
239-
return
240-
} else {
241-
this._oldScrollTop = scrollTop
242-
this._oldScrollBottom = scrollBottom
243-
}
244-
245-
// Variable height mode
246-
if (itemHeight === null) {
247-
const heights = this.heights
248-
let h
249-
let a = 0
250-
let b = l - 1
251-
let i = ~~(l / 2)
252-
let oldI
253-
254-
// Searching for startIndex
255-
do {
256-
oldI = i
257-
h = heights[i]
258-
if (h < scrollTop) {
259-
a = i
260-
} else if (i < l && heights[i + 1] > scrollTop) {
261-
b = i
258+
if (!this.$_updateDirty) {
259+
this.$_updateDirty = true
260+
this.$nextTick(() => {
261+
this.$_updateDirty = false
262+
263+
const l = this.items.length
264+
const scroll = this.getScroll()
265+
const items = this.items
266+
const itemHeight = this.itemHeight
267+
let containerHeight, offsetTop
268+
if (scroll) {
269+
let startIndex = -1
270+
let endIndex = -1
271+
272+
const buffer = parseInt(this.buffer)
273+
const poolSize = parseInt(this.poolSize)
274+
const scrollTop = ~~(scroll.top / poolSize) * poolSize - buffer
275+
const scrollBottom = Math.ceil(scroll.bottom / poolSize) * poolSize + buffer
276+
277+
if (!force && ((scrollTop === this.$_oldScrollTop && scrollBottom === this.$_oldScrollBottom) || this.$_skip)) {
278+
this.$_skip = false
279+
return
280+
} else {
281+
this.$_oldScrollTop = scrollTop
282+
this.$_oldScrollBottom = scrollBottom
262283
}
263-
i = ~~((a + b) / 2)
264-
} while (i !== oldI)
265-
i < 0 && (i = 0)
266-
startIndex = i
267-
268-
// For containers style
269-
offsetTop = i > 0 ? heights[i - 1] : 0
270-
containerHeight = heights[l - 1]
271-
272-
// Searching for endIndex
273-
for (endIndex = i; endIndex < l && heights[endIndex] < scrollBottom; endIndex++);
274-
if (endIndex === -1) {
275-
endIndex = items.length - 1
276-
} else {
277-
endIndex++
278-
// Bounds
279-
endIndex > l && (endIndex = l)
280-
}
281-
} else {
282-
// Fixed height mode
283-
startIndex = ~~(scrollTop / itemHeight)
284-
endIndex = Math.ceil(scrollBottom / itemHeight)
285284
286-
// Bounds
287-
startIndex < 0 && (startIndex = 0)
288-
endIndex > l && (endIndex = l)
289-
290-
offsetTop = startIndex * itemHeight
291-
containerHeight = l * itemHeight
292-
}
293-
294-
if (
295-
force ||
296-
this._startIndex !== startIndex ||
297-
this._endIndex !== endIndex ||
298-
this._offsetTop !== offsetTop ||
299-
this._height !== containerHeight ||
300-
this._length !== l
301-
) {
302-
this.keysEnabled = !(startIndex > this._endIndex || endIndex < this._startIndex)
303-
304-
this.itemContainerStyle = {
305-
height: containerHeight + 'px',
306-
}
307-
this.itemsStyle = {
308-
marginTop: offsetTop + 'px',
309-
}
285+
// Variable height mode
286+
if (itemHeight === null) {
287+
const heights = this.heights
288+
let h
289+
let a = 0
290+
let b = l - 1
291+
let i = ~~(l / 2)
292+
let oldI
293+
294+
// Searching for startIndex
295+
do {
296+
oldI = i
297+
h = heights[i]
298+
if (h < scrollTop) {
299+
a = i
300+
} else if (i < l && heights[i + 1] > scrollTop) {
301+
b = i
302+
}
303+
i = ~~((a + b) / 2)
304+
} while (i !== oldI)
305+
i < 0 && (i = 0)
306+
startIndex = i
307+
308+
// For containers style
309+
offsetTop = i > 0 ? heights[i - 1] : 0
310+
containerHeight = heights[l - 1]
311+
312+
// Searching for endIndex
313+
for (endIndex = i; endIndex < l && heights[endIndex] < scrollBottom; endIndex++);
314+
if (endIndex === -1) {
315+
endIndex = items.length - 1
316+
} else {
317+
endIndex++
318+
// Bounds
319+
endIndex > l && (endIndex = l)
320+
}
321+
} else {
322+
// Fixed height mode
323+
startIndex = ~~(scrollTop / itemHeight)
324+
endIndex = Math.ceil(scrollBottom / itemHeight)
325+
326+
// Bounds
327+
startIndex < 0 && (startIndex = 0)
328+
endIndex > l && (endIndex = l)
329+
330+
offsetTop = startIndex * itemHeight
331+
containerHeight = l * itemHeight
332+
}
310333
311-
if (this.delayPreviousItems) {
312-
// Add next items
313-
this.visibleItems = items.slice(this._startIndex, endIndex)
314-
// Remove previous items
315-
this.$nextTick(() => {
316-
this.visibleItems = items.slice(startIndex, endIndex)
317-
})
318-
} else {
319-
this.visibleItems = items.slice(startIndex, endIndex)
334+
if (
335+
force ||
336+
this.$_startIndex !== startIndex ||
337+
this.$_endIndex !== endIndex ||
338+
this.$_offsetTop !== offsetTop ||
339+
this.$_height !== containerHeight ||
340+
this.$_length !== l
341+
) {
342+
this.keysEnabled = !(startIndex > this.$_endIndex || endIndex < this.$_startIndex)
343+
344+
this.itemContainerStyle = {
345+
height: containerHeight + 'px',
346+
}
347+
this.itemsStyle = {
348+
marginTop: offsetTop + 'px',
349+
}
350+
351+
if (this.delayPreviousItems) {
352+
// Add next items
353+
this.visibleItems = items.slice(this.$_startIndex, endIndex)
354+
// Remove previous items
355+
this.$nextTick(() => {
356+
this.visibleItems = items.slice(startIndex, endIndex)
357+
})
358+
} else {
359+
this.visibleItems = items.slice(startIndex, endIndex)
360+
}
361+
362+
this.emitUpdate && this.$emit('update', startIndex, endIndex)
363+
364+
this.$_startIndex = startIndex
365+
this.$_endIndex = endIndex
366+
this.$_length = l
367+
this.$_offsetTop = offsetTop
368+
this.$_height = containerHeight
369+
}
320370
}
321-
322-
this.emitUpdate && this.$emit('update', startIndex, endIndex)
323-
324-
this._startIndex = startIndex
325-
this._endIndex = endIndex
326-
this._length = l
327-
this._offsetTop = offsetTop
328-
this._height = containerHeight
329-
}
371+
})
330372
}
331373
},
332374
@@ -341,8 +383,8 @@ export default {
341383
},
342384
343385
setDirty () {
344-
this._oldScrollTop = null
345-
this._oldScrollBottom = null
386+
this.$_oldScrollTop = null
387+
this.$_oldScrollBottom = null
346388
},
347389
348390
applyPageMode () {
@@ -364,56 +406,29 @@ export default {
364406
},
365407
366408
handleScroll () {
367-
this.updateVisibleItems()
409+
if (!this.$_scrollDirty) {
410+
this.$_scrollDirty = true
411+
requestAnimationFrame(() => {
412+
this.$_scrollDirty = false
413+
this.updateVisibleItems()
414+
})
415+
}
368416
},
369417
370418
handleResize () {
371419
this.$emit('resize')
372-
this._ready && this.updateVisibleItems()
420+
this.$_ready && this.updateVisibleItems()
373421
},
374422
375423
handleVisibilityChange (isVisible, entry) {
376-
if (this._ready && (isVisible || entry.boundingClientRect.width !== 0 || entry.boundingClientRect.height !== 0)) {
424+
if (this.$_ready && (isVisible || entry.boundingClientRect.width !== 0 || entry.boundingClientRect.height !== 0)) {
377425
this.$emit('visible')
378426
this.$nextTick(() => {
379427
this.updateVisibleItems()
380428
})
381429
}
382430
},
383431
},
384-
385-
created () {
386-
this._ready = false
387-
this._startIndex = 0
388-
this._oldScrollTop = null
389-
this._oldScrollBottom = null
390-
this._offsetTop = 0
391-
this._height = 0
392-
const prerender = parseInt(this.prerender)
393-
if (prerender > 0) {
394-
this.visibleItems = this.items.slice(0, prerender)
395-
this._length = this.visibleItems.length
396-
this._endIndex = this._length - 1
397-
this._skip = true
398-
} else {
399-
this._endIndex = 0
400-
this._length = 0
401-
this._skip = false
402-
}
403-
},
404-
405-
mounted () {
406-
this.applyPageMode()
407-
this.$nextTick(() => {
408-
this.updateVisibleItems(true)
409-
this._ready = true
410-
this.$nextTick(this.updateVisibleItems)
411-
})
412-
},
413-
414-
beforeDestroy () {
415-
this.removeWindowScroll()
416-
},
417432
}
418433
</script>
419434

0 commit comments

Comments
 (0)