Skip to content

Commit

Permalink
feat(VirtualScroll): improve scroll update quasarframework#9329
Browse files Browse the repository at this point in the history
  • Loading branch information
pdanpdan committed Nov 23, 2022
1 parent ec84052 commit 521a05b
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 18 deletions.
2 changes: 1 addition & 1 deletion ui/dev/src/pages/components/virtual-scroll-2.vue
Expand Up @@ -5,7 +5,7 @@
<div class="text-h6 q-my-lg q-pa-lg bg-grey-10 text-white" style="height: 500px">
Before list
</div>
<q-virtual-scroll component="q-list" :items="heavyList" separator scroll-target="body">
<q-virtual-scroll component="q-list" :items="heavyList" separator scroll-target="body" :virtual-scroll-item-size="48">
<template v-slot="{ item, index }">
<q-item
v-if="(index % 3) === 0"
Expand Down
2 changes: 1 addition & 1 deletion ui/dev/src/pages/components/virtual-scroll-3.vue
Expand Up @@ -11,7 +11,7 @@
component="q-list"
:items-size="size"
:items-fn="getItems"
:virtual-scroll-item-size="5"
:virtual-scroll-item-size="57"
separator
ref="list"
>
Expand Down
85 changes: 69 additions & 16 deletions ui/src/mixins/virtual-scroll.js
@@ -1,6 +1,35 @@
import debounce from '../utils/debounce.js'
import { rtlHasScrollBug } from '../utils/scroll.js'

function scrollDebounce (fn) {
let timeoutWait
let throttleCounter = 0

const fnDebounced = () => {
timeoutWait = void 0
throttleCounter = 0
fn()
}

function debounced () {
throttleCounter++
clearTimeout(timeoutWait)
timeoutWait = setTimeout(fnDebounced, 200)

if (throttleCounter > 50) {
throttleCounter = 0
fn(true)
}
}

debounced.cancel = () => {
clearTimeout(timeoutWait)
timeoutWait = void 0
throttleCounter = 0
}

return debounced
}

const aggBucketSize = 1000

const scrollToEdges = [
Expand Down Expand Up @@ -300,13 +329,26 @@ export default {
)
},

__onVirtualScrollEvt () {
__onVirtualScrollEvt (throttled) {
const scrollEl = this.__getVirtualScrollTarget()

if (scrollEl === void 0 || scrollEl === null || scrollEl.nodeType === 8) {
return
}

if (this.$q.interaction.isKeyboard === true && this.$q.interaction.event !== null && typeof this.$q.interaction.event.key === 'string') {
const key = this.$q.interaction.event.key.toLowerCase()

if (key === 'home' || key === 'end') {
setScroll(
scrollEl,
key === 'home' ? 0 : 'end',
this.virtualScrollHorizontal,
this.$q.lang.rtl
)
}
}

const
scrollDetails = getScrollDetails(
scrollEl,
Expand Down Expand Up @@ -382,11 +424,13 @@ export default {
scrollEl,
scrollDetails,
toIndex,
offset
offset,
void 0,
throttled
)
},

__setVirtualScrollSliceRange (scrollEl, scrollDetails, toIndex, offset, align) {
__setVirtualScrollSliceRange (scrollEl, scrollDetails, toIndex, offset, align, throttled) {
const alignForce = typeof align === 'string' && align.indexOf('-force') > -1
const alignEnd = alignForce === true ? align.replace('-force', '') : align
const alignRange = alignEnd !== void 0 ? alignEnd : 'start'
Expand Down Expand Up @@ -416,13 +460,18 @@ export default {
rangeChanged === true &&
contentEl !== void 0 &&
contentEl !== activeElement &&
contentEl.contains(activeElement) === true
contentEl.contains(activeElement) === true &&
contentEl.qVsFocusout !== true
) {
contentEl.qVsFocusout = true
contentEl.addEventListener('focusout', this.__onBlurRefocusFn)

setTimeout(() => {
contentEl !== void 0 && contentEl.removeEventListener('focusout', this.__onBlurRefocusFn)
})
if (contentEl !== void 0) {
contentEl.removeEventListener('focusout', this.__onBlurRefocusFn)
contentEl.qVsFocusout = false
}
}, 500)
}

setOverflowAnchor(contentEl, toIndex - from)
Expand All @@ -441,15 +490,17 @@ export default {
this.virtualScrollPaddingBefore = sumSize(this.virtualScrollSizesAgg, this.virtualScrollSizes, 0, from)
this.virtualScrollPaddingAfter = sumSize(this.virtualScrollSizesAgg, this.virtualScrollSizes, this.virtualScrollSliceRange.to, this.virtualScrollLength)

requestAnimationFrame(() => {
cancelAnimationFrame(this.vsRangeToAnimationFrame)
this.vsRangeToAnimationFrame = requestAnimationFrame(() => {
if (this.virtualScrollSliceRange.to !== to && this.prevScrollStart === scrollDetails.scrollStart) {
this.virtualScrollSliceRange = { from: this.virtualScrollSliceRange.from, to }
this.virtualScrollPaddingAfter = sumSize(this.virtualScrollSizesAgg, this.virtualScrollSizes, to, this.virtualScrollLength)
}
})
}

requestAnimationFrame(() => {
cancelAnimationFrame(this.vsScrollAnimationFrame)
this.vsScrollAnimationFrame = requestAnimationFrame(() => {
// if the scroll was changed give up
// (another call to __setVirtualScrollSliceRange before animation frame)
if (this.prevScrollStart !== scrollDetails.scrollStart) {
Expand Down Expand Up @@ -482,12 +533,14 @@ export default {

this.prevScrollStart = scrollPosition

setScroll(
scrollEl,
scrollPosition,
this.virtualScrollHorizontal,
this.$q.lang.rtl
)
if (throttled !== true || (scrollEl === window && this.$q.platform.is.ios !== true)) {
setScroll(
scrollEl,
scrollPosition,
this.virtualScrollHorizontal,
this.$q.lang.rtl
)
}

this.__emitScroll(toIndex)
})
Expand Down Expand Up @@ -716,7 +769,7 @@ export default {
},

beforeMount () {
this.__onVirtualScrollEvt = debounce(this.__onVirtualScrollEvt, this.$q.platform.is.ios === true ? 120 : 35)
this.__onVirtualScrollEvt = scrollDebounce(this.__onVirtualScrollEvt)
this.__setVirtualScrollSize()
},

Expand Down

0 comments on commit 521a05b

Please sign in to comment.