viewport cells intersect animation
This commit is contained in:
parent
4b79a5af0f
commit
a99dfdb8ff
4 changed files with 63 additions and 3 deletions
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
30
src/plugins/intersect.js
Normal 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',
|
||||
},
|
||||
)
|
||||
},
|
||||
})
|
||||
},
|
||||
}
|
||||
Loading…
Reference in a new issue