viewport cells intersect animation

This commit is contained in:
nicwands 2026-06-19 15:32:09 -04:00
parent 4b79a5af0f
commit a99dfdb8ff
4 changed files with 63 additions and 3 deletions

View file

@ -1,6 +1,6 @@
<template>
<div class="infinite-scroll-container">
<transition-group name="fade" tag="div" class="viewport-stage">
<div class="viewport-stage">
<div
v-for="viewport in visibleViewports"
class="viewport"
@ -24,7 +24,7 @@
</div>
</slot>
</div>
</transition-group>
</div>
</div>
</template>
@ -97,6 +97,7 @@ watch(currentViewportIndex, (newIndex, oldIndex) => {
emit('viewport-change', { newIndex, oldIndex })
})
// Expose to parent
defineExpose({
reset,
retryViewport,

View file

@ -1,5 +1,11 @@
<template>
<div class="mosaic-viewport">
<div
class="mosaic-viewport"
v-intersect:once="{
callback: onIntersect,
rootMargin: '-25% 0px -25% 0px',
}"
>
<div class="mosaic-container">
<router-link
v-for="cell in content.cells"
@ -18,6 +24,7 @@
params: { id: cell.content.slug },
}"
:key="`${viewport.index}-${cell.id}`"
ref="cells"
>
<strapi-media
:image="cell.content.image"
@ -30,6 +37,9 @@
</template>
<script setup>
import { ref } from 'vue'
import gsap from 'gsap'
const props = defineProps({
viewport: {
type: Object,
@ -40,6 +50,20 @@ const props = defineProps({
required: true,
},
})
const cells = ref([])
const onIntersect = (isIntersecting) => {
if (isIntersecting) {
if (!cells.value) return
const cellGifs = cells.value
.map((cell) => cell.$el?.children?.[0])
.filter(Boolean)
gsap.to(cellGifs, { opacity: 1, duration: 0, stagger: 0.1 })
}
}
</script>
<style lang="scss">
@ -76,6 +100,7 @@ const props = defineProps({
}
.strapi-media {
mix-blend-mode: darken;
opacity: 0;
}
}
}

View file

@ -3,11 +3,15 @@ import App from './App.vue'
import { ViteSSG } from 'vite-ssg'
import routes from './routes'
import strapiPlugin from './plugins/strapi'
import gsapPlugin from './plugins/gsap'
import intersectPlugin from './plugins/intersect'
import { strapi } from '@strapi/client'
export const createApp = ViteSSG(App, routes, ({ app }) => {
// Plugins
app.use(strapiPlugin)
app.use(gsapPlugin)
app.use(intersectPlugin)
})
// Prerender mosaic item routes

30
src/plugins/intersect.js Normal file
View file

@ -0,0 +1,30 @@
import { useIntersectionObserver } from '@vueuse/core'
export default {
install: (app) => {
app.directive('intersect', {
mounted(el, binding) {
const { stop } = useIntersectionObserver(
el,
([{ isIntersecting }]) => {
binding.value?.callback?.(isIntersecting)
if (isIntersecting) {
el.classList.add('intersected')
if (binding.arg === 'once') {
stop()
}
} else {
el.classList.remove('intersected')
}
},
{
threshold: binding.value?.threshold || 0,
rootMargin: binding.value?.rootMargin || '0px',
},
)
},
})
},
}