三分钟复刻B站首页动态Banner(视差效果头图)

之前我尝试使用 JS 实现 B 站的首页 Banner,实现了基本1:1的复刻效果,一来是觉得很有意思,二来也是为了练习前端技术。与此同时我也写了一篇技术文章,详细讲解了整个实现过程的技术细节和原理:《如何用原生 JS 复刻 Bilibili 首页头图的视差交互效果

肝完文章时 B 站就更换了新的 Banner 图,文章发布一个月后又更新了中秋版本,这次更新也出现了此前未曾谋面的 video 元素效果,我又跃跃欲试,打算复刻一遍这个 Banner 效果。

新增视频类型

由于这次的 Banner 中增加了水面波光和萤火虫的复杂动态,所以加入了视频图层来展示,是 webm 格式的视频文件,体积非常小。在之前所写代码的基础上只需要增加一个类型字段,然后按类型创建标签即可:

js 复制代码
const child = document.createElement(item.tagName || 'img');
if (item.tagName === 'video') {
    child.loop=true; child.autoplay=true; child.muted=true;
}

其中我们为 video 标签加入了 loop 属性表示一直循环播放视频,autoplay 设置自动播放,但是在浏览器上这通常不会生效,所以得将 muted 也设置为 true 表示静音,这样视频就会默认自动播放了。

抓取资源

每次手动获取图层资源还是挺麻烦的,所以我想使用 node 编写一段脚本来自动下载我所需的资源,并根据每个 div 上的样式生成我所需的基础 json 数据。

由于 B 站的 Banner 是动态加载的,普通的网页抓取手段并不能获取资源,所以我们可以使用 puppeteer 加载网页,然后等待其资源加载完毕后获取网页中所有 Banner 图层元素:

js 复制代码
// 获取所有 ".layer" 元素
const layerElements = await page.$$(".animated-banner .layer");

接着我们使用 page.evaluate(() => {}, layerElements) 方法循环获取到的元素集合,拿到相对应的信息如 width height src style tagName transform 等,其中 src 是图片/视频资源对应的链接地址,图片为远程链接,视频则为 blob 协议链接,我们同样使用 evaluate 方法,将 src 作为方法的第二个参数传入,在网页中下载资源并保存到本地:

js 复制代码
const content = await page.evaluate(async (url) => {
      const response = await fetch(url);
      const buffer = await response.arrayBuffer();
      return { buffer: Array.from(new Uint8Array(buffer)) };
}, src);

这里我们使用浏览器原生方法 fetch 可以很方便地发起一个请求,得到响应数据后将其转为二进制的 arrayBuffer 数据,注意此时的操作都是在"浏览器"的环境当中,当我们获取完数据后,接收到的 content 变量则是在 node 环境中,接下来可对数据进行保存:

js 复制代码
const fileData = Buffer.from(content.buffer);
fs.writeFileSync(filePath, fileData);

使用 nodeBuffer.from 将其转化为 node 环境下的缓冲区,最后使用 fs 的方法操作写入即可,运行结果:

最后

代码我也整理为了 Github 仓库,之后每次 B 站更新都可以使用该仓库来收集,作为 B 站的历史 Banner 存档以供学习和欣赏。

在线网站地址

palxiao.github.io/bilibili-ba...

截至目前总共有三种 Banner 可以切换查看。

如何使用

拉取项目:

shell 复制代码
git clone https://github.com/palxiao/bilibili-banner.git

运行 pnpm iyarn / npm i 安装项目本地依赖;

运行 npm run serve 启动演示网页。

获取最新效果

如果当前 B 站 Banner 已更新,那么可以通过简单配置获取最新数据:

  1. 运行 npm run grab,自动在 assets 目录下生成数据(以当天日期命名)
  2. config.js 中添加配置(使用 fetch 引入 json)
js 复制代码
const banner_20231001 = await fetch('./assets/2023-10-01/data.json?r='+Math.random())

export default [{
        name: '海上明月 - 兔子',
        data: await banner_20231001.json()
    }, ...... ]

接下来需要对每个图层进行参数调试,具体步骤为:打开 assets 目录下对应的 data.json 文件,修改其中每个对象的参数,刷新网页查看效果。

目前支持参数如下:

属性 类型 说明
a number 表示加速度,数值越高移动变化越大(接受正负值)
deg number 表示旋转幅度,数值越高旋转越快(接受正负值)
g number 表示重力,数值越高上下移动变化越大(接受正负值)
f number 表示大小变化,对应 CSS transform: scale
opacity array 透明度变化,接收一个区间

注:正负值会影响变化的方向

思考

在这次中秋 Banner 的解析中我发现,DOM Tree 当中多出来一些完全无用的图层,它们被设置了 opacity: 0;,在 Banner 中并无论如何操作都不会展示,而 translate 则依旧会跟随变化。通过这一不合理现象可以确定,B 站的动态 Banner 并不是每次都由前端进行开发的,而是由设计师或产品PM直接进行设计和发布,应该有一个后台配置的界面。

而且这次更新的 Banner 存在视频图层,我猜测可能先在 PS 中制作了静态版的设计文件,然后其中两个动态图层再从视频工具中制作后导出。后咨询了设计师朋友,他则认为整个设计文件应该在 AE 这类软件中完整制作导出比较合理。接下来设计师将如何导出数据上传及调试,如何高效率协作,这背后他们一定做了不少努力和尝试。

以上就是文章的全部内容了,感谢看到这里!如果觉得写得还不错,对你有所帮助或启发,别忘了点赞收藏关注"一键三连"哦~ 我是茶无味de一天(公众号: 品味前端),一名平凡的前端 Developer,希望与你共同成长~

相关推荐
gqkmiss19 分钟前
Chrome 浏览器插件获取网页 iframe 中的 window 对象
前端·chrome·iframe·postmessage·chrome 插件
m0_748247552 小时前
Web 应用项目开发全流程解析与实战经验分享
开发语言·前端·php
m0_748255023 小时前
前端常用算法集合
前端·算法
真的很上进3 小时前
如何借助 Babel+TS+ESLint 构建现代 JS 工程环境?
java·前端·javascript·css·react.js·vue·html
web130933203983 小时前
vue elementUI form组件动态添加el-form-item并且动态添加rules必填项校验方法
前端·vue.js·elementui
NiNg_1_2344 小时前
Echarts连接数据库,实时绘制图表详解
前端·数据库·echarts
如若1234 小时前
对文件内的文件名生成目录,方便查阅
java·前端·python
滚雪球~5 小时前
npm error code ETIMEDOUT
前端·npm·node.js
沙漏无语5 小时前
npm : 无法加载文件 D:\Nodejs\node_global\npm.ps1,因为在此系统上禁止运行脚本
前端·npm·node.js
supermapsupport5 小时前
iClient3D for Cesium在Vue中快速实现场景卷帘
前端·vue.js·3d·cesium·supermap