Skip to content

Instantly share code, notes, and snippets.

@phanan
Last active February 2, 2023 17:10
Show Gist options
  • Save phanan/83fa9b10ad48a687a896133893dcf5d4 to your computer and use it in GitHub Desktop.
Save phanan/83fa9b10ad48a687a896133893dcf5d4 to your computer and use it in GitHub Desktop.
Virtual scroller with Vue Composition API
<template>
<VirtualScroller v-slot="{ item }" :item-height="35" :items="myMassiveArray">
<div :item="item" :key="item.id">{{ item.details.i.guess? }}</div>
</VirtualScroller>
</template>
<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>
@jerrylau91
Copy link

nice

@ijjvim
Copy link

ijjvim commented Apr 29, 2022

item.details.i.guess? here the question mark ? meaning ? Is something like the ts optional feature?

@ilyaDegtyarenko
Copy link

If Items have different heights?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment