nicwands.com/src/components/MosaicScroll.vue
2026-06-24 11:16:12 -04:00

90 lines
2.2 KiB
Vue

<template>
<div class="mosaic-scroll">
<infinite-scroll-container
:bufferSize="2"
:initialIndex="0"
:generateContent="generateMosaicContent"
ref="scrollContainer"
>
<template #viewport="{ viewport, content }">
<mosaic-viewport :viewport="viewport" :content="content" />
</template>
</infinite-scroll-container>
</div>
</template>
<script setup>
import { nextTick, ref, watch } from 'vue'
import _shuffle from 'lodash/shuffle'
import { useMosaicLayout } from '@/composables/useMosaicLayout'
import { useRoute } from 'vue-router'
const props = defineProps({
items: Array,
})
const itemIndex = ref(0)
const shuffledItems = ref(_shuffle(props.items))
const scrollContainer = ref(null)
const { generateLayout, populateCells } = useMosaicLayout()
const route = useRoute()
const generateCellContent = () => {
if (itemIndex.value >= shuffledItems.value.length - 1) {
itemIndex.value = 0
shuffledItems.value = _shuffle(props.items)
return generateCellContent()
}
const item = shuffledItems.value[itemIndex.value]
itemIndex.value++
return item
}
// Generate mosaic content for each viewport
const generateMosaicContent = async (index) => {
// Calculate target number of cells based on viewport size and index
const baseCellCount = 6
const variance = Math.abs(index % 2) * 2
const targetCells = baseCellCount + variance
const layoutOptions = {
targetCells: targetCells,
}
// Generate the space-filling layout
const cells = generateLayout(layoutOptions)
// Populate cells with GIF content
const populatedCells = populateCells(cells.value, generateCellContent)
return {
cells: populatedCells,
}
}
// Start/stop scroll when page is open
watch(
() => route.name,
async () => {
if (route.name === 'mosaic-item') {
if (!scrollContainer.value) {
await nextTick()
}
scrollContainer.value?.stop?.()
} else {
scrollContainer.value?.start?.()
}
},
{ immediate: true },
)
</script>
<style lang="scss">
.mosaic-scroll {
position: relative;
}
</style>