modal animation

This commit is contained in:
nicwands 2026-06-24 16:24:30 -04:00
parent 1894e37605
commit 3f96155321
2 changed files with 85 additions and 7 deletions

View file

@ -1,18 +1,28 @@
<template>
<div class="mosaic-modal theme-dark layout-grid-inner" @click="onClose">
<div class="meta">
<div v-if="item.dl?.length" class="window dl" @click.stop>
<div
v-if="item.dl?.length"
class="window dl"
@click.stop
ref="dlWindow"
>
<dl-row v-for="row in item.dl" :row="row" />
</div>
<div class="window gif" @click.stop>
<div class="window gif" @click.stop ref="gifWindow">
<strapi-media :image="item.image" fit="contain" />
<strapi-link class="h6" v-bind="item.image_attribution" />
</div>
</div>
<div class="window content" @click.stop data-lenis-prevent>
<div
class="window content"
@click.stop
data-lenis-prevent
ref="contentWindow"
>
<lenis instance="mosaic-modal">
<button class="close mobile-only" @click="onClose">
Close X
@ -24,19 +34,80 @@
</lenis>
</div>
<button class="window close desktop-only" @click="onClose">
<button
class="window close desktop-only"
@click="onClose"
ref="closeWindow"
>
Close X
</button>
</div>
</template>
<script setup>
import gsap from 'gsap'
import { onMounted, ref, shallowRef } from 'vue'
import { useRouter } from 'vue-router'
const props = defineProps({ item: Object })
const router = useRouter()
const onClose = () => router.push('/')
const onClose = async () => {
await animateOut()
router.push('/')
}
const dlWindow = ref()
const gifWindow = ref()
const contentWindow = ref()
const closeWindow = ref()
// Animation
const tl = shallowRef()
const ANIMATION_SETTINGS = {
duration: 0.5,
ease: 'power3.out',
}
onMounted(() => {
tl.value = gsap.timeline()
tl.value.fromTo(
dlWindow.value,
{ opacity: 0, height: 0 },
{ opacity: 1, height: 'auto', ...ANIMATION_SETTINGS },
)
tl.value.fromTo(
gifWindow.value,
{ opacity: 0, height: 0 },
{ opacity: 1, height: 'auto', ...ANIMATION_SETTINGS },
'+0.1',
)
tl.value.fromTo(
contentWindow.value,
{ opacity: 0, height: 0 },
{ opacity: 1, height: 'auto', ...ANIMATION_SETTINGS },
'+0.1',
)
tl.value.fromTo(
closeWindow.value,
{ opacity: 0, height: 0 },
{ opacity: 1, height: 'auto', ...ANIMATION_SETTINGS },
'+0.1',
)
tl.value.pause(0)
})
const animateIn = () => {
tl.value?.play()
}
const animateOut = () => {
return new Promise((resolve) => {
if (!tl.value) return resolve()
tl.value.eventCallback('onReverseComplete', resolve)
tl.value.reverse()
})
}
defineExpose({ animateIn, animateOut })
</script>
<style lang="scss">
@ -56,6 +127,7 @@ const onClose = () => router.push('/')
background: var(--theme-bg);
color: var(--theme-fg);
border-radius: desktop-vw(10px);
overflow: hidden;
&.dl {
padding: desktop-vw(12px) desktop-vw(10px);

View file

@ -1,19 +1,25 @@
<template>
<main class="mosaic-item">
<mosaic-modal :item="activeItem" />
<mosaic-modal :item="activeItem" ref="modal" />
</main>
</template>
<script setup>
import { useStrapiGlobal } from '@/composables/useStrapi'
import { computed } from 'vue'
import { computed, onBeforeUnmount, onMounted, ref } from 'vue'
import { useRoute } from 'vue-router'
const route = useRoute()
const id = route.params.id
const modal = ref(null)
const { data: global } = await useStrapiGlobal()
onMounted(() => {
modal.value?.animateIn()
})
const mosaicItems = computed(() => global.value?.mosaic_items || [])
const activeItem = computed(() =>
mosaicItems.value.find((item) => item.slug === id),