bilibili 动态背景 winter || autumn || spring,刷新随机顶部 cover,本篇不做教程,仅作为记录艰辛的过程 📝

本来集成成了好几个 pug 结果发现并不能结构化引入,最后还是变成了 js 操作,难受难受,果然写习惯了 vue, react 再回来就不熟练了

坎坷的失败品

本来拆分出来了了以下几个 pug,结果发现完全用不了

autumn.pug

.blqbanner.mobile-hidden
  div
    img(src='https://npm.elemecdn.com/anzhiyu-blog@2.1.7/img/banner/autumn/bilibili-autumn-1.webp')
  div
    img(src='https://npm.elemecdn.com/anzhiyu-blog@2.1.7/img/banner/autumn/bilibili-autumn-2.png')
  div
    img(src='https://npm.elemecdn.com/anzhiyu-blog@2.1.7/img/banner/autumn/bilibili-autumn-3.png')
  div
    img(src='https://npm.elemecdn.com/anzhiyu-blog@2.1.7/img/banner/autumn/bilibili-autumn-4.png')
  div
    img(src='https://npm.elemecdn.com/anzhiyu-blog@2.1.7/img/banner/autumn/bilibili-autumn-5.png')
  div
    img(src='https://npm.elemecdn.com/anzhiyu-blog@2.1.7/img/banner/autumn/bilibili-autumn-6.png')

link(rel="stylesheet", href=url_for("/css/biliBg/autumn/autumn.css"))
script(src=url_for("/js/biliBg/autumn/autumn.js"))

spring.pug

.bili-banner
  .animated-banner
    //-  背景
    .layer
      img(src='https://npm.elemecdn.com/anzhiyu-blog@2.1.7/img/banner/spring/bg1.png', data-height='360', data-width='9666', height='180', width='4833', style='transform: scale(1) translate(0px, -15px) rotate(0deg); opacity: 1;')
    //-  左山
    .layer
      img(src='https://npm.elemecdn.com/anzhiyu-blog@2.1.7/img/banner/spring/bg2.png', data-height='360', data-width='9666', height='180', width='4833', style='transform: scale(1) translate(1100px, 0px) rotate(0deg); opacity: 1;')
    //-  右山
    .layer
      img(src='https://npm.elemecdn.com/anzhiyu-blog@2.1.7/img/banner/spring/bg3.png', data-height='360', data-width='3523', height='162', width='1585', style='transform: scale(1) translate(675px, 0px) rotate(0deg); opacity: 1;')
    //-  左桥
    .layer
      img(src='https://npm.elemecdn.com/anzhiyu-blog@2.1.7/img/banner/spring/bg4.png', data-height='360', data-width='2938', height='176', width='1439', style='transform: scale(1) translate(-637px, 0px) rotate(0deg); opacity: 1;')
    //-  右船
    .layer
      img(src='https://npm.elemecdn.com/anzhiyu-blog@2.1.7/img/banner/spring/bg5.png', data-height='139', data-width='556', height='62', width='250', style='transform: scale(1) translate(607.5px, 45px) rotate(0deg); opacity: 1;')
    //-  开船人物
    .layer
      img(src='https://npm.elemecdn.com/anzhiyu-blog@2.1.7/img/banner/spring/p4.png', data-height='302', data-width='734', height='84', width='205', style='transform: scale(1) translate(252px, 36.4px) rotate(0deg); opacity: 0;')
    //-  中草坪+.layer
      img(src='https://npm.elemecdn.com/anzhiyu-blog@2.1.7/img/banner/spring/bg6.png', data-height='180', data-width='1757', height='125', width='1229', style='transform: scale(1) translate(112px, 25px) rotate(0deg); opacity: 1;')
    //-  中草坪+风筝
    .layer
      img(src='https://npm.elemecdn.com/anzhiyu-blog@2.1.7/img/banner/spring/bg7.png', data-height='116', data-width='1757', height='81', width='1229', style='transform: scale(1) translate(-350px, 49px) rotate(0deg); opacity: 1;')
    //-  人物
    .layer
      img(src='https://npm.elemecdn.com/anzhiyu-blog@2.1.7/img/banner/spring/p2.png', data-height='346', data-width='497', height='138', width='198', style='transform: scale(1) translate(-240px, 16px) rotate(0deg); opacity: 0;')
    //-  人物
    .layer
      img(src='https://npm.elemecdn.com/anzhiyu-blog@2.1.7/img/banner/spring/p1.png', data-height='256', data-width='146', height='102', width='58', style='transform: scale(1) translate(-340px, 32px) rotate(0deg); opacity: 0;')
    //-  中树
    .layer
      img(src='https://npm.elemecdn.com/anzhiyu-blog@2.1.7/img/banner/spring/t1.png', data-height='254', data-width='602', height='114', width='270', style='transform: scale(1) translate(-90px, 13.5px) rotate(0deg); opacity: 1;')
    //-  中草坪+.layer
      img(src='https://npm.elemecdn.com/anzhiyu-blog@2.1.7/img/banner/spring/bg8.png', data-height='360', data-width='4277', height='180', width='2138', style='transform: scale(1) translate(100px, 0px) rotate(0deg); opacity: 1;')
    //-  中人物
    .layer
      img(src='https://npm.elemecdn.com/anzhiyu-blog@2.1.7/img/banner/spring/p3.png', data-height='327', data-width='933', height='147', width='419', style='transform: scale(1) translate(216px, 13.5px) rotate(0deg); opacity: 1;')
    //-  右树
    .layer
      img(src='https://npm.elemecdn.com/anzhiyu-blog@2.1.7/img/banner/spring/t3.png', data-height='353', data-width='740', height='211', width='444', style='transform: scale(1) translate(2100px, 0px) rotate(0deg); filter: blur(2px); opacity: 1;')
    //-  左树
    .layer
      img(src='https://npm.elemecdn.com/anzhiyu-blog@2.1.7/img/banner/spring/t2.png', data-height='360', data-width='1916', height='180', width='958', style='transform: scale(1) translate(-1000px, 0px) rotate(0deg); filter: blur(1px); opacity: 1;')
    canvas#springCanvas(width='1519', height='155', style='position: absolute; top: 0px; left: 0px;    width: 100%;')

link(rel="stylesheet", href=url_for("/css/biliBg/spring/spring.css"))
script(src=url_for("/js/biliBg/spring/spring.js") data-pjax)

winter.pug

.bldbanner
  .bldview
    img.morning(src='https://npm.elemecdn.com/anzhiyu-blog@2.1.7/img/banner/winter/bilibili-winter-view-1.webp')
    img.afternoon(src='https://npm.elemecdn.com/anzhiyu-blog@2.1.7/img/banner/winter/bilibili-winter-view-2.webp')
    img.ball(src='https://npm.elemecdn.com/anzhiyu-blog@2.1.7/img/banner/winter/bilibili-winter-ball.png')
    video.evening(autoplay, loop, muted)
      source(src='https://npm.elemecdn.com/anzhiyu-blog@2.1.7/img/banner/winter/bilibili-winter-view-3.webm', type='video/webm')
    img.window-cover(src='https://npm.elemecdn.com/anzhiyu-blog@2.1.7/img/banner/winter/bilibili-winter-view-3-snow.png')
  .tree
    img.morning(src='https://npm.elemecdn.com/anzhiyu-blog@2.1.7/img/banner/winter/bilibili-winter-tree-1.png')
    img.afternoon(src='https://npm.elemecdn.com/anzhiyu-blog@2.1.7/img/banner/winter/bilibili-winter-tree-2.png')
    img.evening(src='https://npm.elemecdn.com/anzhiyu-blog@2.1.7/img/banner/winter/bilibili-winter-tree-3.png')

link(rel="stylesheet", href=url_for("/css/biliBg/winter/winter.css"))
script(src=url_for("/js/biliBg/winter/winter.js"))

然后发现 pug 居然不能动态引入组件化,因为在编译的时候就已经确定 html 结构了,然而我还是在已经上线后才知道,还曾一度怀疑是 cdn 的缓存 🤡。

最后修改改改还是磕磕绊绊的完成了春秋冬的集成,就是这 js 代码我真的觉得好难受。

魔改记录

判断文章是否需要随机 banner

修改 themes/butterfly/layout/includes/header/index.pug,引入我们的 bili-banner,首先判断文章顶部是否有 bilibili_bg,如果有就引入 bilibili 随机 banner

header#page-header(class=`${isHomeClass}`)
  !=partial('includes/header/nav', {}, {cache: true})
  if top_img !== false
    if is_post()
      include ./post-info.pug
+     if page.bilibili_bg
+       !=partial('includes/bili-banner/index')

新建 themes/butterfly/layout/includes/bili-banner/index.pug,这段直接引入所需要的 js

//- 随机背景
script(src=url_for("/js/biliBg/biliBg.js"), data-pjax)

新建source/js/biliBg/biliBg.js, 动态插入节点

var biliBannerIndex = Math.floor(Math.random() * 3);
var $biliBannerParentsEl = document.getElementById("page-header");
//- 创建脚本节点
var biliBannerScript = document.createElement("script");
biliBannerScript.type = "text/javascript";
console.info(biliBannerIndex);
if (biliBannerIndex === 0) {
  //- autumn
  $biliBannerParentsEl.insertAdjacentHTML(
    "beforeend",
    `
      <div class="blqbanner mobile-hidden">
        <div><img src="https://npm.elemecdn.com/anzhiyu-blog@2.1.7/img/banner/autumn/bilibili-autumn-1.webp"/></div>
        <div><img src="https://npm.elemecdn.com/anzhiyu-blog@2.1.7/img/banner/autumn/bilibili-autumn-2.png"/></div>
        <div><img src="https://npm.elemecdn.com/anzhiyu-blog@2.1.7/img/banner/autumn/bilibili-autumn-3.png"/></div>
        <div><img src="https://npm.elemecdn.com/anzhiyu-blog@2.1.7/img/banner/autumn/bilibili-autumn-4.png"/></div>
        <div><img src="https://npm.elemecdn.com/anzhiyu-blog@2.1.7/img/banner/autumn/bilibili-autumn-5.png"/></div>
        <div><img src="https://npm.elemecdn.com/anzhiyu-blog@2.1.7/img/banner/autumn/bilibili-autumn-6.png"/></div>
      </div>
      <link rel="stylesheet", href="/css/biliBg/autumn/autumn.css" />
    `
  );

  //- 插入js节点
  biliBannerScript.src = "https://npm.elemecdn.com/anzhiyu-blog@2.1.7/js/biliBg/autumn/autumn.js";
  $biliBannerParentsEl.appendChild(biliBannerScript);
} else if (biliBannerIndex === 1) {
  //- spring
  $biliBannerParentsEl.insertAdjacentHTML(
    "beforeend",
    `
      <div class="bili-banner">
        <div class="animated-banner">
            <div class="layer"><img src="https://npm.elemecdn.com/anzhiyu-blog@2.1.7/img/banner/spring/bg1.png" data-height="360" data-width="9666" height="180" width="4833" style="transform: scale(1) translate(0px, -15px) rotate(0deg); opacity: 1;"/></div>
            <div class="layer"><img src="https://npm.elemecdn.com/anzhiyu-blog@2.1.7/img/banner/spring/bg2.png" data-height="360" data-width="9666" height="180" width="4833" style="transform: scale(1) translate(1100px, 0px) rotate(0deg); opacity: 1;"/></div>
            <div class="layer"><img src="https://npm.elemecdn.com/anzhiyu-blog@2.1.7/img/banner/spring/bg3.png" data-height="360" data-width="3523" height="162" width="1585" style="transform: scale(1) translate(675px, 0px) rotate(0deg); opacity: 1;"/></div>
            <div class="layer"><img src="https://npm.elemecdn.com/anzhiyu-blog@2.1.7/img/banner/spring/bg4.png" data-height="360" data-width="2938" height="176" width="1439" style="transform: scale(1) translate(-637px, 0px) rotate(0deg); opacity: 1;"/></div>
            <div class="layer"><img src="https://npm.elemecdn.com/anzhiyu-blog@2.1.7/img/banner/spring/bg5.png" data-height="139" data-width="556" height="62" width="250" style="transform: scale(1) translate(607.5px, 45px) rotate(0deg); opacity: 1;"/></div>
            <div class="layer"><img src="https://npm.elemecdn.com/anzhiyu-blog@2.1.7/img/banner/spring/p4.png" data-height="302" data-width="734" height="84" width="205" style="transform: scale(1) translate(252px, 36.4px) rotate(0deg); opacity: 0;"/></div>
            <div class="layer"><img src="https://npm.elemecdn.com/anzhiyu-blog@2.1.7/img/banner/spring/bg6.png" data-height="180" data-width="1757" height="125" width="1229" style="transform: scale(1) translate(112px, 25px) rotate(0deg); opacity: 1;"/></div>
            <div class="layer"><img src="https://npm.elemecdn.com/anzhiyu-blog@2.1.7/img/banner/spring/bg7.png" data-height="116" data-width="1757" height="81" width="1229" style="transform: scale(1) translate(-350px, 49px) rotate(0deg); opacity: 1;"/></div>
            <div class="layer"><img src="https://npm.elemecdn.com/anzhiyu-blog@2.1.7/img/banner/spring/p2.png" data-height="346" data-width="497" height="138" width="198" style="transform: scale(1) translate(-240px, 16px) rotate(0deg); opacity: 0;"/></div>
            <div class="layer"><img src="https://npm.elemecdn.com/anzhiyu-blog@2.1.7/img/banner/spring/p1.png" data-height="256" data-width="146" height="102" width="58" style="transform: scale(1) translate(-340px, 32px) rotate(0deg); opacity: 0;"/></div>
            <div class="layer"><img src="https://npm.elemecdn.com/anzhiyu-blog@2.1.7/img/banner/spring/t1.png" data-height="254" data-width="602" height="114" width="270" style="transform: scale(1) translate(-90px, 13.5px) rotate(0deg); opacity: 1;"/></div>
            <div class="layer"><img src="https://npm.elemecdn.com/anzhiyu-blog@2.1.7/img/banner/spring/bg8.png" data-height="360" data-width="4277" height="180" width="2138" style="transform: scale(1) translate(100px, 0px) rotate(0deg); opacity: 1;"/></div>
            <div class="layer"><img src="https://npm.elemecdn.com/anzhiyu-blog@2.1.7/img/banner/spring/p3.png" data-height="327" data-width="933" height="147" width="419" style="transform: scale(1) translate(216px, 13.5px) rotate(0deg); opacity: 1;"/></div>
            <div class="layer"><img src="https://npm.elemecdn.com/anzhiyu-blog@2.1.7/img/banner/spring/t3.png" data-height="353" data-width="740" height="211" width="444" style="transform: scale(1) translate(2100px, 0px) rotate(0deg); filter: blur(2px); opacity: 1;"/></div>
            <div class="layer"><img src="https://npm.elemecdn.com/anzhiyu-blog@2.1.7/img/banner/spring/t2.png" data-height="360" data-width="1916" height="180" width="958" style="transform: scale(1) translate(-1000px, 0px) rotate(0deg); filter: blur(1px); opacity: 1;"/></div>
            <canvas id="springCanvas" width="1519" height="155" style="position: absolute; top: 0px; left: 0px;width: 100%;"></canvas>
        </div>
      </div>
      <link rel="stylesheet", href="/css/biliBg/spring/spring.css" />
    `
  );

  //- 插入js节点
  biliBannerScript.src = "https://npm.elemecdn.com/anzhiyu-blog@2.1.7/js/biliBg/spring/spring.js";
  $biliBannerParentsEl.appendChild(biliBannerScript);
} else if (biliBannerIndex === 2) {
  //- winter
  $biliBannerParentsEl.insertAdjacentHTML(
    "beforeend",
    `
      <div class="bldbanner">
        <div class="bldview"><img class="morning" src="https://npm.elemecdn.com/anzhiyu-blog@2.1.7/img/banner/winter/bilibili-winter-view-1.webp"/><img class="afternoon" src="https://npm.elemecdn.com/anzhiyu-blog@2.1.7/img/banner/winter/bilibili-winter-view-2.webp"/><img class="ball" src="https://npm.elemecdn.com/anzhiyu-blog@2.1.7/img/banner/winter/bilibili-winter-ball.png"/>
          <video class="evening" autoplay="autoplay" loop="loop" muted="muted">
            <source src="https://npm.elemecdn.com/anzhiyu-blog@2.1.7/img/banner/winter/bilibili-winter-view-3.webm" type="video/webm"/>
          </video><img class="window-cover" src="https://npm.elemecdn.com/anzhiyu-blog@2.1.7/img/banner/winter/bilibili-winter-view-3-snow.png"/>
        </div>
        <div class="tree"><img class="morning" src="https://npm.elemecdn.com/anzhiyu-blog@2.1.7/img/banner/winter/bilibili-winter-tree-1.png"/><img class="afternoon" src="https://npm.elemecdn.com/anzhiyu-blog@2.1.7/img/banner/winter/bilibili-winter-tree-2.png"/><img class="evening" src="https://npm.elemecdn.com/anzhiyu-blog@2.1.7/img/banner/winter/bilibili-winter-tree-3.png"/></div>
      </div>
      <link rel="stylesheet", href="/css/biliBg/winter/winter.css" />
    `
  );

  //- 插入js节点
  biliBannerScript.src = "https://npm.elemecdn.com/anzhiyu-blog@2.1.7/js/biliBg/winter/winter.js";
  $biliBannerParentsEl.appendChild(biliBannerScript);
}

部分适配 css

.post-bg:has(.bldbanner) #post-info:after,
.post-bg:has(.bili-banner) #post-info:after,
.post-bg:has(.blqbanner) #post-info:after {
  box-shadow: 0px -214px 287px 45px var(--anzhiyu-black-op) inset;
}
.post-bg:has(.bldbanner) #post-info .post-meta,
.post-bg:has(.bili-banner) #post-info .post-meta,
.post-bg:has(.blqbanner) #post-info .post-meta {
  pointer-events: all;
}
.post-bg:has(.bldbanner) #post-info,
.post-bg:has(.bili-banner) #post-info,
.post-bg:has(.blqbanner) #post-info {
  pointer-events: none;
}
#page-header.post-bg:has(.bldbanner),
#page-header.post-bg:has(.bili-banner),
#page-header.post-bg:has(.blqbanner) {
  height: 15rem;
}

春 spring.css

.bili-banner {
  margin: 0 auto;
  position: relative;
  z-index: 0;
  width: 100%;
  height: 11rem;
  min-height: 155px;
  min-width: 999px;
  background-color: #f9f9f9;
  display: flex;
  justify-content: center;
  margin-top: 60px;
}
.bili-banner .animated-banner {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  margin: auto;
  overflow: hidden;
}

.bili-banner .layer {
  position: absolute;
  left: 0;
  top: 0;
  height: 100%;
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
}

.bili-banner .layer img {
  transition: transform 0.2s;
}

秋 autumn.css

.blqbanner {
  height: 15rem;
  position: relative;
  overflow: hidden;
}

.blqbanner > div {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  /*    将图片上下左右居中*/
  display: flex;
  justify-content: center;
  align-items: center;
  --offset: 0px;
  --blur: 2px;
}

.blqbanner > div > img {
  display: flex;
  width: 110%;
  /*    图片需要左右移动,预先设置的宽一些*/
  height: 100%;
  object-fit: cover;
  /*    将图片按照比例填满容器*/
  transform: translateX(var(--offset));
  filter: blur(var(--blur));
  transition: 0.3s;
}
.blqbanner > div:nth-child(1) > img {
  --offset: 1.38426px;
  --blur: 0.226766px;
}
.blqbanner > div:nth-child(2) > img {
  --offset: 1.79954px;
  --blur: 0.0724451px;
}
.blqbanner > div:nth-child(3) > img {
  --offset: 2.3394px;
  --blur: 1.02924px;
}
.blqbanner > div:nth-child(4) > img {
  --offset: 3.04122px;
  --blur: 3.09714px;
}
.blqbanner > div:nth-child(5) > img {
  --offset: 3.95358px;
  --blur: 6.27615px;
}
.blqbanner > div:nth-child(6) > img {
  --offset: 5.13966px;
  --blur: 10.5663px;
}

冬 winter.css

.bldbanner {
  min-height: 155px;
  height: 20rem;
  position: relative;
  overflow: hidden;
  --percentage: 0.5;
}

.bldbanner .tree,
.bldbanner .bldview {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0px;
  display: -webkit-box;
  display: -ms-flexbox;
  display: flex;
  -webkit-box-pack: center;
  -ms-flex-pack: center;
  justify-content: center;
  -webkit-box-align: center;
  -ms-flex-align: center;
  align-items: center;
  -webkit-transition: all 0.2s ease-in;
  transition: all 0.2s ease-in;
}

.bldbanner .bldview {
  -webkit-transform: translatex(calc(var(--percentage) * 100px));
  transform: translatex(calc(var(--percentage) * 100px));
}

.bldbanner .tree {
  -webkit-transform: translatex(calc(var(--percentage) * 150px - 25px));
  transform: translatex(calc(var(--percentage) * 150px - 25px));
  -webkit-filter: blur(3px);
  filter: blur(3px);
}

.bldbanner img,
.bldbanner video {
  position: absolute;
  height: 100%;
  max-width: none;
}

.bldbanner .evening {
  -webkit-transition: all 0.2s ease-in;
  transition: all 0.2s ease-in;
  z-index: 20;
  opacity: calc((0.5 - var(--percentage)) / 0.5);
}

.bldbanner .afternoon {
  -webkit-transition: all 0.2s ease-in;
  transition: all 0.2s ease-in;
  z-index: 10;
  opacity: calc(1 - (var(--percentage) - 0.5) / 0.5);
}

.bldbanner.moving .afternoon,
.bldbanner.moving .ball,
.bldbanner.moving .evening,
.bldbanner.moving .tree,
.bldbanner.moving .bldview {
  -webkit-transition: none;
  transition: none;
}

.bldbanner .ball {
  -webkit-transition: all 0.2s ease-in;
  transition: all 0.2s ease-in;
  z-index: 10;
  opacity: calc(1.5 - (var(--percentage) - 0.5) / 0.5);
  -webkit-transform: translate(calc(100px * var(--percentage)), 22px) rotate(calc(10deg * var(--percentage) + 5deg));
  transform: translate(calc(100px * var(--percentage)), 22px) rotate(calc(10deg * var(--percentage) + 5deg));
}

.bldbanner .window-cover {
  z-index: 20;
  opacity: calc(var(--percentage) * -2);
}

总结

总的来说还不错,巩固了我对于 canvas 的知识与 css 视差绘制。