style: 页面动画效果

This commit is contained in:
2026-01-20 15:42:17 +08:00
parent 516ad19db7
commit aa193f08cb
5 changed files with 851 additions and 461 deletions

View File

@@ -0,0 +1,173 @@
<template>
<div
class="selection-container container flex flex-col align-center"
ref="selectionRef"
>
<div class="title">Selection Criteria</div>
<!-- <img src="@/assets/images/award/bloom_logo.png" class="logo" /> -->
<div class="sub-title">Evaluation</div>
<div class="criteria-list flex" ref="criteriaListRef">
<div
class="item flex flex-col align-center"
v-for="item in criteriaList"
:key="item.name"
>
<img :src="item.icon" class="icon" :style="item.style" />
<div class="name">{{ item.name }}</div>
<div class="desc">{{ item.desc }}</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { nextTick, onBeforeUnmount, onMounted, ref } from 'vue'
import { gsap } from 'gsap'
import criteria1 from '@/assets/images/award/criteria_1.png'
import criteria2 from '@/assets/images/award/criteria_2.png'
import criteria3 from '@/assets/images/award/criteria_3.png'
import criteria4 from '@/assets/images/award/criteria_4.png'
const criteriaList = ref([
{
icon: criteria1,
name: 'Originality',
desc: 'Unique perspective and innovative approach to fashion design',
style: { width: '13rem', height: '17rem' }
},
{
icon: criteria2,
name: 'Creativity',
desc: 'Artistic vision and exceptional design excellence',
style: { width: '16rem', height: '18rem' }
},
{
icon: criteria3,
name: 'AiDA Integration',
desc: 'Effective application of AI design tools and functions',
style: { width: '16rem', height: '18rem' }
},
{
icon: criteria4,
name: 'Execution',
desc: 'Quality of presentation and technical craftsmanship',
style: { width: '18.8rem', height: '18rem' }
}
])
const selectionRef = ref<HTMLElement | null>(null)
const criteriaListRef = ref<HTMLElement | null>(null)
const hasPlayedSelectionAnim = ref(false)
let selectionObserver: IntersectionObserver | null = null
const setupSelectionInitialState = () => {
const items =
criteriaListRef.value?.querySelectorAll<HTMLElement>('.item') ?? []
if (items.length) {
gsap.set(items, {
opacity: 0,
scale: 0,
transformOrigin: '50% 50%'
})
}
}
const playSelectionAnimation = () => {
if (hasPlayedSelectionAnim.value) return
const items =
criteriaListRef.value?.querySelectorAll<HTMLElement>('.item') ?? []
if (!items.length) return
gsap.to(items, {
opacity: 1,
scale: 1,
duration: 0.6,
ease: 'back.out(1.6)',
stagger: 0.3
})
hasPlayedSelectionAnim.value = true
selectionObserver?.disconnect()
}
onMounted(() => {
nextTick(() => {
setupSelectionInitialState()
if ('IntersectionObserver' in window) {
selectionObserver = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
playSelectionAnimation()
}
})
},
{ threshold: 0.25 }
)
if (selectionRef.value) {
selectionObserver.observe(selectionRef.value)
}
} else {
playSelectionAnimation()
}
})
})
onBeforeUnmount(() => {
selectionObserver?.disconnect()
})
</script>
<style scoped lang="less">
.selection-container {
background: url('@/assets/images/award/selection_bg.png') no-repeat;
background-size: 100% 100%;
padding-top: 9.3rem;
.title {
color: #fff;
font-family: 'PoppinsBold';
font-weight: 600;
font-size: 4rem;
}
.logo {
margin: 2.3rem 0 2.3rem;
}
.sub-title {
color: #f95750;
font-family: 'Popins';
font-weight: 400;
font-size: 3rem;
margin-bottom: 11.8rem;
}
.criteria-list {
column-gap: 6rem;
.item {
height: 44rem;
width: 32.2rem;
box-sizing: border-box;
&:nth-of-type(3) {
background: url('@/assets/images/award/criteria_bg.png') no-repeat;
background-size: 100% 100%;
}
.icon {
width: 18.8rem;
height: 18rem;
}
.name {
font-family: 'PoppinsMedium';
font-weight: 500;
font-size: 2.8rem;
color: #fff;
margin: 2rem 0 5rem;
}
.desc {
font-family: 'Arial';
font-weight: 400;
font-size: 2.4rem;
color: #e0e0e0;
text-align: center;
}
}
}
}
</style>