TV动画《我心里危险的东西》官网开屏动画复刻

1710 字
9 分钟
TV动画《我心里危险的东西》官网开屏动画复刻

TV 动画《我心里危险的东西》(Boku no Kokoro no Yabai Yatsu / 简称 Bokuyaba)官方网站(bokuyaba-anime.com)在页面首次载入时,展现了一套精致优雅的开屏动画。它通过将 Logo 标题分割为多个独立的 SVG/PNG 图层,在经过短暂的圆环 Loader 旋转预载后,各个分图层从不同的倾斜、缩放与偏移位置按特定时间差平滑拼合归位,营造出极具仪式感的开场视觉冲力。

本文将详细剖析这套动画的核心实现原理,并附上完整的复刻源代码。


动画效果演示#

以下为复刻实现的开屏动画效果实录:

TV动画《我心里危险的东西》官网开屏动画复刻
TV动画《我心里危险的东西》官网开屏动画复刻


核心实现技术拆解#

整个开屏动画由三个关键阶段构成:独立圆环旋转加载(0.6s) \rightarrow 多图层延时分层组装 \rightarrow 淡出解封主界面

1. 0.6s 独立旋转 Loader 动画#

在开屏动画尚未播放之前,界面中央会优先展示一个轻量的旋转加载圈。它不需要复杂的图片资源,仅通过 CSS 伪元素与 keyframes 即可实现:

l-wrap__loader 旋转加载圈样式
.l-wrap__loader::before {
content: "";
width: 100%;
height: 100%;
display: block;
position: absolute;
top: 0;
left: 0;
border-radius: 50%;
border: 0.3rem solid #ffffff;
border-top: 0.3rem solid #fb32ca; /* 突出粉色描边 */
animation: rotate 0.6s linear infinite;
box-sizing: border-box;
}
@keyframes rotate {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}

当 DOM 加载完毕并经过 600ms 后,通过给 body 容器添加 .is-op 类,加载圈的 opacity 会在 0.5s 内平滑过渡为 0 并隐藏。


2. Logo 9 图层拆分与初始形变设置#

官方将 Logo 拆分成了 9 个独立图层(logo_1.png ~ logo_9.pnglogo_11.png)。为了让各个拆分块在拼合时产生错落有致的入场效果,初始状态(未添加 .is-op 类)下为不同的图层单独设定了缩放比例、位移偏移量以及旋转中心点(transform-origin):

Logo 各分图层初始状态设置
.p-op__logo-item {
position: relative;
opacity: 0;
filter: brightness(120%) blur(8px);
transform: scale(1.05);
}
/* 各分图层的个性化偏移与旋转中心点 (transform-origin) */
.p-op__logo-item:nth-of-type(1) {
transform: translate3d(0, 0, 0) scale(1.1);
}
.p-op__logo-item:nth-of-type(2) {
transform: translate3d(0, -15px, 0) scale(1.05);
}
.p-op__logo-item:nth-of-type(3) {
transform: translate3d(-10px, 0, 0) scale(1.05);
transform-origin: 65.3% 24.4%;
}
.p-op__logo-item:nth-of-type(5) {
transform: translate3d(0, 0, 0) scale(1.35);
transform-origin: 17.9% 74.8%;
}
.p-op__logo-item:nth-of-type(9) {
transform: translate3d(6px, 0, 0) scale(0.8);
transform-origin: 91.8% 58.1%;
}
Note

针对带有旋转和缩放的某些文字图层,精确定位 transform-origin 是确保其在缩放归位过程中轨迹自然的秘诀。


3. 三阶贝塞尔缓动与序次延时 (Transition Delay)#

当触发开屏状态(类名变为 .is-op)时,所有图层的 transform 均归位为 translate3d(0, 0, 0) scale(1)opacity 恢复为 1blur 变为 0

为了实现依次弹入的效果,各图层配置了不同的 transition-delay 步频:

Logo 归位过渡与延时控制
.is-op .p-op__logo-item {
opacity: 1;
filter: brightness(100%) blur(0);
transform: translate3d(0, 0, 0) scale(1);
transition: transform cubic-bezier(0.01, 0.53, 0.37, 0.99) 1s,
opacity cubic-bezier(0.5, 1, 0.89, 1) 1s,
filter cubic-bezier(0.5, 1, 0.89, 1) 1s;
}
/* 阶梯式动画触发延迟 */
.is-op .p-op__logo-item:nth-of-type(1) { transition-delay: 0.9s; }
.is-op .p-op__logo-item:nth-of-type(2) { transition-delay: 1.22s; }
.is-op .p-op__logo-item:nth-of-type(3) { transition-delay: 1.54s; }
.is-op .p-op__logo-item:nth-of-type(4) { transition-delay: 1.86s; }
.is-op .p-op__logo-item:nth-of-type(5) { transition-delay: 2.41s; }
.is-op .p-op__logo-item:nth-of-type(6) { transition-delay: 2.65s; }
.is-op .p-op__logo-item:nth-of-type(7) { transition-delay: 2.89s; }
.is-op .p-op__logo-item:nth-of-type(8) { transition-delay: 3.13s; }
.is-op .p-op__logo-item:nth-of-type(9) { transition-delay: 3.37s; }

使用的三次贝塞尔曲线 cubic-bezier(0.01, 0.53, 0.37, 0.99) 前段急速冲刺、后段吸附减速,使整个组装动画非常具有吸附感。


4. 状态机控制与强制重绘 (Reflow)#

通过 JavaScript 来切换控制类 is-op,并在重新播放动画时利用访问 offsetWidth 触发浏览器强制重绘(Reflow),以重置动画状态:

动画重新播放控制逻辑
function playAnimation() {
document.body.classList.remove('is-op', 'is-load');
// 强制触发浏览器重绘 (Reflow),重置 CSS 过渡与 keyframes 动画
void document.body.offsetWidth;
const delay = parseInt(document.getElementById('loaderDuration').value, 10) || 600;
// 定时器进入开屏 Logo 组装阶段
setTimeout(() => {
document.body.classList.add('is-op');
}, delay);
}

完整源代码#

点击下方展开框即可查阅并复制包含 HTML、CSS 与 JS 控制面板的完整复刻源码:

点击展开查看完整 op_animation.html 源代码
op_animation.html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>TV动画《我心里危险的东西》- 开屏动画还原</title>
<style>
:root {
--winH: 100vh;
--color-black: #333;
--color-white: #fff;
--color-pink: #fb32ca;
}
html {
width: 100%;
font-size: 62.5%;
overflow-x: hidden;
overflow-y: auto;
}
@media screen and (max-width: 960px) and (min-width: 561px) {
html { font-size: 68.75%; }
}
@media screen and (max-width: 960px) and (min-width: 751px) {
html { font-size: 78.125%; }
}
body {
width: 100%;
height: 100vh;
margin: 0;
padding: 0;
font-size: 1.6rem;
line-height: 1.3;
letter-spacing: 0.15rem;
font-weight: 500;
color: var(--color-black);
background: #ffffff;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
overflow: hidden;
user-select: none;
}
img {
max-width: 100%;
display: block;
vertical-align: middle;
}
/* 容器与加载圈 */
.l-wrap {
display: block;
position: relative;
width: 100%;
height: 100vh;
z-index: 0;
}
.l-wrap__loader {
height: 6rem;
width: 6rem;
display: block;
position: fixed;
top: 50%;
left: 50%;
z-index: 15;
transform: translate(-50%, -50%);
pointer-events: none;
opacity: 1;
transition: opacity 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94);
}
@media screen and (max-width: 960px) {
.l-wrap__loader {
height: 3rem;
width: 3rem;
}
}
.is-op .l-wrap__loader,
.is-load .l-wrap__loader {
opacity: 0;
}
.l-wrap__loader::before {
content: "";
width: 100%;
height: 100%;
display: block;
position: absolute;
top: 0;
left: 0;
border-radius: 50%;
border: 0.3rem solid var(--color-white);
border-top: 0.3rem solid var(--color-pink);
animation: rotate 0.6s linear infinite;
z-index: 20;
pointer-events: none;
box-sizing: border-box;
}
@keyframes rotate {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
/* 开屏动画层 */
.p-op {
display: flex;
align-items: center;
justify-content: center;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
overflow: hidden;
z-index: 100;
pointer-events: none;
}
.is-op .p-op {
pointer-events: auto;
}
.is-load .p-op {
opacity: 0;
transition: opacity 1.4s cubic-bezier(0.65, 0, 0.35, 1) 0.5s;
pointer-events: none;
}
.p-op__logo {
position: relative;
width: 82%;
max-width: 550px;
transform: translateY(5%) scale(1.15);
}
.is-op .p-op__logo {
transform: translateY(5%) scale(1);
transition: transform 12s cubic-bezier(0.5, 1, 0.89, 1), filter 0.55s;
}
.p-op__logo-item {
position: relative;
opacity: 0;
filter: brightness(120%) blur(8px);
transform: scale(1.05);
}
.p-op__logo-item:not(:first-of-type) {
position: absolute;
top: 0;
left: 0;
width: 100%;
}
/* 初始拆分偏移及缩放设置 */
.p-op__logo-item:nth-of-type(1) { transform: translate3d(0, 0, 0) scale(1.1); }
.p-op__logo-item:nth-of-type(2) { transform: translate3d(0, -15px, 0) scale(1.05); }
.p-op__logo-item:nth-of-type(3) { transform: translate3d(-10px, 0, 0) scale(1.05); transform-origin: 65.3% 24.4%; }
.p-op__logo-item:nth-of-type(4) { transform: translate3d(-2px, -2px, 0) scale(0.9); transform-origin: 86.2% 21.4%; }
.p-op__logo-item:nth-of-type(5) { transform: translate3d(0, 0, 0) scale(1.35); transform-origin: 17.9% 74.8%; }
.p-op__logo-item:nth-of-type(6) { transform: translate3d(15px, 0, 0) scale(1.05); }
.p-op__logo-item:nth-of-type(7) { transform: translate3d(5px, 0, 0) scale(1.01); }
.p-op__logo-item:nth-of-type(8) { transform: translate3d(5px, 0, 0) scale(1.01); }
.p-op__logo-item:nth-of-type(9) { transform: translate3d(6px, 0, 0) scale(0.8); transform-origin: 91.8% 58.1%; }
/* is-op 状态下的动画归位 */
.is-op .p-op__logo-item {
opacity: 1;
filter: brightness(100%) blur(0);
transform: translate3d(0, 0, 0) scale(1);
transition: transform cubic-bezier(0.01, 0.53, 0.37, 0.99) 1s,
opacity cubic-bezier(0.5, 1, 0.89, 1) 1s,
filter cubic-bezier(0.5, 1, 0.89, 1) 1s;
}
.is-op .p-op__logo-item:nth-of-type(1) { transition-delay: 0.9s; }
.is-op .p-op__logo-item:nth-of-type(2) { transition-delay: 1.22s; }
.is-op .p-op__logo-item:nth-of-type(3) { transition-delay: 1.54s; }
.is-op .p-op__logo-item:nth-of-type(4) { transition-delay: 1.86s; }
.is-op .p-op__logo-item:nth-of-type(5) { transition-delay: 2.41s; }
.is-op .p-op__logo-item:nth-of-type(6) { transition-delay: 2.65s; }
.is-op .p-op__logo-item:nth-of-type(7) { transition-delay: 2.89s; }
.is-op .p-op__logo-item:nth-of-type(8) { transition-delay: 3.13s; }
.is-op .p-op__logo-item:nth-of-type(9) { transition-delay: 3.37s; }
.is-op .p-op__logo-item:nth-of-type(10) { transition-delay: 3.61s; }
/* 控制面板 */
.controls {
position: fixed;
bottom: 24px;
left: 50%;
transform: translateX(-50%);
z-index: 1000;
background: rgba(255, 255, 255, 0.9);
backdrop-filter: blur(12px);
border: 1px solid rgba(251, 50, 202, 0.2);
padding: 12px 24px;
border-radius: 40px;
display: flex;
align-items: center;
gap: 16px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.08);
font-size: 14px;
}
.btn-replay {
background: linear-gradient(135deg, #fb32ca, #e012ac);
color: #fff;
border: none;
padding: 8px 20px;
border-radius: 20px;
font-weight: 600;
cursor: pointer;
box-shadow: 0 4px 12px rgba(251, 50, 202, 0.3);
}
</style>
</head>
<body>
<div class="l-wrap js-wrap">
<div class="l-wrap__loader"></div>
<div class="l-wrap__cover">
<div class="p-op" id="js-op">
<div class="p-op__logo">
<div class="p-op__logo-item"><img src="./assets/img/op/logo_1.png" alt=""></div>
<div class="p-op__logo-item"><img src="./assets/img/op/logo_2.png" alt=""></div>
<div class="p-op__logo-item"><img src="./assets/img/op/logo_3.png" alt=""></div>
<div class="p-op__logo-item"><img src="./assets/img/op/logo_4.png" alt=""></div>
<div class="p-op__logo-item"><img src="./assets/img/op/logo_5.png" alt=""></div>
<div class="p-op__logo-item"><img src="./assets/img/op/logo_6.png" alt=""></div>
<div class="p-op__logo-item"><img src="./assets/img/op/logo_7.png" alt=""></div>
<div class="p-op__logo-item"><img src="./assets/img/op/logo_8.png" alt=""></div>
<div class="p-op__logo-item"><img src="./assets/img/op/logo_9.png" alt=""></div>
<div class="p-op__logo-item"><img src="./assets/img/op/logo_11.png" alt=""></div>
</div>
</div>
</div>
</div>
<div class="controls">
<button class="btn-replay" onclick="playAnimation()">↻ 重新播放动画</button>
</div>
<script>
function playAnimation() {
document.body.classList.remove('is-op', 'is-load');
void document.body.offsetWidth;
setTimeout(() => {
document.body.classList.add('is-op');
}, 600);
}
window.addEventListener('DOMContentLoaded', () => {
playAnimation();
});
</script>
</body>
</html>

支持与分享

如果这篇文章对你有帮助,欢迎分享给更多人或打赏支持!

打赏
TV动画《我心里危险的东西》官网开屏动画复刻
https://hodaru.com/posts/web/2026-07-24-bokuyaba-animation/
作者
Sonder
发布于
2026-07-24
许可协议
CC BY-NC-SA 4.0

评论区

Profile Image of the Author
Sonder
好想要技术
Welcome~
たびごころの 赴くままに
分类
标签
最新动态
站点统计
文章
13
分类
6
标签
11
总字数
28,300
运行时长
0
最后活动
0 天前
站点信息
构建平台
Cloudflare Workers
博客版本
Firefly v6.14.5
文章许可
CC BY-NC-SA 4.0