/* * 开盒动效 · 三幕分镜 * 参考 doc/box.md * * Act 1 (0–400ms): 光晕扩散 + 盒子震动 + 背景变暗 * Act 2 (400–1000ms): 盒盖飞起 + 金色光芒 + 粒子溅射 + 白色闪屏 * Act 3 (1000–1800ms): 内容弹性弹出 + 彩带纸屑 * * 总时长约 1.8–2.0s */ /* ═══ 覆盖层 ═══ */ .animation-overlay { position: fixed; top: 0; left: 0; right: 0; bottom: 0; z-index: 1000; display: flex; align-items: center; justify-content: center; pointer-events: none; } .animation-overlay.visible { pointer-events: auto; } /* ═══ 背景变暗 ═══ */ .bg-dimmer { position: absolute; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0, 0, 0, 0.55); opacity: 0; transition: opacity 0.4s ease-out; } .bg-dimmer.active { opacity: 1; } /* ════════════════════════ Act 1 · 光晕扩散 (0–400ms) ════════════════════════ */ .act1-glow-ring { position: absolute; width: 320rpx; height: 320rpx; border-radius: 50%; opacity: 0; transform: scale(0.15); } .act1-glow-ring.active { animation: glowExpand 0.5s ease-out forwards; } .glow-inner { width: 100%; height: 100%; border-radius: 50%; } /* 模式颜色 */ .box-takeout .glow-inner { background: radial-gradient(circle, #E8693B 0%, rgba(232,105,59,0.4) 35%, transparent 70%); } .box-fridge .glow-inner { background: radial-gradient(circle, #4CAF50 0%, rgba(76,175,80,0.4) 35%, transparent 70%); } .box-explore .glow-inner { background: radial-gradient(circle, #7C3AED 0%, rgba(124,58,237,0.4) 35%, transparent 70%); } @keyframes glowExpand { 0% { opacity: 0.9; transform: scale(0.15); } 50% { opacity: 0.6; transform: scale(1.5); } 100% { opacity: 0; transform: scale(3.2); } } /* ════════════════════════ Act 1 · 盒子震动 ════════════════════════ */ .box-stage { position: relative; z-index: 10; display: flex; flex-direction: column; align-items: center; opacity: 0; transform: scale(0.85); transition: opacity 0.15s ease, transform 0.15s ease; } .box-stage.active { opacity: 1; transform: scale(1); } /* ── 盒盖 ── */ .box-lid { position: relative; z-index: 3; margin-bottom: -12rpx; transition: transform 0.35s ease-in, opacity 0.35s ease-in; transition-delay: 0.05s; } .lid-top { width: 120rpx; height: 40rpx; background: linear-gradient(180deg, #F5A623 0%, #E8961A 100%); border-radius: 20rpx 20rpx 4rpx 4rpx; box-shadow: 0 4rpx 16rpx rgba(200, 120, 20, 0.4); } .box-lid.active { transform: translateY(-80rpx) rotate(-8deg); opacity: 0; } /* ── 金色光芒 ── */ .golden-light { position: absolute; z-index: 1; width: 200rpx; height: 200rpx; border-radius: 50%; opacity: 0; transform: scale(0.3); } .box-takeout .golden-light { background: radial-gradient(circle, #FFD54F 0%, rgba(255, 180, 50, 0.6) 30%, transparent 65%); } .box-fridge .golden-light { background: radial-gradient(circle, #A5D6A7 0%, rgba(76, 175, 80, 0.5) 30%, transparent 65%); } .box-explore .golden-light { background: radial-gradient(circle, #CE93D8 0%, rgba(124, 58, 237, 0.5) 30%, transparent 65%); } .golden-light.active { animation: goldenBurst 0.6s ease-out forwards; } @keyframes goldenBurst { 0% { opacity: 0; transform: scale(0.3); } 30% { opacity: 1; transform: scale(1.8); } 100% { opacity: 0; transform: scale(3.5); } } /* ── 盒体(Act2 压缩反弹) ── */ .box-body { position: relative; z-index: 2; transition: transform 0.25s ease-out; } .box-body.compressed { animation: bodyCompress 0.3s ease-out; } .box-emoji { font-size: 160rpx; display: block; } @keyframes bodyCompress { 0% { transform: scaleY(1); } 40% { transform: scaleY(0.85); } 100% { transform: scaleY(1); } } /* ── 粒子溅射(Act2) ── */ .spark-particles { position: absolute; z-index: 0; top: 50%; left: 50%; width: 0; height: 0; pointer-events: none; } .spark-particles.active .spark { animation: sparkBurst 0.7s ease-out forwards; } .spark { position: absolute; font-size: 32rpx; opacity: 0; } .spark.s1 { animation-delay: 0s !important; } .spark.s2 { animation-delay: 0.06s !important; } .spark.s3 { animation-delay: 0.12s !important; } .spark.s4 { animation-delay: 0.18s !important; } @keyframes sparkBurst { 0% { opacity: 1; transform: translate(0, 0) scale(0.5); } 100% { opacity: 0; transform: translate(var(--sx, 60rpx), var(--sy, -80rpx)) scale(1.2); } } .box-takeout .spark { --sx: 70rpx; --sy: -90rpx; } .box-takeout .spark.s2 { --sx: -60rpx; --sy: -70rpx; } .box-takeout .spark.s3 { --sx: 50rpx; --sy: -100rpx; } .box-takeout .spark.s4 { --sx: -80rpx; --sy: -80rpx; } .box-fridge .spark { --sx: 70rpx; --sy: -90rpx; } .box-fridge .spark.s2 { --sx: -60rpx; --sy: -70rpx; } .box-fridge .spark.s3 { --sx: 50rpx; --sy: -100rpx; } .box-fridge .spark.s4 { --sx: -80rpx; --sy: -80rpx; } .box-explore .spark { --sx: 70rpx; --sy: -90rpx; } .box-explore .spark.s2 { --sx: -60rpx; --sy: -70rpx; } .box-explore .spark.s3 { --sx: 50rpx; --sy: -100rpx; } .box-explore .spark.s4 { --sx: -80rpx; --sy: -80rpx; } /* ════════════════════════ Act 2 · 白色闪屏 (600–1000ms) ════════════════════════ */ .act2-mask { position: absolute; top: 0; left: 0; right: 0; bottom: 0; background: #FFFFFF; opacity: 0; pointer-events: none; } .act2-mask.active { animation: maskFlash 0.5s ease-in-out forwards; animation-delay: 0.2s; } @keyframes maskFlash { 0% { opacity: 0; } 45% { opacity: 0.92; } 100% { opacity: 0; } } /* ════════════════════════ Act 3 · 内容弹性弹出 (1000–1800ms) ════════════════════════ */ .act3-content { position: relative; z-index: 20; opacity: 0; transform: scale(0.85) translateY(30rpx); } .act3-content.active { animation: contentReveal 0.45s cubic-bezier(0.34, 1.56, 0.64, 1) forwards; } .content-inner { display: flex; flex-direction: column; align-items: center; } @keyframes contentReveal { 0% { opacity: 0; transform: scale(0.85) translateY(30rpx); } 100% { opacity: 1; transform: scale(1) translateY(0); } } /* ════════════════════════ Act 3 · 彩带纸屑 (1600ms+) ════════════════════════ */ .confetti-stage { position: absolute; top: 0; left: 0; right: 0; bottom: 0; pointer-events: none; overflow: hidden; } .confetti-stage.active .confetti { animation: confettiDrop 1.4s ease-in forwards; } .confetti { position: absolute; top: -20rpx; left: var(--x, 20%); width: 14rpx; height: 14rpx; border-radius: 3rpx; background: var(--c, #E8693B); opacity: 0; animation-delay: var(--d, 0s); } @keyframes confettiDrop { 0% { opacity: 1; transform: translateY(0) rotate(0deg) scale(1); } 30% { opacity: 0.9; transform: translateY(280rpx) rotate(180deg) scale(0.7); } 60% { opacity: 0.5; transform: translateY(600rpx) rotate(400deg) scale(0.4); } 100% { opacity: 0; transform: translateY(1000rpx) rotate(720deg) scale(0.1); } }