Files
gloabl_award_front/src/views/AwardPage/components/JudgesSection.vue

279 lines
6.8 KiB
Vue

<template>
<div class="judges-container flex flex-col align-center" :class="{ mobile: isMobile && !isPad }">
<div class="title" ref="judgesTitleRef">{{ $t('AwardsPage.panelOfJudges') }}</div>
<!-- <img src="@/assets/images/award/bloom_logo.png" class="logo" /> -->
<div class="sub-title" ref="judgesSubTitleRef">{{ $t('AwardsPage.expertise') }}</div>
<div class="judgement-list" ref="judgementListRef">
<div
class="judgement-item flex flex-col align-center"
v-for="item in judgements"
:key="item.name"
>
<template v-if="!item.tbd">
<img :src="item.picture" class="picture" />
<div class="name">{{ $t(item.name) }}</div>
<div class="desc">{{ $t(item.desc) }}</div>
</template>
<template v-else>
<div class="picture tbd"></div>
<div class="name">{{ $t('AwardsPage.tbd') }}</div>
</template>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { onBeforeUnmount, onMounted, nextTick, ref, inject } from 'vue'
import { useI18n } from 'vue-i18n'
import { gsap } from 'gsap'
import jae from '@/assets/images/award/jae.png'
import diego from '@/assets/images/award/diego.png'
import gregory from '@/assets/images/award/gregory.png'
import vincenzo from '@/assets/images/award/vincenzo.png'
import tim from '@/assets/images/award/tim.png'
import desmond from '@/assets/images/award/desmond.png'
const { t } = useI18n()
const isMobile = inject<boolean>('isMobile')
const isPad = inject<boolean>('isPad')
const judgements = [
{
picture: jae,
name: 'Jae Hyuk Lim',
desc: 'AwardsPage.judgesHat.jae'
},
{
picture: diego,
name: 'Diego Dultzin Lacoste',
desc: 'AwardsPage.judgesHat.diego',
tbd: true
},
{
picture: gregory,
name: 'Gregory de la Hogue Moran',
desc: 'AwardsPage.judgesHat.gregory'
},
{
picture: vincenzo,
name: 'Vincenzo La Torre',
desc: 'AwardsPage.judgesHat.vincenzo',
tbd: true
},
{
picture: tim,
name: 'Tim Lim',
desc: 'AwardsPage.judgesHat.tim',
tbd: true
},
{
picture: desmond,
name: 'Desmond Lim',
desc: 'AwardsPage.judgesHat.desmond'
}
]
const judgesTitleRef = ref<HTMLElement | null>(null)
const judgesSubTitleRef = ref<HTMLElement | null>(null)
const judgementListRef = ref<HTMLElement | null>(null)
const hasPlayedJudgementAnim = ref(false)
let judgementObserver: IntersectionObserver | null = null
const setupJudgementInitialState = () => {
const titleEls = [judgesTitleRef.value, judgesSubTitleRef.value].filter(Boolean) as HTMLElement[]
if (titleEls.length) {
gsap.set(titleEls, {
opacity: 0,
scale: 0,
transformOrigin: '50% 50%'
})
}
const items = judgementListRef.value?.querySelectorAll<HTMLElement>('.judgement-item')
if (items?.length) {
gsap.set(items, {
opacity: 0,
clipPath: 'inset(0 0 100% 0)'
})
}
}
const playJudgementAnimation = () => {
if (hasPlayedJudgementAnim.value) return
const titleEls = [judgesTitleRef.value, judgesSubTitleRef.value].filter(Boolean) as HTMLElement[]
const listEl = judgementListRef.value
if (!titleEls.length || !listEl) return
const items = Array.from(listEl.querySelectorAll<HTMLElement>('.judgement-item'))
const tl = gsap.timeline({ defaults: { ease: 'power2.out' } })
tl.to(titleEls, {
opacity: 1,
scale: 1,
duration: 0.4,
ease: 'back.out(1.6)',
stagger: 0.1
})
if (items.length) {
const firstRow = items.slice(0, 3)
const secondRow = items.slice(3)
if (firstRow.length) {
tl.to(
firstRow,
{
opacity: 1,
clipPath: 'inset(0% 0% 0% 0%)',
duration: 0.45,
stagger: 0.05
},
'-=0.2'
)
}
if (secondRow.length) {
tl.to(
secondRow,
{
opacity: 1,
clipPath: 'inset(0% 0% 0% 0%)',
duration: 0.45,
stagger: 0.05
},
'+=0.1'
)
}
}
hasPlayedJudgementAnim.value = true
judgementObserver?.disconnect()
}
onMounted(() => {
nextTick(() => {
setupJudgementInitialState()
if ('IntersectionObserver' in window) {
judgementObserver = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
playJudgementAnimation()
}
})
},
{ threshold: 0.3 }
)
if (judgementListRef.value) {
judgementObserver.observe(judgementListRef.value)
}
} else {
// Fallback: play immediately if IntersectionObserver unsupported
playJudgementAnimation()
}
})
})
onBeforeUnmount(() => {
judgementObserver?.disconnect()
})
</script>
<style scoped lang="less">
.judges-container {
height: 147.4rem;
background: url('@/assets/images/award/judges_bg.png') no-repeat;
background-size: 100% 100%;
padding-top: 12.8rem;
.title {
color: #232323;
font-family: 'PoppinsBold';
font-weight: 600;
font-size: 4rem;
margin-bottom: 2.4rem;
}
.logo {
margin: 2.4rem 0 2.2rem;
}
.sub-title {
color: #b10000;
font-family: 'Arial';
font-weight: 400;
font-size: 3rem;
margin-bottom: 12rem;
}
.judgement-list {
display: grid;
grid-template-columns: repeat(3, 1fr);
column-gap: 23.22rem;
row-gap: 8rem;
padding: 0 25rem 0 26.6rem;
div {
text-align: center;
}
.judgement-item {
overflow: hidden;
.picture {
width: 20.2rem;
height: 26rem;
border-radius: 0.8rem;
&.tbd {
background-color: #d7d7d7;
}
}
.name {
margin: 3rem 0 2.4rem;
color: #232323;
font-family: 'PoppinsBold';
font-weight: 600;
font-size: 2.4rem;
}
.desc {
color: #585858;
font-family: 'Arial';
font-weight: 400;
font-size: 2rem;
white-space: pre-line;
text-align: center;
}
}
}
&.mobile {
height: 139.6rem;
background: url('@/assets/images/mobile_version_background/judge_bg.png') no-repeat;
background-size: 100% 100%;
padding-top: 6rem;
.title {
font-size: 3.2rem;
margin-bottom: 0.8rem;
}
.sub-title {
font-family: 'PoppinsMedium';
font-weight: 500;
font-size: 2.4rem;
margin-bottom: 5.8rem;
}
.judgement-list {
padding: 0 8.6rem;
grid-template-columns: repeat(2, 1fr);
column-gap: 6.8rem;
row-gap: 6rem;
// row-gap: 4rem;
// padding: 0 1.6rem;
.judgement-item {
.picture {
width: 15.6rem;
height: 20rem;
}
.name {
font-size: 2.4rem;
margin: 1.2rem 0;
}
.desc {
font-family: 'Instrument';
font-size: 2rem;
}
}
}
}
}
</style>