如何用CSS实现GTA RP加载页的文字展开缩放动画?
实现《The Rookie》风格的GTA RP加载页动画
以下是修改后的完整代码,实现文字从左右两侧滑出、随后文字与背景同步缩放的效果:
HTML 结构
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <link href="assets/style.css" rel="stylesheet" type="text/css" /> <script src="assets/app.js" defer></script> </head> <body> <div id="app"> <!-- 背景图容器,用于缩放动画 --> <div id="bg-container"> <img id="image" src="https://cdn2.steamgriddb.com/hero_thumb/e6293d6df91c1f64f6ce1d86c8be87e4.jpg" /> </div> <div id="logo"> <div id="logo-container"> <!-- 左侧滑出的文字容器 --> <div class="slide-left"> <span>Sunset</span> </div> <!-- 右侧滑出的文字容器 --> <div class="slide-right"> <span>RP</span> </div> </div> </div> <div class="container"> <div class="volume-container"> <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"> <path stroke-linecap="round" stroke-linejoin="round" d="M15.536 8.464a5 5 0 010 7.072m2.828-9.9a9 9 0 010 12.728M5.586 15H4a1 1 0 01-1-1v-4a1 1 0 011-1h1.586l4.707-4.707C10.923 3.663 12 4.109 12 5v14c0 .891-1.077 1.337-1.707.707L5.586 15z" /> </svg> <input type="range" id="volume"> </div> </div> <audio src="assets/musique.mp3" loop></audio> </div> </body> </html>
CSS 样式(核心动画实现)
@import url('https://fonts.googleapis.com/css2?family=Prompt:wght@300&display=swap'); * { user-select: none; margin: 0; padding: 0; box-sizing: border-box; } body { font-family: 'Prompt', sans-serif; font-weight: 300; overflow: hidden; } #app { position: absolute; inset: 0; } /* 背景图容器,用于缩放动画 */ #bg-container { position: absolute; inset: 0; z-index: -1; animation: bg-scale 4s ease-in-out 2s forwards; /* 滑出动画后触发缩放 */ } #bg-container img { width: 100%; height: 100%; object-fit: cover; } #logo { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; } #logo-container { display: flex; gap: 2rem; text-transform: uppercase; animation: text-scale 4s ease-in-out 2s forwards; /* 与背景同步缩放 */ } /* 左侧文字滑出动画 */ .slide-left { color: white; font-size: 128px; overflow: hidden; position: relative; animation: slide-in-left 2s ease-out forwards; } .slide-left span { position: absolute; top: 0; left: -100%; animation: slide-text-left 2s ease-out forwards; } /* 右侧文字滑出动画 */ .slide-right { color: white; font-size: 96px; overflow: hidden; position: relative; animation: slide-in-right 2s ease-out forwards; } .slide-right span { position: absolute; top: 0; right: -100%; animation: slide-text-right 2s ease-out forwards; } /* 文字容器滑入动画(控制可见区域) */ @keyframes slide-in-left { 0% { width: 0; } 100% { width: 480px; } /* 适配文字宽度 */ } @keyframes slide-in-right { 0% { width: 0; } 100% { width: 180px; } /* 适配文字宽度 */ } /* 文字本身滑入动画 */ @keyframes slide-text-left { 0% { left: -100%; } 100% { left: 0; } } @keyframes slide-text-right { 0% { right: -100%; } 100% { right: 0; } } /* 背景缩放动画 */ @keyframes bg-scale { 0% { transform: scale(1); } 100% { transform: scale(1.2); } } /* 文字缩放动画 */ @keyframes text-scale { 0% { transform: scale(1); } 100% { transform: scale(1.1); } } /* 音量控制部分样式保持不变 */ .container { position: absolute; bottom: 0; left: 0; display: flex; justify-content: center; align-items: center; padding: .3rem 1rem; margin: 1vh 1vw; background: rgba(0,0,0,.5); border-radius: .5rem; z-index: 1; } .volume-container { display: flex; align-items: center; flex-grow: 1; } .volume-container svg { color: rgba(255,255,255,.8); width: 1.5rem; height: 1.5rem; margin-right: .2rem; } .volume-container input { height: 0.3rem; }
JavaScript 代码(保持原有功能)
const app = document.getElementById('app'); const musique = app.querySelector('audio'); const volume = app.querySelector('#volume'); const VOLUME_MAX_MULT = 0.8; function setVolume(volume) { musique.volume = (volume / 100) * VOLUME_MAX_MULT; } function bootstrap() { volume.addEventListener('input', (event) => { setVolume(event.currentTarget.value); }); musique.play().catch(err => console.log('音频播放失败:', err)); } window.addEventListener('message', function(e) { if (e.data.eventName === 'initFunctionInvoking' && e.data.name === 'FinalizeLoad') { const fadeInterval = setInterval(() => { if (musique.volume > 0.01) { musique.volume -= 0.01; } else { clearInterval(fadeInterval); } }, 50); } }); bootstrap();
关键改动说明
- 文字滑出实现:将文字放在独立容器内,通过控制容器宽度和文字的绝对位置,实现左右两侧的滑出效果,避免原代码中仅修改宽高的生硬过渡。
- 同步缩放动画:给背景容器和文字容器添加延迟触发的缩放动画,确保文字滑出完成后再开始缩放,还原《The Rookie》片头的节奏。
- 动画时序优化:通过
animation-delay设置缩放动画在滑出动画完成后触发,保证动画流程连贯。 - 音频播放兼容:给
musique.play()添加catch处理,避免浏览器自动播放限制导致的报错。
内容的提问来源于stack exchange,提问作者Shad Canard
相关产品推荐
相关产品推荐

