B站同款可动头图做法⬆️
B站同款可动头图做法
背景
B站很久之前的头图都变成了可动的,感觉很漂亮,于是就想自己复现一下。但是本人并不是太会前端,所以也只能边摸索边搞。不过最终还是成功搞出来了,感觉效果还不错。
原理和准备
可动头图是由多个图层组合而来的,然后每个图层的位移不同,营造一种前后深度。这里我是用自设形象跑的AI,以下是原图。使用模型是自搭建的SD服务,模型是waiANINSFWPONYXL
,骑扫帚融了Lora:riding_broom
。然后还需要一个背景,背景也是用这个基模跑的。
然后利用PS抠图,将图片分出几个可动图层,注意这里要把背景弄成透明的,按照png格式保存。
动画
这里通过监听mouseenter
,mousemove
,mouseleave
三者动作,具体而言:
mouseenter
当光标进入头图区域时,将过渡动画关闭,并记录进入时的横坐标,并开始计算光标相对进入时的坐标位移。
mousemove
当光标在移动时,记录光标相对进入头图时的坐标位移,然后根据位移大小更新各个图层的位移大小。
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' } else { for (var i = 0; i < blocks.length; i++) { blocks[i].style.transition = '' } back_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; 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 协议进行许可
即在署明文章作者及来源的情况下,您可以将本文用于包括商业目在内的任何用途。
除此之外,本文不做正确性担保,本人不对产生的问题负责。