use IntersectionObserver for virtual scroll
This commit is contained in:
parent
94cab7aaf7
commit
5e3e56d454
5
package-lock.json
generated
5
package-lock.json
generated
|
@ -3344,6 +3344,11 @@
|
|||
"resolved": "https://registry.npmjs.org/interpret/-/interpret-1.1.0.tgz",
|
||||
"integrity": "sha1-ftGxQQxqDg94z5XTuEQMY/eLhhQ="
|
||||
},
|
||||
"intersection-observer": {
|
||||
"version": "0.5.0",
|
||||
"resolved": "https://registry.npmjs.org/intersection-observer/-/intersection-observer-0.5.0.tgz",
|
||||
"integrity": "sha512-8Zgt4ijlyvIrQVTA7MPb2W9+KhoetrAbxlh0RmTGxpx0+ZsAXvy7IsbNnZIrqZ6TddAdWeQj49x7Ph7Ir6KRkA=="
|
||||
},
|
||||
"intl-messageformat": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-2.2.0.tgz",
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
"font-awesome-svg-png": "^1.2.2",
|
||||
"glob": "^7.1.2",
|
||||
"idb": "^2.0.4",
|
||||
"intersection-observer": "^0.5.0",
|
||||
"intl-relativeformat": "^2.1.0",
|
||||
"lodash": "^4.17.4",
|
||||
"node-fetch": "^1.7.3",
|
||||
|
|
|
@ -61,7 +61,7 @@
|
|||
".............. status-toolbar";
|
||||
grid-template-columns: 58px 1fr;
|
||||
border-bottom: 1px solid var(--main-border);
|
||||
will-change: transform; /* TODO: is this necessary? */
|
||||
/* will-change: transform; */ /* TODO: is this necessary? */
|
||||
}
|
||||
|
||||
:global(.status-sidebar) {
|
||||
|
|
|
@ -2,6 +2,11 @@
|
|||
<VirtualList component="{{StatusListItem}}" items="{{statuses}}" />
|
||||
<button type="button" on:click="addMoreItems()">Add more items</button>
|
||||
</div>
|
||||
<style>
|
||||
.timeline {
|
||||
min-height: 50vh;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
import { store } from '../_utils/store'
|
||||
import { getHomeTimeline } from '../_utils/mastodon/oauth'
|
||||
|
|
|
@ -42,8 +42,6 @@
|
|||
})
|
||||
}, THROTTLE_TIME))
|
||||
},
|
||||
ondestroy () {
|
||||
},
|
||||
data: () => ({
|
||||
component: null
|
||||
}),
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
<div class="virtual-list-item"
|
||||
<div class="virtual-list-item {{shown ? 'shown' : ''}}"
|
||||
virtual-list-key="{{key}}"
|
||||
ref:node
|
||||
style="transform: translate3d(0, {{offset}}px, 0);"
|
||||
>
|
||||
|
@ -8,6 +9,12 @@
|
|||
.virtual-list-item {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
.shown {
|
||||
opacity: 1;
|
||||
pointer-events: auto;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
|
@ -15,28 +22,45 @@
|
|||
|
||||
let updateItemHeights = {}
|
||||
let promise = Promise.resolve()
|
||||
let onIntersectionCallbacks = {}
|
||||
|
||||
let intersectionObserver = new IntersectionObserver(entries => {
|
||||
entries.forEach(entry => {
|
||||
let key = entry.target.getAttribute('virtual-list-key')
|
||||
onIntersectionCallbacks[key](entry)
|
||||
})
|
||||
})
|
||||
|
||||
export default {
|
||||
oncreate() {
|
||||
let key = this.get('key')
|
||||
updateItemHeights[key] = this.refs.node.offsetHeight
|
||||
promise.then(() => {
|
||||
// update all item heights in one microtask batch for better perf
|
||||
let updatedKeys = Object.keys(updateItemHeights)
|
||||
if (!updatedKeys.length) {
|
||||
return
|
||||
}
|
||||
// batch all updates to itemHeights for better perf
|
||||
let itemHeights = this.store.get('itemHeights')
|
||||
for (key of updatedKeys) {
|
||||
itemHeights[key] = updateItemHeights[key]
|
||||
}
|
||||
this.store.set({
|
||||
itemHeights: itemHeights
|
||||
onIntersectionCallbacks[key] = entry => {
|
||||
updateItemHeights[key] = entry.boundingClientRect.height
|
||||
promise.then(() => {
|
||||
// update all item heights in one microtask batch for better perf
|
||||
let updatedKeys = Object.keys(updateItemHeights)
|
||||
if (!updatedKeys.length) {
|
||||
return
|
||||
}
|
||||
// batch all updates to itemHeights for better perf
|
||||
let itemHeights = this.store.get('itemHeights')
|
||||
for (key of updatedKeys) {
|
||||
itemHeights[key] = updateItemHeights[key]
|
||||
}
|
||||
this.store.set({
|
||||
itemHeights: itemHeights
|
||||
})
|
||||
updateItemHeights = {}
|
||||
})
|
||||
updateItemHeights = {}
|
||||
})
|
||||
}
|
||||
intersectionObserver.observe(this.refs.node)
|
||||
},
|
||||
store: () => virtualListStore
|
||||
ondestroy() {
|
||||
intersectionObserver.unobserve(this.refs.node)
|
||||
},
|
||||
store: () => virtualListStore,
|
||||
computed: {
|
||||
'shown': ($itemHeights, key) => $itemHeights[key] > 0
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -13,7 +13,12 @@ const importTimeline = () => import(
|
|||
/* webpackChunkName: 'Timeline' */ '../_components/Timeline.html'
|
||||
).then(mod => mod.default)
|
||||
|
||||
const importIntersectionObserver = () => import(
|
||||
/* webpackChunkname: 'intersection-observer' */ 'intersection-observer'
|
||||
)
|
||||
|
||||
export {
|
||||
importURLSearchParams,
|
||||
importTimeline
|
||||
importTimeline,
|
||||
importIntersectionObserver
|
||||
}
|
|
@ -1,9 +1,11 @@
|
|||
import { init } from 'sapper/runtime.js'
|
||||
import { importURLSearchParams } from '../routes/_utils/asyncModules'
|
||||
import { importIntersectionObserver } from '../routes/_utils/asyncModules'
|
||||
|
||||
// polyfills
|
||||
Promise.all([
|
||||
typeof URLSearchParams === 'undefined' && importURLSearchParams()
|
||||
typeof URLSearchParams === 'undefined' && importURLSearchParams(),
|
||||
typeof IntersectionObserver === 'undefined' && importIntersectionObserver()
|
||||
]).then(() => {
|
||||
// `routes` is an array of route objects injected by Sapper
|
||||
init(document.querySelector('#sapper'), __routes__)
|
||||
|
|
Loading…
Reference in a new issue