Last active
February 2, 2023 17:10
-
-
Save phanan/83fa9b10ad48a687a896133893dcf5d4 to your computer and use it in GitHub Desktop.
Virtual scroller with Vue Composition API
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<template> | |
<VirtualScroller v-slot="{ item }" :item-height="35" :items="myMassiveArray"> | |
<div :item="item" :key="item.id">{{ item.details.i.guess? }}</div> | |
</VirtualScroller> | |
</template> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<template> | |
<div ref="scroller" class="virtual-scroller" @scroll="onScroll"> | |
<div :style="{ height: `${totalHeight}px` }"> | |
<div :style="{ transform: `translateY(${offsetY}px)`}"> | |
<template v-for="item in renderedItems"> | |
<slot :item="item"></slot> | |
</template> | |
</div> | |
</div> | |
</div> | |
</template> | |
<script lang="ts" setup> | |
import { computed, onMounted, ref, toRefs } from 'vue' | |
const props = defineProps<{ items: any[], itemHeight: number }>() | |
const { items, itemHeight } = toRefs(props) | |
const scroller = ref<HTMLElement>() | |
const scrollerHeight = ref(0) | |
const renderAhead = 5 | |
const scrollTop = ref(0) | |
const totalHeight = computed(() => items.value.length * itemHeight.value) | |
const startPosition = computed(() => Math.max(0, Math.floor(scrollTop.value / itemHeight.value) - renderAhead)) | |
const offsetY = computed(() => startPosition.value * itemHeight.value) | |
const renderedItems = computed(() => { | |
let count = Math.ceil(scrollerHeight.value / itemHeight.value) + 2 * renderAhead | |
count = Math.min(items.value.length - startPosition.value, count) | |
return items.value.slice(startPosition.value, startPosition.value + count) | |
}) | |
const onScroll = e => requestAnimationFrame(() => scrollTop.value = (e.target as HTMLElement).scrollTop) | |
const observer = new ResizeObserver(entries => entries.forEach(el => scrollerHeight.value = el.contentRect.height)) | |
onMounted(() => { | |
observer.observe(scroller.value!) | |
scrollerHeight.value = scroller.value!.offsetHeight | |
}) | |
</script> | |
<style lang="scss" scoped> | |
.virtual-scroller { | |
overflow: auto; | |
will-change: transform; | |
> div { | |
overflow: hidden; | |
will-change: transform; | |
> div { | |
will-change: transform; | |
} | |
} | |
} | |
</style> |
item.details.i.guess?
here the question mark ?
meaning ? Is something like the ts optional feature?
If Items have different heights?
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
nice