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> <template>
<div class="mosaic-modal theme-dark layout-grid-inner" @click="onClose"> <div class="mosaic-modal theme-dark layout-grid-inner" @click="onClose">
<div class="meta"> <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" /> <dl-row v-for="row in item.dl" :row="row" />
</div> </div>
<div class="window gif" @click.stop> <div class="window gif" @click.stop ref="gifWindow">
<strapi-media :image="item.image" fit="contain" /> <strapi-media :image="item.image" fit="contain" />
<strapi-link class="h6" v-bind="item.image_attribution" /> <strapi-link class="h6" v-bind="item.image_attribution" />
</div> </div>
</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"> <lenis instance="mosaic-modal">
<button class="close mobile-only" @click="onClose"> <button class="close mobile-only" @click="onClose">
Close X Close X
@ -24,19 +34,80 @@
</lenis> </lenis>
</div> </div>
<button class="window close desktop-only" @click="onClose"> <button
class="window close desktop-only"
@click="onClose"
ref="closeWindow"
>
Close X Close X
</button> </button>
</div> </div>
</template> </template>
<script setup> <script setup>
import gsap from 'gsap'
import { onMounted, ref, shallowRef } from 'vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
const props = defineProps({ item: Object }) const props = defineProps({ item: Object })
const router = useRouter() 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> </script>
<style lang="scss"> <style lang="scss">
@ -56,6 +127,7 @@ const onClose = () => router.push('/')
background: var(--theme-bg); background: var(--theme-bg);
color: var(--theme-fg); color: var(--theme-fg);
border-radius: desktop-vw(10px); border-radius: desktop-vw(10px);
overflow: hidden;
&.dl { &.dl {
padding: desktop-vw(12px) desktop-vw(10px); padding: desktop-vw(12px) desktop-vw(10px);

View file

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