more progress on infinite scroll

This commit is contained in:
Nolan Lawson 2018-01-16 23:16:15 -08:00
parent e670b57381
commit eacf28317e
5 changed files with 61 additions and 48 deletions

View file

@ -1,16 +1,55 @@
<:Window bind:innerHeight='innerHeight'/>
<Nav page={{page}} /> <Nav page={{page}} />
<div class="container"> <div class="container" on:scroll="onScroll(event)" ref:node>
<main> <main>
<slot></slot> <slot></slot>
</main> </main>
</div> </div>
<script> <script>
import Nav from './Nav.html'; import Nav from './Nav.html';
import { virtualListStore } from '../_utils/virtualListStore'
import throttle from 'lodash/throttle'
const THROTTLE_DELAY = 500
export default { export default {
oncreate() {
this.observe('innerHeight', throttle(() => {
// respond to window resize events
this.store.set({
offsetHeight: this.refs.node.offsetHeight
})
}, THROTTLE_DELAY))
this.store.set({
scrollTop: this.refs.node.scrollTop,
scrollHeight: this.refs.node.scrollHeight,
offsetHeight: this.refs.node.offsetHeight
})
},
components: { components: {
Nav Nav
} },
store: () => virtualListStore,
events: {
scroll(node, callback) {
const onScroll = throttle(callback, THROTTLE_DELAY)
node.addEventListener('scroll', onScroll);
return {
teardown() {
node.removeEventListener('scroll', onScroll);
}
};
}
},
methods: {
onScroll(event) {
this.store.set({
scrollTop: event.target.scrollTop,
scrollHeight: event.target.scrollHeight
})
}
}
}; };
</script> </script>

View file

@ -18,7 +18,7 @@
let i = -1 let i = -1
const createData = () => fixture.slice(0, 5).map(_ => ({ const createData = () => fixture.slice(0, 20).map(_ => ({
key: `${++i}`, key: `${++i}`,
props: _ props: _
})) }))
@ -37,7 +37,10 @@
splice: splice, splice: splice,
addMoreItems() { addMoreItems() {
console.log('addMoreItems') console.log('addMoreItems')
this.splice('statuses', this.get('statuses').length, 0, ...createData()) let statuses = this.get('statuses')
if (statuses) {
this.splice('statuses', statuses.length, 0, ...createData())
}
} }
} }
} }

View file

@ -1,5 +1,4 @@
<:Window bind:innerHeight='innerHeight'/> <div class="virtual-list">
<div class="virtual-list" style="height: {{$height}}px;">
<!-- <div class="virtual-list-viewport" ref:viewport></div> --> <!-- <div class="virtual-list-viewport" ref:viewport></div> -->
{{#each $visibleItems as item @key}} {{#each $visibleItems as item @key}}
<VirtualListItem :component <VirtualListItem :component
@ -17,46 +16,24 @@
<script> <script>
import VirtualListItem from './VirtualListItem' import VirtualListItem from './VirtualListItem'
import { virtualListStore } from '../_utils/virtualListStore' import { virtualListStore } from '../_utils/virtualListStore'
import throttle from 'lodash/throttle'
const THROTTLE_TIME = 500
const DISTANCE_FROM_BOTTOM_TO_FIRE = 400 const DISTANCE_FROM_BOTTOM_TO_FIRE = 400
export default { export default {
oncreate () { oncreate () {
let container = document.body.querySelector('.container')
this.observe('innerHeight', throttle(() => {
// respond to window resize events
this.store.set({
offsetHeight: container.offsetHeight
})
}, THROTTLE_TIME))
this.observe('items', (items) => { this.observe('items', (items) => {
this.store.set({ this.store.set({
'items': items items: items
}) })
}) })
this.store.observe('distanceFromBottom', distanceFromBottom => {
console.log('distanceFromBottom', distanceFromBottom) this.observe('distanceFromBottom', (distanceFromBottom) => {
if (distanceFromBottom >= 0 && //console.log('distanceFromBottom', distanceFromBottom)
if (distanceFromBottom > 0 && // hack: the first it's reported, it's always 0
distanceFromBottom <= DISTANCE_FROM_BOTTOM_TO_FIRE) { distanceFromBottom <= DISTANCE_FROM_BOTTOM_TO_FIRE) {
this.fire('scrollToBottom') this.fire('scrollToBottom')
} }
}) })
container.addEventListener('scroll', throttle((e) => {
this.store.set({
scrollTop: e.target.scrollTop,
scrollHeight: e.target.scrollHeight
}, {
leading: false,
trailing: true
})
}, THROTTLE_TIME))
this.store.set({
scrollTop: container.scrollTop,
scrollHeight: container.scrollHeight,
offsetHeight: container.offsetHeight
})
}, },
data: () => ({ data: () => ({
component: null component: null
@ -64,6 +41,11 @@
store: () => virtualListStore, store: () => virtualListStore,
components: { components: {
VirtualListItem VirtualListItem
},
computed: {
distanceFromBottom: ($scrollHeight, $scrollTop, $offsetHeight) => {
return $scrollHeight - $scrollTop - $offsetHeight
}
} }
} }
</script> </script>

View file

@ -35,7 +35,8 @@
oncreate() { oncreate() {
let key = this.get('key') let key = this.get('key')
onIntersectionCallbacks[key] = entry => { onIntersectionCallbacks[key] = entry => {
updateItemHeights[key] = entry.boundingClientRect.height let rect = entry.boundingClientRect
updateItemHeights[key] = rect.height
promise.then(() => { promise.then(() => {
// update all item heights in one microtask batch for better perf // update all item heights in one microtask batch for better perf
let updatedKeys = Object.keys(updateItemHeights) let updatedKeys = Object.keys(updateItemHeights)

View file

@ -27,7 +27,7 @@ virtualListStore.compute('visibleItems',
continue // below the area we want to render continue // below the area we want to render
} }
} else { } else {
if (currentOffset > (scrollTop + offsetHeight + renderBuffer)) { if (currentOffset > (scrollTop + height + renderBuffer)) {
break // above the area we want to render break // above the area we want to render
} }
} }
@ -35,24 +35,12 @@ virtualListStore.compute('visibleItems',
offset: currentOffset, offset: currentOffset,
props: props, props: props,
key: key, key: key,
index: i, index: i
height: height
}) })
} }
return visibleItems return visibleItems
}) })
virtualListStore.compute('distanceFromBottom',
['scrollHeight', 'scrollTop', 'offsetHeight'],
(scrollHeight, scrollTop, offsetHeight) => {
if (typeof scrollHeight === 'undefined' ||
typeof scrollTop === 'undefined' ||
typeof offsetHeight === 'undefined') {
return -1
}
return scrollHeight - scrollTop - offsetHeight
})
virtualListStore.compute('height', ['items', 'itemHeights'], (items, itemHeights) => { virtualListStore.compute('height', ['items', 'itemHeights'], (items, itemHeights) => {
let sum = 0 let sum = 0
let i = -1 let i = -1