b站首页banner景深移动特效 bilibili秋
看到b站的animation-banner效果太妙了,就自己也想仿写一个.去查了查资料鼓捣出来一个.
首先是成品展示

大家可能觉得这个效果看起来很复杂,其实拆开来看比较简单.
他的效果就是实现了一个镜头聚焦,那么我们分步骤进行讲解
step1
首先,让图层跟随鼠标进行移动,大家应该已经想到怎么办了,但是有个问题,每个图层跟随鼠标移动的距离不能相同,这里可以用循环进行移动距离的迭代.
step2
其次是让图层跟随鼠标的移动距离进行清晰度的变化,和上述的办法类似,也是构造一个函数来计算清晰度,可以看到,随着鼠标的移动,最后和靠前的图层清晰度都变低(后面称之为失焦点),而靠中间的图层清晰度变高(后面称之为聚焦点).我们可以知道,这个偏移距离和焦点的转移是有关系的

实际上这个函数有很好的选择,就是我们小学二年级就 学过的的二次函数,两端的数值大,中间的数值小,符合我们的要求.
然后我们利用图层的index把六个图层按顺序排列在x轴上,得到的y值就符合我们的要求了
这就是最核心的思路啦,那么我们就开始
首先获得六个图层
这里我就直接用b站的图了,因为咱菜鸡画不出来
放出来方便大家练习,侵删





<!-- 这就是我的html结构,很简单 -->
<header>
<div><img src="./img/bilibili-autumn-1.webp"></div>
<div><img src="./img/bilibili-autumn-2.webp"></div>
<div><img src="./img/bilibili-autumn-3.webp"></div>
<div><img src="./img/bilibili-autumn-4.webp"></div>
<div><img src="./img/bilibili-autumn-5.webp"></div>
<div><img src="./img/bilibili-autumn-6.webp"></div>
</header>
body{
margin: 0;//经典margin0
}
header{
height: 155px;//给容器一个高度,防止高度坍塌
position: relative;//开定位,六个图层需要以容器为坐标进行左右移动
overflow: hidden;//因为图层需要移动,所以图层要略大于容器,容器要做溢出隐藏
}
header>div{
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;//直接flex居中,太美了这东西
justify-content: center;
align-items: center;
--offset: 0px;//这里定义css的变量方便后面js的处理
--blur: 2px;
}
header>div>img{
display: block;
width: 110%;//放大点,移动的时候不会有空白溢出
height: 100%;
object-fit: cover;
transform: translateX(var(--offset));//变量的使用
filter: blur(var(--blur));
}
接下来就是js代码,可能我写的比较繁琐,但是实现的效果我觉得和b站的很像
var startX = 0
const images = document.querySelectorAll('header>div>img')//拿到img元素的数组
document.querySelector('header').addEventListener('mouseover', (e)=>{
startX = e.clientX//在鼠标移入的时候记录初始位置
for (let [index, image] of images.entries()) {
image.style.transition = 'none'//在每次鼠标进入的时候让每一项的渐变效果为none,防止鼠标移动时看起来不连贯
}
})
document.querySelector('header').addEventListener('mousemove', (e)=>{
let offsetX = e.clientX - startX + 482//这里是保证初始值为482
if(Math.abs(offsetX - 482) < 10)offsetX = 482//鼠标刚刚移动上去会产生很小的偏移,看起来效果就好像图层突然变换了位置,所以做了一个移动最小值检测,这样就没有问题了
let percentage = offsetX / window.outerWidth//计算移动距离占视窗宽度的百分比,其实我们的主要目的是拿到这个东西
let offset = 15 * percentage//把百分比放大合适的比例就能带入公式了
let blur = 20//这里是二次函数公式中二次项的系数,20是比较合适的
for (let [index, image] of images.entries()) {
offset *= 1.3//每次循环对偏移位置进行迭代
let blurValue = (Math.pow(((index / images.length - percentage)), 2) * blur)//每次循环对清晰度进行迭代,这里就使用了刚刚提到的二次函数的形式
image.style.setProperty('--offset',`${offset}px`)//赋值回去,实现效果
image.style.setProperty('--blur',`${blurValue}px`)
}
})
document.querySelector('header').addEventListener('mouseout', ()=>{
//其实这里完全就是偷了个懒,直接用程序去计算初始位置,我测出来当offset为482的时候,初始效果看起来是最棒的,就不用看了下面的就是上面的公式而已
let offsetX = 482
let blur = 20
let percentage = offsetX / window.outerWidth
let offset = 15 * percentage
for (let [index, image] of images.entries()) {
offset *= 1.3
blurValue = (Math.pow((index / images.length - percentage), 2) * blur)
image.style.setProperty('--offset',`${offset}px`)
image.style.setProperty('--blur',`${blurValue}px`)
image.style.transition = 'all .3s ease'//给每一项加上渐变效果,图层会平滑回到初始位置,视觉效果不会太突兀
}
})
window.addEventListener('load', () => {
//这里就是在开始加载时计算初始位置.
let offsetX = 482
let blur = 20
let percentage = offsetX / window.outerWidth
let offset = 15 * percentage
for (let [index, image] of images.entries()) {
offset *= 1.3
blurValue = (Math.pow((index / images.length - percentage), 2) * blur)
image.style.setProperty('--offset',`${offset}px`)
image.style.setProperty('--blur',`${blurValue}px`)
}
})
其实上面完全没必要写那么冗长的代码,给每一项设置一个初始的偏移位置和初始清晰度就好了,这样代码也能简洁.大家可以自己想想怎么改,其实很简单.
参考了b站的
CodingStartup起码课
下面放出本人的完整代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
body{
margin: 0;
}
header{
height: 155px;
position: relative;
overflow: hidden;
}
header>div{
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
--offset: 0px;
--blur: 2px;
}
header>div>img{
display: block;
width: 110%;
height: 100%;
object-fit: cover;
transform: translateX(var(--offset));
filter: blur(var(--blur));
}
</style>
</head>
<body>
<header>
<div><img src="./img/bilibili-autumn-1.webp"></div>
<div><img src="./img/bilibili-autumn-2.webp"></div>
<div><img src="./img/bilibili-autumn-3.webp"></div>
<div><img src="./img/bilibili-autumn-4.webp"></div>
<div><img src="./img/bilibili-autumn-5.webp"></div>
<div><img src="./img/bilibili-autumn-6.webp"></div>
</header>
<script>
var startX = 0
const images = document.querySelectorAll('header>div>img')
document.querySelector('header').addEventListener('mousemove', (e)=>{
let offsetX = e.clientX - startX + 482
let percentage = offsetX / window.outerWidth
let offset = 15 * percentage
let blur = 20
for (let [index, image] of images.entries()) {
offset *= 1.3
let blurValue = (Math.pow(((index / images.length - percentage)), 2) * blur)
image.style.setProperty('--offset',`${offset}px`)
image.style.setProperty('--blur',`${blurValue}px`)
}
})
document.querySelector('header').addEventListener('mouseover', (e)=>{
startX = e.clientX
for (let [index, image] of images.entries()) {
image.style.transition = 'none'
}
})
document.querySelector('header').addEventListener('mouseout', ()=>{
let offsetX = 482
let blur = 20
let percentage = offsetX / window.outerWidth
let offset = 15 * percentage
for (let [index, image] of images.entries()) {
offset *= 1.3
blurValue = (Math.pow((index / images.length - percentage), 2) * blur)
image.style.setProperty('--offset',`${offset}px`)
image.style.setProperty('--blur',`${blurValue}px`)
image.style.transition = 'all .3s ease'
}
})
window.addEventListener('load', () => {
let offsetX = 482
let blur = 20
let percentage = offsetX / window.outerWidth
let offset = 15 * percentage
for (let [index, image] of images.entries()) {
offset *= 1.3
blurValue = (Math.pow((index / images.length - percentage), 2) * blur)
image.style.setProperty('--offset',`${offset}px`)
image.style.setProperty('--blur',`${blurValue}px`)
}
})
</script>
</body>
</html>
thanks for reading