Skip to content

Commit

Permalink
klicker: Replace getBoundingClientRect to reduce reflows
Browse files Browse the repository at this point in the history
  • Loading branch information
schneefux committed Jan 19, 2024
1 parent 9d5c95d commit 7b191d0
Show file tree
Hide file tree
Showing 4 changed files with 27 additions and 35 deletions.
22 changes: 9 additions & 13 deletions klicker/components/ui/b-scroll-spy.vue
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ export default defineComponent({
const stopCallbacks = ref<(() => void)[]>([])
const updateObservers = () => {
const topOffset = lgAndLarger.value ? 0 : navContainer.value!.getBoundingClientRect().bottom
const topOffset = lgAndLarger.value ? 0 : navContainer.value!.offsetHeight + navContainer.value!.offsetTop
const newStopCallbacks: (() => void)[] = []
for (const section of validSections.value) {
Expand Down Expand Up @@ -235,18 +235,14 @@ export default defineComponent({
const toc = ref<HTMLElement>()
const shouldStick = ref(false)
onMounted(() => {
const top = parseInt(window.getComputedStyle(navContainer.value!).top)
useIntersectionObserver(toc, ([{ isIntersecting }]) => {
if (isIntersecting) {
shouldStick.value = false
} else {
shouldStick.value = toc.value!.getBoundingClientRect().bottom < top
}
}, {
threshold: 0.0,
})
useIntersectionObserver(toc, ([ entry ]) => {
if (entry.isIntersecting) {
shouldStick.value = false
} else {
shouldStick.value = entry.boundingClientRect.bottom < navContainer.value!.offsetTop
}
}, {
threshold: 0.0,
})
return {
Expand Down
28 changes: 12 additions & 16 deletions klicker/components/ui/b-scrolling-list.vue
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ import { computed, defineComponent, onMounted, PropType, ref, nextTick } from 'v
import BScrollingDashboard, { ScrollEvent } from './b-scrolling-dashboard.vue'
import BDashboardCell from './b-dashboard-cell.vue'
import BShimmer from './b-shimmer.vue'
import { useMutationObserver } from '@vueuse/core'
import { useIntersectionObserver } from '@vueuse/core'
/**
* Horizontally-scrolling dashboard with fixed-size, virtually scrolling items
Expand Down Expand Up @@ -210,24 +210,20 @@ export default defineComponent({
}))
)
const getColumnStyle = (wrapper: HTMLElement) => {
const getColumnStyle = (wrapper: HTMLElement, firstItemRect?: DOMRectReadOnly) => {
const pxColumnWidth = parseInt(window.getComputedStyle(wrapper)
.getPropertyValue('grid-auto-columns')
.replace(/^.*?(\d+)px.*$/i, '$1'))
const pxGap = parseInt(window.getComputedStyle(wrapper).getPropertyValue('column-gap'))
let pxPerItem = props.cellColumns * pxColumnWidth + (props.cellColumns - 1) * pxGap
let columnsPerItem = props.cellColumns
const firstItem = Object.values(itemRefs.value)
.find(v => v != undefined)
if (firstItem != undefined) {
const firstItemElement = firstItem.$el
if (firstItemRect != undefined) {
// items may stretch their columns so prefer the actual width over the default values
pxPerItem = firstItemElement.getBoundingClientRect().width
pxPerItem = firstItemRect.width
// items may be squashed to take up fewer columns on smaller screens
columnsPerItem = parseInt(window.getComputedStyle(firstItemElement)
columnsPerItem = parseInt(window.getComputedStyle(firstItemRef.value!.$el)
.getPropertyValue('grid-column-end')
.replace(/(^.*?)(\d+)(.*$)/i, '$2'))
}
Expand All @@ -253,19 +249,19 @@ export default defineComponent({
pxGap: number,
}>()
const columnsPerItem = computed(() => columnStyle.value?.columnsPerItem ?? props.cellColumns)
const updateColumnWidths = () => {
const updateColumnWidths = (firstItemRect?: DOMRectReadOnly) => {
if (container.value?.wrapper == undefined) {
nextTick(() => updateColumnWidths())
nextTick(() => updateColumnWidths(firstItemRect))
return
}
columnStyle.value = getColumnStyle(container.value.wrapper)
columnStyle.value = getColumnStyle(container.value.wrapper, firstItemRect)
}
onMounted(() => updateColumnWidths())
const containerWrapper = computed(() => container.value?.wrapper)
useMutationObserver(containerWrapper, () => updateColumnWidths(), {
childList: true,
})
const firstItemRef = computed(() => Object.values(itemRefs.value).find(v => v != undefined))
// use intersection observer to get the boundingClientRect without forcing reflow
// https://toruskit.com/blog/how-to-get-element-bounds-without-reflow/
useIntersectionObserver(firstItemRef, ([ entry ]) => updateColumnWidths(entry.boundingClientRect))
const scrollSnap = ref(true)
let timeout: NodeJS.Timeout
Expand Down
6 changes: 3 additions & 3 deletions klicker/components/visualisations/v-barplot.vue
Original file line number Diff line number Diff line change
Expand Up @@ -185,15 +185,15 @@ export default defineComponent({
const pageSize = ref(values.value.length)
const wrapper = ref<HTMLElement>()
useResizeObserver(wrapper, () => window.requestAnimationFrame(() => {
useResizeObserver(wrapper, ([ entry ]) => {
if (wrapper.value == undefined) {
return
}
const pxAvailableWidth = wrapper.value.getBoundingClientRect().width - 100
const pxAvailableWidth = entry.contentRect.width - 100
const pxPerBar = 12
pageSize.value = Math.min(Math.max(Math.floor(pxAvailableWidth / pxPerBar), 1), values.value.length)
}))
})
return {
wrapper,
Expand Down
6 changes: 3 additions & 3 deletions klicker/components/visualisations/v-roll.vue
Original file line number Diff line number Diff line change
Expand Up @@ -175,12 +175,12 @@ export default defineComponent({
nextTick(() => pageSize.value = calculatePageSize())
}
useResizeObserver(wrapper, () => window.requestAnimationFrame(() => {
useResizeObserver(wrapper, ([ entry ]) => {
if (wrapper.value == undefined) {
return
}
const pxWholeWidth = wrapper.value.getBoundingClientRect().width
const pxWholeWidth = entry.contentRect.width
if (pxWholeWidth == pxPreviousWidth.value) {
// wrapper width is same, skip update
return
Expand All @@ -189,7 +189,7 @@ export default defineComponent({
pxPreviousWidth.value = pxWholeWidth
updatePageSize()
}))
})
const previousItemsLength = ref(body.value.length)
watch(() => body.value, () => {
Expand Down

0 comments on commit 7b191d0

Please sign in to comment.