try to calculate list offset

This commit is contained in:
Nolan Lawson 2018-03-22 09:35:22 -07:00
parent ec73ce8149
commit 37cf2ac60c
5 changed files with 78 additions and 17 deletions

View File

@ -1,4 +1,6 @@
<div class="pseudo-virtual-list {{shown ? '' : 'hidden'}}" on:initializedVisibleItems ref:node>
<div class="pseudo-virtual-list {{shown ? '' : 'hidden'}}"
on:initializedVisibleItems
ref:node>
{{#if wrappedItems}}
{{#each wrappedItems as wrappedItem, i @item}}
<PseudoVirtualListLazyItem
@ -22,9 +24,8 @@
}
</style>
<script>
import PseudoVirtualListLazyItem from './PseudoVirtualListLazyItem.html'
import { getRectFromEntry } from '../../_utils/getRectFromEntry'
import { getRectFromEntry, getRootRectFromEntry } from '../../_utils/getRectFromEntry'
import { mark, stop } from '../../_utils/marks'
import { pseudoVirtualListStore } from './pseudoVirtualListStore'
@ -42,8 +43,9 @@
}
this.store.setForRealm({intersectionStates: intersectionStates})
let container = document.querySelector(this.get('containerQuery'))
let intersectionObserver = new IntersectionObserver(this.onIntersection.bind(this), {
root: document.querySelector(this.get('containerQuery')),
root: container,
rootMargin: '300% 0px'
})
this.set({intersectionObserver})
@ -55,6 +57,8 @@
this.fire('initializedVisibleItems')
}
})
this.calculateListOffset()
stop('PseudoVirtualList oncreate()')
},
ondestroy() {
@ -114,6 +118,21 @@
intersectionStates = Object.assign(intersectionStates, newIntersectionStates)
this.store.setForRealm({intersectionStates: intersectionStates})
stop('onIntersection')
},
calculateListOffset() {
let node = this.refs.node
let container = document.querySelector(this.get('containerQuery'))
let containerObserver = new IntersectionObserver(entries => {
entries.forEach(entry => {
let rect = getRectFromEntry(entry)
let rootRect = getRootRectFromEntry(entry, container)
let listOffset = rect.top - rootRect.top
console.log('listOffset', listOffset)
this.store.setForRealm({listOffset})
})
containerObserver.disconnect()
}, {root: container})
containerObserver.observe(node)
}
},
computed: {

View File

@ -1,5 +1,7 @@
<VirtualListContainer :realm :containerQuery >
<div class="virtual-list {{shown ? '' : 'hidden'}}" style="height: {{$height}}px;">
<div class="virtual-list {{shown ? '' : 'hidden'}}"
style="height: {{$height}}px;"
ref:node >
<VirtualListHeader component="{{headerComponent}}" virtualProps="{{headerProps}}" shown="{{$showHeader}}"/>
{{#if $visibleItems}}
{{#each $visibleItems as visibleItem @key}}
@ -31,6 +33,7 @@
import throttle from 'lodash/throttle'
import { mark, stop } from '../../_utils/marks'
import isEqual from 'lodash/isEqual'
import { getRectFromEntry, getRootRectFromEntry } from '../../_utils/getRectFromEntry'
const DISTANCE_FROM_BOTTOM_TO_FIRE = 800
const SCROLL_EVENT_THROTTLE = 1000
@ -86,6 +89,8 @@
this.fireScrollToTop()
}
})
this.calculateListOffset()
},
data: () => ({
component: null
@ -104,6 +109,27 @@
scrollTop: ($scrollTop) => $scrollTop,
// TODO: bug in svelte store, shouldn't need to do this
allVisibleItemsHaveHeight: ($allVisibleItemsHaveHeight) => $allVisibleItemsHaveHeight,
},
methods: {
calculateListOffset() {
let node = this.refs.node
let container = document.querySelector(this.get('containerQuery'))
// TODO: this needs to dynamically update, e.g. after pinned
// toots are rendered and thus the distance between the .container
// and the .virtual-list has changed.
let containerObserver = new IntersectionObserver(entries => {
entries.forEach(entry => {
let rect = getRectFromEntry(entry)
let rootRect = getRootRectFromEntry(entry, container)
let listOffset = rect.top - rootRect.top
console.log('listOffset', listOffset)
this.store.setForRealm({listOffset})
})
containerObserver.disconnect()
}, {root: container})
containerObserver.observe(node)
}
}
}
</script>

View File

@ -20,16 +20,20 @@ virtualListStore.computeForRealm('headerHeight', 0)
virtualListStore.computeForRealm('scrollTop', 0)
virtualListStore.computeForRealm('scrollHeight', 0)
virtualListStore.computeForRealm('offsetHeight', 0)
virtualListStore.computeForRealm('listOffset', 0)
virtualListStore.computeForRealm('itemHeights', {})
virtualListStore.compute('rawVisibleItems',
['items', 'scrollTop', 'itemHeights', 'offsetHeight', 'showHeader', 'headerHeight'],
(items, scrollTop, itemHeights, offsetHeight, showHeader, headerHeight) => {
['items', 'scrollTop', 'itemHeights', 'offsetHeight', 'showHeader', 'headerHeight', 'listOffset'],
(items, scrollTop, itemHeights, offsetHeight, showHeader, headerHeight, listOffset) => {
window.rawVisibleItemsComputed = (window.rawVisibleItemsComputed || 0) + 1
mark('compute visibleItems')
if (!items) {
return null
}
// listOffset is the offset of the entire list within its scrollable container,
// so the effective scrollTop is what the "real" scrollTop is relative to the list.
let effectiveScrollTop = scrollTop - listOffset
let renderBuffer = RENDER_BUFFER_FACTOR * offsetHeight
let visibleItems = []
let totalOffset = showHeader ? headerHeight : 0
@ -40,13 +44,13 @@ virtualListStore.compute('rawVisibleItems',
let height = itemHeights[key] || 0
let currentOffset = totalOffset
totalOffset += height
let isAboveViewport = (currentOffset < scrollTop)
let isAboveViewport = (currentOffset < effectiveScrollTop)
if (isAboveViewport) {
if ((scrollTop - height - renderBuffer) > currentOffset) {
if ((effectiveScrollTop - height - renderBuffer) > currentOffset) {
continue // above the area we want to render
}
} else {
if (currentOffset > (scrollTop + offsetHeight + renderBuffer)) {
if (currentOffset > (effectiveScrollTop + offsetHeight + renderBuffer)) {
break // below the area we want to render
}
}

View File

@ -30,7 +30,6 @@
export default {
oncreate() {
let accountId = this.get('params').accountId
let instanceName = this.store.get('currentInstance')
updateProfileAndRelationship(accountId)
},
store: () => store,

View File

@ -3,16 +3,29 @@
let hasBoundingRectBug
function rectsAreEqual(rectA, rectB) {
return rectA.height === rectB.height &&
rectA.top === rectB.top &&
rectA.width === rectB.width &&
rectA.bottom === rectB.bottom &&
rectA.left === rectB.left &&
rectA.right === rectB.right
}
export function getRectFromEntry (entry) {
if (typeof hasBoundingRectBug !== 'boolean') {
const boundingRect = entry.target.getBoundingClientRect()
const observerRect = entry.boundingClientRect
hasBoundingRectBug = boundingRect.height !== observerRect.height ||
boundingRect.top !== observerRect.top ||
boundingRect.width !== observerRect.width ||
boundingRect.bottom !== observerRect.bottom ||
boundingRect.left !== observerRect.left ||
boundingRect.right !== observerRect.right
hasBoundingRectBug = !rectsAreEqual(boundingRect, observerRect)
}
return hasBoundingRectBug ? entry.target.getBoundingClientRect() : entry.boundingClientRect
}
export function getRootRectFromEntry (entry, root) {
if (typeof hasBoundingRectBug !== 'boolean') {
const boundingRect = root.getBoundingClientRect()
const rootRect = entry.rootBounds
hasBoundingRectBug = !rectsAreEqual(boundingRect, rootRect)
}
return hasBoundingRectBug ? root.getBoundingClientRect() : entry.rootBounds
}