Kalzn
文章20
标签13
分类7
B站同款可动头图做法⬆️

B站同款可动头图做法⬆️

B站同款可动头图做法

背景

B站很久之前的头图都变成了可动的,感觉很漂亮,于是就想自己复现一下。但是本人并不是太会前端,所以也只能边摸索边搞。不过最终还是成功搞出来了,感觉效果还不错。

原理和准备

可动头图是由多个图层组合而来的,然后每个图层的位移不同,营造一种前后深度。这里我是用自设形象跑的AI,以下是原图。使用模型是自搭建的SD服务,模型是waiANINSFWPONYXL,骑扫帚融了Lora:riding_broom。然后还需要一个背景,背景也是用这个基模跑的。 头图原图 背景图

然后利用PS抠图,将图片分出几个可动图层,注意这里要把背景弄成透明的,按照png格式保存。 背景图

动画

这里通过监听mouseenter,mousemove,mouseleave三者动作,具体而言:

  1. mouseenter当光标进入头图区域时,将过渡动画关闭,并记录进入时的横坐标,并开始计算光标相对进入时的坐标位移。

  2. mousemove当光标在移动时,记录光标相对进入头图时的坐标位移,然后根据位移大小更新各个图层的位移大小。

  3. mouseleave当光标退出头图区域时,将过渡动画打开,并将所有图层归为初始位置。

此外对每一个图层加入上下浮动的动画,并在人物跟随光标移动时加入旋转效果。

这里要注意的是,离镜头越远的图层的位移速度越小。

参考源码

1
2
3
4
5
6
7
8
9
10
<div class="header_anime" id="anime_area">
<!-- 各图层 -->
<img src="/static/pic/header_img/header_moutain.png" class="header_moutain_block block" id="header_div_mt"/>
<img src="/static/pic/header_img/bf3.png" class="bf3_block block" id="header_div_bf3"/>
<img src="/static/pic/header_img/bf2.png" class="bf2_block block" id="header_div_bf2"/>
<img src="/static/pic/header_img/witch.png" class="fiska_block block" id="header_div_fiska"/>
<img src="/static/pic/header_img/bf1.png" class="bf1_block block" id="header_div_bf1"/>
<img src="/static/pic/header_img/bf4.png" class="bf4_block block" id="header_div_bf4"/>

</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
function set_block_position(rate, fullWidth) {
fiska_block = document.getElementById("header_div_fiska")
bf1_block = document.getElementById("header_div_bf1")
bf2_block = document.getElementById("header_div_bf2")
bf3_block = document.getElementById("header_div_bf3")
bf4_block = document.getElementById("header_div_bf4")
back_m_block = document.getElementById("header_div_mt")
back_block = document.getElementById("anime_area")
if (rate == -1) {
console.log("Heelo!!!")
switch_anime(true)
fiska_block.style.left = (1.0 * fullWidth * 0.4) + 'px'
fiska_block.style.transform = 'rotate(0deg)'
bf1_block.style.left = (1.0 * fullWidth * 0.7) + 'px'
bf2_block.style.left = (1.0 * fullWidth * 0.6) + 'px'
bf3_block.style.left = (1.0 * fullWidth * 0.2) + 'px'
bf4_block.style.left = (1.0 * fullWidth * 0.25) + 'px'
back_block.style.backgroundPosition = '-100px -110px'
back_m_block.style.left = '-120px'
} else if (rate < 0) {
rate = -rate
fiska_block.style.left = (1.0 * fullWidth * 0.4 * (1 - rate * 0.8)) + 'px'
bf1_block.style.left = (1.0 * fullWidth * 0.7 * (1 - rate * 0.4)) + 'px'
bf2_block.style.left = (1.0 * fullWidth * 0.6 * (1 - rate * 0.02)) + 'px'
bf3_block.style.left = (1.0 * fullWidth * 0.2 * (1 - rate * 0.02)) + 'px'
bf4_block.style.left = (1.0 * fullWidth * 0.25 * (1 - rate * 0.85)) + 'px'
back_m_block.style.left = ((-120) - (rate * 30)) + 'px'
back_block.style.backgroundPosition =(-100-(rate * 10)) + 'px -110px'
if (rate < 0.3) {
fiska_block.style.transform = 'rotate(' + 40 * rate + 'deg)'
} else {
fiska_block.style.transform = 'rotate(' + 40 * 0.3 + 'deg)'
}

} else {
fiska_block.style.left = (1.0 * fullWidth * (0.4 + (rate * 0.8) * 0.4)) + 'px'
bf1_block.style.left = (1.0 * fullWidth * (0.7 + rate * 0.1)) + 'px'
bf2_block.style.left = (1.0 * fullWidth * (0.6 + rate * 0.02)) + 'px'
bf3_block.style.left = (1.0 * fullWidth * (0.2 + rate * 0.02)) + 'px'
bf4_block.style.left = (1.0 * fullWidth * (0.25 + rate * 0.15)) + 'px'
back_block.style.backgroundPosition = (-100 + (rate * 10)) + 'px -110px'
back_m_block.style.left = ((-120) + (rate * 20)) + 'px'
if (rate < 0.3) {
fiska_block.style.transform = 'rotate(' + -25 * rate + 'deg)'
} else {
fiska_block.style.transform = 'rotate(' + -25 * 0.3 + 'deg)'
}
}
}
function switch_anime(flag) {
blocks = document.getElementsByClassName("block")
back_block = document.getElementById("anime_area")
if (flag) {
for (var i = 0; i < blocks.length; i++) {
blocks[i].style.transition = 'left 0.5s ease-in-out 0.0s, transform 0.5s ease-in-out 0.0s'
}
back_block.style.transition = 'background-position 0.5s ease-in-out 0.0s'
// fiska_block.style.transition = 'left 0.5s ease-in-out 0.0s, transform 0.5s ease-in-out 0.0s'
// bf1_block.style.transition = 'left 0.5s ease-in-out 0.0s, transform 0.5s ease-in-out 0.0s'
// bf1_block.style.transition = 'left 0.5s ease-in-out 0.0s, transform 0.5s ease-in-out 0.0s'
} else {
for (var i = 0; i < blocks.length; i++) {
blocks[i].style.transition = ''
}
back_block.style.transition = ''
// fiska_block.style.transition = ''
// bf1_block.style.transition = ''
}
}
function header_anime() {
var status = "-out"
var enterX = 0
var enterY = 0
var curX = 0
var curY = 0
var inFlag = false

const resizeObserver = new ResizeObserver(entries => {
for (let entry of entries) {
const { width, height } = entry.contentRect;
set_block_position(0, width)
}
});
resizeObserver.observe(document.getElementById("anime_area"))

document.addEventListener('mousemove', function(event) {
ani_div = document.getElementById("anime_area")
var text = 'Mouse position:' + event.clientX + "," + event.clientY + status
curX = event.clientX
curY = event.clientY
if (!inFlag)
return
var curDivWith = ani_div.offsetWidth
text += " (Full Width " + curDivWith + ") "
var movePx = curX - enterX
if (movePx < 0) {
text += " : Move <- " + (-movePx)
} else {
text += " : Move -> " + (movePx)
}
left_block = document.getElementById("left_padding_block")
right_block = document.getElementById("right_padding_block")

var moveRate = 1.0 * movePx / curDivWith
set_block_position(moveRate, curDivWith)


document.getElementById("debug_info_area").innerText = text
});
document.getElementById("anime_area").addEventListener('mouseenter', function(event) {
enterX = curX
enterY = curY
switch_anime(false)
console.log("in in pos: " + enterX + "," + enterY)
inFlag = true
})
document.getElementById("anime_area").addEventListener('mouseleave', function(event) {
console.log("out")
inFlag = false
ani_div = document.getElementById("anime_area")
var curDivWith = ani_div.offsetWidth
set_block_position(-1, curDivWith)
})
}
header_anime()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
.fiska_block {
height: 120px;
float: center;
position: absolute;
left: 200px;
margin-top: 0px;
margin-bottom: 0px;
animation: floatBlock 10s infinite ease-in-out;

}

.bf1_block {
height: 60px;
float: center;
position: absolute;
left: 50px;
margin-top: 0px;
margin-bottom: 0px;
animation: floatBlockBf 8s infinite ease-in-out;

}
.bf2_block {
height: 70px;
float: center;
position: absolute;
left: 480px;
margin-top: 0px;
margin-bottom: 0px;
animation: floatBlockBf 13s infinite ease-in-out;
}
.bf3_block {
height: 40px;
float: center;
position: absolute;
left: 50px;
margin-top: 0px;
margin-bottom: 0px;
animation: floatBlockBf 8s infinite ease-in-out;
}
.bf4_block {
height: 170px;
float: center;
position: absolute;
left: 50px;
margin-top: 0px;
margin-bottom: 0px;
animation: floatBlockBf 8s infinite ease-in-out;
}
.header_moutain_block {
height: 240px;
float: center;
position: relative;
/* clip-path: inset(100px 0px); */
left: -90px;
top: 0px;
padding-top: 0px;
margin-top: 0px;
margin-bottom: 0px;

}

.header_anime {
background-image: url('/static/pic/header_img/header_back.png');
background-size: cover; /* 背景图片覆盖整个元素 */
background-position: -100px -110px; /* 背景图片居中 */
background-repeat: no-repeat; /* 背景图片不重复 */
background-size: 125%;
padding-bottom: 20px
margin-bottom: 30px;
border-radius: 16px;
border: 3px solid rgb(23,44,54);
display: flex;
overflow: hidden;
align-items: center;
height: 120px; /* 使容器的高度为视口高度 */
}

@keyframes floatBlockBf {
0%, 100% {
margin-top: 5px;
margin-bottom: 0px;
}
50% {
margin-top: 0px;
margin-bottom: 20px;
}
}


@keyframes floatBlock {
0%, 100% {
margin-top: 10px;
margin-bottom: 0px;
}
50% {
margin-top: 0px;
margin-bottom: 45px;
}
}
本文作者:Kalzn
本文链接:http://kalzncc.github.io/2024/11/27/header_anime/
版权声明:除文章中特别注明外,本站所有文章采用 CC BY 3.0 CN 协议进行许可
即在署明文章作者及来源的情况下,您可以将本文用于包括商业目在内的任何用途。
除此之外,本文不做正确性担保,本人不对产生的问题负责。
×