style: slogan区域移动端样式

This commit is contained in:
2026-03-18 10:22:24 +08:00
parent f2c265e4e7
commit ec4ba64030

View File

@@ -1,156 +1,280 @@
<template>
<div
class="blocks-list flex"
ref="root"
:class="{ 'in-view': inView }"
>
<div
class="block-item flex flex-col flex-center"
v-for="(item, idx) in blocksList"
:key="item.number"
:style="{ '--delay': `${idx * 0.18}s` }"
>
<div class="number">{{ $t(item.number) }}</div>
<div class="label">{{ $t(item.label) }}</div>
<div class="line"></div>
</div>
</div>
<div class="blocks-list flex" ref="root" :class="{ 'in-view': inView }">
<div
class="block-item flex flex-col flex-center"
v-for="(item, idx) in blocksList"
:key="item.number"
:style="{ '--delay': `${idx * 0.18}s` }"
>
<div class="number">{{ $t(item.number) }}</div>
<div class="label">{{ $t(item.label) }}</div>
<div class="line"></div>
</div>
</div>
</template>
<script setup lang="ts">
import { computed, ref, onMounted, onUnmounted } from 'vue'
import { useI18n } from 'vue-i18n'
import { computed, ref, onMounted, onUnmounted } from 'vue'
import { useI18n } from 'vue-i18n'
const { t } = useI18n()
const { t } = useI18n()
const blocksList = ref([
{
number: 'AwardsPage.totalCashPrizes',
label: 'AwardsPage.totalCashPrizesLabel'
},
{
number: 'AwardsPage.globalMediaExpose',
label: 'AwardsPage.globalMediaExposeLabel'
},
{
number: 'AwardsPage.networkingOpportunities',
label: 'AwardsPage.networkingOpportunitiesLabel'
},
{
number: 'AwardsPage.awardCeremonyHongKong',
label: 'AwardsPage.awardCeremonyLabel'
}
])
const root = ref<HTMLElement | null>(null)
const inView = ref(false)
let io: IntersectionObserver | null = null
const blocksList = ref([
{
number: 'AwardsPage.totalCashPrizes',
label: 'AwardsPage.totalCashPrizesLabel'
},
{
number: 'AwardsPage.globalMediaExpose',
label: 'AwardsPage.globalMediaExposeLabel'
},
{
number: 'AwardsPage.networkingOpportunities',
label: 'AwardsPage.networkingOpportunitiesLabel'
},
{
number: 'AwardsPage.awardCeremonyHongKong',
label: 'AwardsPage.awardCeremonyLabel'
}
])
const root = ref<HTMLElement | null>(null)
const inView = ref(false)
let io: IntersectionObserver | null = null
onMounted(() => {
io = new IntersectionObserver(
entries => {
for (const entry of entries) {
if (entry.isIntersecting) {
// 延迟 0.5s 后触发动画并断开观察
setTimeout(() => {
inView.value = true
}, 500)
if (io) {
io.disconnect()
}
}
}
},
{ threshold: 0.05 }
)
if (root.value) {
io.observe(root.value)
}
})
onMounted(() => {
io = new IntersectionObserver(
(entries) => {
for (const entry of entries) {
if (entry.isIntersecting) {
// 延迟 0.5s 后触发动画并断开观察
setTimeout(() => {
inView.value = true
}, 500)
if (io) {
io.disconnect()
}
}
}
},
{ threshold: 0.05 }
)
if (root.value) {
io.observe(root.value)
}
})
onUnmounted(() => {
io?.disconnect()
})
onUnmounted(() => {
io?.disconnect()
})
</script>
<style lang="less" scoped>
.blocks-list {
height: 31.4rem;
background: linear-gradient(98.55deg, #232323 18.22%, #898989 101.1%);
.blocks-list {
height: 31.4rem;
background: linear-gradient(98.55deg, #232323 18.22%, #898989 101.1%);
.block-item {
flex: 1;
height: 100%;
color: #fff;
position: relative;
text-align: center;
white-space: pre-line;
row-gap: 3rem;
/* text scale-in animations */
.number {
font-size: 3.6rem;
font-family: 'PoppinsBold';
font-weight: 600;
transform: scale(0);
opacity: 0;
will-change: transform, opacity;
}
.label {
font-size: 2.4rem;
font-family: 'Arial';
font-weight: 400;
letter-spacing: 0.05em;
transform: scale(0);
opacity: 0;
will-change: transform, opacity;
}
/* vertical line grows top -> bottom */
.line {
position: absolute;
right: 0;
/* 固定 top 为最终高度的一半位置,这样 height 从 0 -> 27.4rem 时会从上向下增长 */
top: calc(50% - 13.7rem);
width: 0.1rem;
height: 0;
background-color: #8d8d8d;
will-change: height;
}
}
}
.block-item {
flex: 1;
height: 100%;
color: #fff;
position: relative;
text-align: center;
white-space: pre-line;
row-gap: 3rem;
/* text scale-in animations */
.number {
font-size: 3.6rem;
font-family: 'PoppinsBold';
font-weight: 600;
transform: scale(0);
opacity: 0;
will-change: transform, opacity;
}
.label {
font-size: 2.4rem;
font-family: 'Arial';
font-weight: 400;
letter-spacing: 0.05em;
transform: scale(0);
opacity: 0;
will-change: transform, opacity;
}
/* vertical line grows top -> bottom */
.line {
position: absolute;
right: 0;
/* 固定 top 为最终高度的一半位置,这样 height 从 0 -> 27.4rem 时会从上向下增长 */
top: calc(50% - 13.7rem);
width: 0.1rem;
height: 0;
background-color: #8d8d8d;
will-change: height;
}
}
}
/* 当组件进入视口并且等待 0.5s 后,.in-view 会加入根节点,下面规则触发动画 */
.in-view .block-item .number {
animation: scaleIn 0.48s cubic-bezier(0.2, 0.9, 0.2, 1) forwards;
animation-delay: var(--delay);
}
/* 当组件进入视口并且等待 0.5s 后,.in-view 会加入根节点,下面规则触发动画 */
.in-view .block-item .number {
animation: scaleIn 0.48s cubic-bezier(0.2, 0.9, 0.2, 1) forwards;
animation-delay: var(--delay);
}
.in-view .block-item .label {
animation: scaleIn 0.48s cubic-bezier(0.2, 0.9, 0.2, 1) forwards;
animation-delay: calc(var(--delay) + 0.12s);
}
.in-view .block-item .label {
animation: scaleIn 0.48s cubic-bezier(0.2, 0.9, 0.2, 1) forwards;
animation-delay: calc(var(--delay) + 0.12s);
}
.in-view .block-item .line {
animation: growLine 0.7s cubic-bezier(0.2, 0.9, 0.2, 1) forwards;
animation-delay: calc(var(--delay) + 0.18s);
}
.in-view .block-item .line {
animation: growLine 0.7s cubic-bezier(0.2, 0.9, 0.2, 1) forwards;
animation-delay: calc(var(--delay) + 0.18s);
}
/* keyframes */
@keyframes scaleIn {
from {
transform: scale(0);
opacity: 0;
}
to {
transform: scale(1);
opacity: 1;
}
}
/* keyframes */
@keyframes scaleIn {
from {
transform: scale(0);
opacity: 0;
}
to {
transform: scale(1);
opacity: 1;
}
}
@keyframes growLine {
from {
height: 0;
}
to {
height: 27.4rem;
}
}
@keyframes growLine {
from {
height: 0;
}
to {
height: 27.4rem;
}
}
/* 移动端 <= 1200px 四宫格布局 */
@media (max-width: 1200px) {
.blocks-list {
height: auto;
flex-wrap: wrap;
padding: 2rem 1.5rem;
column-gap: 0;
row-gap: 0;
.block-item {
flex: none;
width: 50%;
height: 18rem;
padding: 2rem 1rem;
box-sizing: border-box;
.number {
font-size: 2.8rem;
}
.label {
font-size: 1.8rem;
}
.line {
display: none;
}
&:nth-child(1)::after,
&:nth-child(2)::after,
&:nth-child(3)::after {
content: '';
position: absolute;
background-color: #8d8d8d;
}
/* 第一行的两个item底部需要分隔线 */
&:nth-child(1)::after,
&:nth-child(2)::after {
bottom: 0;
left: 0;
width: 100%;
height: 0.1rem;
}
/* 第一个item右侧需要分隔线 */
&:nth-child(1)::before {
content: '';
position: absolute;
right: 0;
top: 0;
height: 100%;
width: 0.1rem;
background-color: #8d8d8d;
}
&:nth-child(4)::before {
content: '';
position: absolute;
bottom: 0;
left: 0;
width: 0.1rem;
height: 100%;
background-color: #8d8d8d;
transform: translateX(-100%);
}
}
}
.in-view {
.block-item {
&:nth-child(1)::before {
transform-origin: bottom;
transform: scaleY(0);
animation: growUp 2s cubic-bezier(0.4, 0, 0.2, 1) forwards;
animation-delay: calc(var(--delay) + 0.5s);
}
&:nth-child(1)::after {
transform-origin: right;
transform: scaleX(0);
animation: growRight 2s cubic-bezier(0.4, 0, 0.2, 1) forwards;
animation-delay: calc(var(--delay) + 0.5s);
}
&:nth-child(2)::after {
transform-origin: left;
transform: scaleX(0);
animation: growLeft 2s cubic-bezier(0.4, 0, 0.2, 1) forwards;
animation-delay: calc(var(--delay) + 0.5s);
}
&:nth-child(4)::before {
transform: translateX(-100%) scaleY(0);
transform-origin: top;
animation: growDown 2s cubic-bezier(0.4, 0, 0.2, 1) forwards;
animation-delay: calc(var(--delay) + 0.5s);
}
}
}
@keyframes growUp {
0% {
transform: scaleY(0);
}
100% {
transform: scaleY(1);
}
}
@keyframes growDown {
0% {
transform: translateX(-100%) scaleY(0);
}
100% {
transform: translateX(-100%) scaleY(1);
}
}
@keyframes growLeft {
0% {
transform: scaleX(0);
}
100% {
transform: scaleX(1);
}
}
@keyframes growRight {
0% {
transform: scaleX(0);
}
100% {
transform: scaleX(1);
}
}
}
</style>