fine-tune infinite scrolling list
This commit is contained in:
parent
eacf28317e
commit
9e111bfc5a
|
@ -5848,6 +5848,11 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"requestidlecallback": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/requestidlecallback/-/requestidlecallback-0.3.0.tgz",
|
||||
"integrity": "sha1-b7dOBzP5DfP6pIOPn2oqX5t0KsU="
|
||||
},
|
||||
"require-directory": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
"npm-run-all": "^4.1.2",
|
||||
"performance-now": "^2.1.0",
|
||||
"pify": "^3.0.0",
|
||||
"requestidlecallback": "^0.3.0",
|
||||
"sapper": "^0.3.2",
|
||||
"serve-static": "^1.13.1",
|
||||
"style-loader": "^0.19.1",
|
||||
|
|
|
@ -10,17 +10,20 @@
|
|||
import Nav from './Nav.html';
|
||||
import { virtualListStore } from '../_utils/virtualListStore'
|
||||
|
||||
import debounce from 'lodash/debounce'
|
||||
import throttle from 'lodash/throttle'
|
||||
const THROTTLE_DELAY = 500
|
||||
|
||||
const SCROLL_EVENT_DELAY = 300
|
||||
const RESIZE_EVENT_DELAY = 700
|
||||
|
||||
export default {
|
||||
oncreate() {
|
||||
this.observe('innerHeight', throttle(() => {
|
||||
this.observe('innerHeight', debounce(() => {
|
||||
// respond to window resize events
|
||||
this.store.set({
|
||||
offsetHeight: this.refs.node.offsetHeight
|
||||
})
|
||||
}, THROTTLE_DELAY))
|
||||
}, RESIZE_EVENT_DELAY))
|
||||
this.store.set({
|
||||
scrollTop: this.refs.node.scrollTop,
|
||||
scrollHeight: this.refs.node.scrollHeight,
|
||||
|
@ -33,7 +36,10 @@
|
|||
store: () => virtualListStore,
|
||||
events: {
|
||||
scroll(node, callback) {
|
||||
const onScroll = throttle(callback, THROTTLE_DELAY)
|
||||
const onScroll = throttle(callback, SCROLL_EVENT_DELAY, {
|
||||
leading: true,
|
||||
trailing: true
|
||||
})
|
||||
node.addEventListener('scroll', onScroll);
|
||||
|
||||
return {
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
import fixture from '../_utils/fixture.json'
|
||||
import StatusListItem from './StatusListItem.html'
|
||||
import VirtualList from './VirtualList.html'
|
||||
import { splice } from 'svelte-extras'
|
||||
import { splice, push } from 'svelte-extras'
|
||||
|
||||
let i = -1
|
||||
|
||||
|
@ -35,12 +35,33 @@
|
|||
},
|
||||
methods: {
|
||||
splice: splice,
|
||||
push: push,
|
||||
addMoreItems() {
|
||||
console.log('addMoreItems')
|
||||
let statuses = this.get('statuses')
|
||||
if (statuses) {
|
||||
this.splice('statuses', statuses.length, 0, ...createData())
|
||||
let itemsToAdd = createData()
|
||||
if (itemsToAdd.length) {
|
||||
|
||||
}
|
||||
|
||||
let importantFirstItem = itemsToAdd
|
||||
this.splice('statuses', statuses.length, 0, ...itemsToAdd)
|
||||
}
|
||||
},
|
||||
addTheseItems(items) {
|
||||
if (!items.length) {
|
||||
return
|
||||
}
|
||||
this.push(items.pop())
|
||||
while (items.length) {
|
||||
this.addItemLazily(items.pop())
|
||||
}
|
||||
},
|
||||
addItemLazily(item) {
|
||||
requestIdleCallback(() => {
|
||||
this.push(item)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
<div class="virtual-list">
|
||||
<!-- <div class="virtual-list-viewport" ref:viewport></div> -->
|
||||
<div class="virtual-list" style="height: {{$height}}px;">
|
||||
{{#each $visibleItems as item @key}}
|
||||
<VirtualListItem :component
|
||||
offset="{{item.offset}}"
|
||||
|
@ -27,9 +26,14 @@
|
|||
})
|
||||
})
|
||||
|
||||
let observedOnce = false
|
||||
|
||||
this.observe('distanceFromBottom', (distanceFromBottom) => {
|
||||
//console.log('distanceFromBottom', distanceFromBottom)
|
||||
if (distanceFromBottom > 0 && // hack: the first it's reported, it's always 0
|
||||
if (!observedOnce) {
|
||||
observedOnce = true // TODO: the first time is always 0... need better way to handle this
|
||||
return
|
||||
}
|
||||
if (distanceFromBottom >= 0 &&
|
||||
distanceFromBottom <= DISTANCE_FROM_BOTTOM_TO_FIRE) {
|
||||
this.fire('scrollToBottom')
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
top: 0;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
/* will-change: transform; */ /* causes jank in mobile Firefox */
|
||||
}
|
||||
.shown {
|
||||
opacity: 1;
|
||||
|
|
|
@ -17,8 +17,13 @@ const importIntersectionObserver = () => import(
|
|||
/* webpackChunkname: 'intersection-observer' */ 'intersection-observer'
|
||||
)
|
||||
|
||||
const importRequestIdleCallback = () => import(
|
||||
/* webpackChunkName: 'requestidlecallback' */ 'requestidlecallback'
|
||||
)
|
||||
|
||||
export {
|
||||
importURLSearchParams,
|
||||
importTimeline,
|
||||
importIntersectionObserver
|
||||
importIntersectionObserver,
|
||||
importRequestIdleCallback
|
||||
}
|
|
@ -11,7 +11,7 @@ const virtualListStore = new VirtualListStore({
|
|||
virtualListStore.compute('visibleItems',
|
||||
['items', 'scrollTop', 'itemHeights', 'offsetHeight'],
|
||||
(items, scrollTop, itemHeights, offsetHeight) => {
|
||||
let renderBuffer = 1.5 * offsetHeight
|
||||
let renderBuffer = 3 * offsetHeight
|
||||
let visibleItems = []
|
||||
let totalOffset = 0
|
||||
let len = items.length
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
import { init } from 'sapper/runtime.js'
|
||||
import { importURLSearchParams } from '../routes/_utils/asyncModules'
|
||||
import { importIntersectionObserver } from '../routes/_utils/asyncModules'
|
||||
import {
|
||||
importURLSearchParams,
|
||||
importIntersectionObserver,
|
||||
importRequestIdleCallback
|
||||
} from '../routes/_utils/asyncModules'
|
||||
|
||||
// polyfills
|
||||
Promise.all([
|
||||
typeof URLSearchParams === 'undefined' && importURLSearchParams(),
|
||||
typeof IntersectionObserver === 'undefined' && importIntersectionObserver()
|
||||
typeof IntersectionObserver === 'undefined' && importIntersectionObserver(),
|
||||
typeof requestIdleCallback === 'undefined' && importRequestIdleCallback()
|
||||
]).then(() => {
|
||||
// `routes` is an array of route objects injected by Sapper
|
||||
init(document.querySelector('#sapper'), __routes__)
|
||||
|
|
Loading…
Reference in New Issue