Skip to content

Commit

Permalink
perf(QScrollArea): prevent content re-rendering on scroll or mousemove (
Browse files Browse the repository at this point in the history
  • Loading branch information
rstoenescu committed Sep 11, 2024
1 parent 83ee715 commit be9133f
Show file tree
Hide file tree
Showing 2 changed files with 180 additions and 132 deletions.
258 changes: 126 additions & 132 deletions ui/src/components/scroll-area/QScrollArea.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { h, ref, computed, watch, withDirectives, onActivated, onDeactivated, onBeforeUnmount, getCurrentInstance } from 'vue'
import { h, ref, computed, watch, onActivated, onDeactivated, onBeforeUnmount, getCurrentInstance } from 'vue'

import useDark, { useDarkProps } from '../../composables/private.use-dark/use-dark.js'

import ScrollAreaControls from './ScrollAreaControls.js'
import QResizeObserver from '../resize-observer/QResizeObserver.js'
import QScrollObserver from '../scroll-observer/QScrollObserver.js'

Expand Down Expand Up @@ -97,110 +98,112 @@ export default createComponent({
+ (isDark.value === true ? ' q-scrollarea--dark' : '')
)

scroll.vertical.percentage = computed(() => {
const diff = scroll.vertical.size.value - container.vertical.value
if (diff <= 0) { return 0 }
const p = between(scroll.vertical.position.value / diff, 0, 1)
return Math.round(p * 10000) / 10000
})
scroll.vertical.thumbHidden = computed(() =>
(
(props.visible === null ? hover.value : props.visible) !== true
&& tempShowing.value === false
&& panning.value === false
) || scroll.vertical.size.value <= container.vertical.value + 1
)
scroll.vertical.thumbStart = computed(() =>
scroll.vertical.percentage.value * (container.vertical.value - scroll.vertical.thumbSize.value)
)
scroll.vertical.thumbSize = computed(() =>
Math.round(
between(
container.vertical.value * container.vertical.value / scroll.vertical.size.value,
getMinThumbSize(container.vertical.value),
container.vertical.value
Object.assign(scroll.vertical, {
percentage: computed(() => {
const diff = scroll.vertical.size.value - container.vertical.value
if (diff <= 0) { return 0 }
const p = between(scroll.vertical.position.value / diff, 0, 1)
return Math.round(p * 10000) / 10000
}),

thumbHidden: computed(() =>
(
(props.visible === null ? hover.value : props.visible) !== true
&& tempShowing.value === false
&& panning.value === false
) || scroll.vertical.size.value <= container.vertical.value + 1
),

thumbStart: computed(() =>
scroll.vertical.percentage.value * (container.vertical.value - scroll.vertical.thumbSize.value)
),

thumbSize: computed(() =>
Math.round(
between(
container.vertical.value * container.vertical.value / scroll.vertical.size.value,
getMinThumbSize(container.vertical.value),
container.vertical.value
)
)
),

style: computed(() => {
return {
...props.thumbStyle,
...props.verticalThumbStyle,
top: `${ scroll.vertical.thumbStart.value }px`,
height: `${ scroll.vertical.thumbSize.value }px`
}
}),

thumbClass: computed(() =>
'q-scrollarea__thumb q-scrollarea__thumb--v absolute-right'
+ (scroll.vertical.thumbHidden.value === true ? ' q-scrollarea__thumb--invisible' : '')
),

barClass: computed(() =>
'q-scrollarea__bar q-scrollarea__bar--v absolute-right'
+ (scroll.vertical.thumbHidden.value === true ? ' q-scrollarea__bar--invisible' : '')
)
)
scroll.vertical.style = computed(() => {
return {
...props.thumbStyle,
...props.verticalThumbStyle,
top: `${ scroll.vertical.thumbStart.value }px`,
height: `${ scroll.vertical.thumbSize.value }px`
}
})
scroll.vertical.thumbClass = computed(() =>
'q-scrollarea__thumb q-scrollarea__thumb--v absolute-right'
+ (scroll.vertical.thumbHidden.value === true ? ' q-scrollarea__thumb--invisible' : '')
)
scroll.vertical.barClass = computed(() =>
'q-scrollarea__bar q-scrollarea__bar--v absolute-right'
+ (scroll.vertical.thumbHidden.value === true ? ' q-scrollarea__bar--invisible' : '')
)

scroll.horizontal.percentage = computed(() => {
const diff = scroll.horizontal.size.value - container.horizontal.value
if (diff <= 0) { return 0 }
const p = between(Math.abs(scroll.horizontal.position.value) / diff, 0, 1)
return Math.round(p * 10000) / 10000
})
scroll.horizontal.thumbHidden = computed(() =>
(
(props.visible === null ? hover.value : props.visible) !== true
&& tempShowing.value === false
&& panning.value === false
) || scroll.horizontal.size.value <= container.horizontal.value + 1
)
scroll.horizontal.thumbStart = computed(() =>
scroll.horizontal.percentage.value * (container.horizontal.value - scroll.horizontal.thumbSize.value)
)
scroll.horizontal.thumbSize = computed(() =>
Math.round(
between(
container.horizontal.value * container.horizontal.value / scroll.horizontal.size.value,
getMinThumbSize(container.horizontal.value),
container.horizontal.value
Object.assign(scroll.horizontal, {
percentage: computed(() => {
const diff = scroll.horizontal.size.value - container.horizontal.value
if (diff <= 0) { return 0 }
const p = between(Math.abs(scroll.horizontal.position.value) / diff, 0, 1)
return Math.round(p * 10000) / 10000
}),

thumbHidden: computed(() =>
(
(props.visible === null ? hover.value : props.visible) !== true
&& tempShowing.value === false
&& panning.value === false
) || scroll.horizontal.size.value <= container.horizontal.value + 1
),

thumbStart: computed(() =>
scroll.horizontal.percentage.value * (container.horizontal.value - scroll.horizontal.thumbSize.value)
),

thumbSize: computed(() =>
Math.round(
between(
container.horizontal.value * container.horizontal.value / scroll.horizontal.size.value,
getMinThumbSize(container.horizontal.value),
container.horizontal.value
)
)
),

style: computed(() => {
return {
...props.thumbStyle,
...props.horizontalThumbStyle,
[ proxy.$q.lang.rtl === true ? 'right' : 'left' ]: `${ scroll.horizontal.thumbStart.value }px`,
width: `${ scroll.horizontal.thumbSize.value }px`
}
}),

thumbClass: computed(() =>
'q-scrollarea__thumb q-scrollarea__thumb--h absolute-bottom'
+ (scroll.horizontal.thumbHidden.value === true ? ' q-scrollarea__thumb--invisible' : '')
),

barClass: computed(() =>
'q-scrollarea__bar q-scrollarea__bar--h absolute-bottom'
+ (scroll.horizontal.thumbHidden.value === true ? ' q-scrollarea__bar--invisible' : '')
)
)
scroll.horizontal.style = computed(() => {
return {
...props.thumbStyle,
...props.horizontalThumbStyle,
[ proxy.$q.lang.rtl === true ? 'right' : 'left' ]: `${ scroll.horizontal.thumbStart.value }px`,
width: `${ scroll.horizontal.thumbSize.value }px`
}
})
scroll.horizontal.thumbClass = computed(() =>
'q-scrollarea__thumb q-scrollarea__thumb--h absolute-bottom'
+ (scroll.horizontal.thumbHidden.value === true ? ' q-scrollarea__thumb--invisible' : '')
)
scroll.horizontal.barClass = computed(() =>
'q-scrollarea__bar q-scrollarea__bar--h absolute-bottom'
+ (scroll.horizontal.thumbHidden.value === true ? ' q-scrollarea__bar--invisible' : '')
)

const mainStyle = computed(() => (
scroll.vertical.thumbHidden.value === true && scroll.horizontal.thumbHidden.value === true
? props.contentStyle
: props.contentActiveStyle
))

const thumbVertDir = [ [
TouchPan,
e => { onPanThumb(e, 'vertical') },
void 0,
{ vertical: true, ...panOpts }
] ]

const thumbHorizDir = [ [
TouchPan,
e => { onPanThumb(e, 'horizontal') },
void 0,
{ horizontal: true, ...panOpts }
] ]

function getScroll () {
const info = {}

Expand Down Expand Up @@ -329,14 +332,6 @@ export default createComponent({
}
}

function onVerticalMousedown (evt) {
onMousedown(evt, 'vertical')
}

function onHorizontalMousedown (evt) {
onMousedown(evt, 'horizontal')
}

function startTimer () {
tempShowing.value = true

Expand Down Expand Up @@ -431,6 +426,32 @@ export default createComponent({
}
})

const store = {
scroll,

thumbVertDir: [ [
TouchPan,
e => { onPanThumb(e, 'vertical') },
void 0,
{ vertical: true, ...panOpts }
] ],

thumbHorizDir: [ [
TouchPan,
e => { onPanThumb(e, 'horizontal') },
void 0,
{ horizontal: true, ...panOpts }
] ],

onVerticalMousedown (evt) {
onMousedown(evt, 'vertical')
},

onHorizontalMousedown (evt) {
onMousedown(evt, 'horizontal')
}
}

return () => {
return h('div', {
class: classes.value,
Expand Down Expand Up @@ -463,39 +484,12 @@ export default createComponent({
onResize: updateContainer
}),

h('div', {
class: scroll.vertical.barClass.value,
style: [ props.barStyle, props.verticalBarStyle ],
'aria-hidden': 'true',
onMousedown: onVerticalMousedown
}),

h('div', {
class: scroll.horizontal.barClass.value,
style: [ props.barStyle, props.horizontalBarStyle ],
'aria-hidden': 'true',
onMousedown: onHorizontalMousedown
}),

withDirectives(
h('div', {
ref: scroll.vertical.ref,
class: scroll.vertical.thumbClass.value,
style: scroll.vertical.style.value,
'aria-hidden': 'true'
}),
thumbVertDir
),

withDirectives(
h('div', {
ref: scroll.horizontal.ref,
class: scroll.horizontal.thumbClass.value,
style: scroll.horizontal.style.value,
'aria-hidden': 'true'
}),
thumbHorizDir
)
h(ScrollAreaControls, {
store,
barStyle: props.barStyle,
verticalBarStyle: props.verticalBarStyle,
horizontalBarStyle: props.horizontalBarStyle
})
])
}
}
Expand Down
54 changes: 54 additions & 0 deletions ui/src/components/scroll-area/ScrollAreaControls.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { h, withDirectives } from 'vue'

import { createComponent } from '../../utils/private.create/create.js'

/**
* We are using a sub-component to avoid unnecessary re-renders
* of the QScrollArea content when the scrollbars are interacted with.
*/
export default createComponent({
props: [
'store',
'barStyle',
'verticalBarStyle',
'horizontalBarStyle'
],

setup (props) {
return () => ([
h('div', {
class: props.store.scroll.vertical.barClass.value,
style: [ props.barStyle, props.verticalBarStyle ],
'aria-hidden': 'true',
onMousedown: props.store.onVerticalMousedown
}),

h('div', {
class: props.store.scroll.horizontal.barClass.value,
style: [ props.barStyle, props.horizontalBarStyle ],
'aria-hidden': 'true',
onMousedown: props.store.onHorizontalMousedown
}),

withDirectives(
h('div', {
ref: props.store.scroll.vertical.ref,
class: props.store.scroll.vertical.thumbClass.value,
style: props.store.scroll.vertical.style.value,
'aria-hidden': 'true'
}),
props.store.thumbVertDir
),

withDirectives(
h('div', {
ref: props.store.scroll.horizontal.ref,
class: props.store.scroll.horizontal.thumbClass.value,
style: props.store.scroll.horizontal.style.value,
'aria-hidden': 'true'
}),
props.store.thumbHorizDir
)
])
}
})

0 comments on commit be9133f

Please sign in to comment.