Hexo设置动态背景图的动态更新

文章目录

  • 前言
  • 动态的背景图
  • 更新图库
  • 时间计算
  • 修改src属性还是直接将整个img标签替换掉
  • 代码实现
    • 保存位置
    • 增加代码
    • 没什么必要的其他东西
    • 两篇文章的全部代码
  • 修改为开关形式
  • 目前还没完善的

前言

在这篇文章中,我们学习了如何设置固定数量的动态背景图。接下来,我们在这篇文章的基础上继续增加功能,让动态背景图的数量也不是固定的、图像也不是固定的。

顺便一提,预览效果点击这里查看。

动态的背景图

在这里,我个人想从两个方面解释动态

一方面是当前呈现的图片是不断轮转的,这是轮播图的基本定义。

另一方面就是呈现的图片并不是一开始就固定好的,而是在轮播的过程中不断更新的。

第一个方面我们已经实现了,在这里也就不多赘述。接下来我们思考如何实现第二个方面。

更新图库

为了响应速度更快,我个人倾向于将loading期间加载的内容固定下来,主要是为了能够让loading期间有一个基本的轮播图。

然后,在第一张图片轮转消失后,更新第一张图片。这样的话,显示的是第二张图片,而第一张图片已经在你看不见的地方更新掉了。当回到第一张图片的时候,我们发现轮转出来的是一张新的图片。

这样就能够分两个阶段给出一大批背景图片了。

时间计算

还记得上一篇文章吗,我们使用 64 s 64s 64s的时间作为一整轮的轮播动画时间。这也就意味着,算上淡入淡出,一张图片的整个周期也就是 64 ÷ 4 = 16 ( s ) 64{\div}4=16(s) 64÷4=16(s)

为了让第一张图片在这个周期之外更新,我们所需要做的,首先就是确定 16 s 16s 16s的周期执行更新动作。

但是,光有这个 16 s 16s 16s可不够。我们都知道,当浏览器加载过一次图片之后,下一次加载该图片将从缓存中直接读取,也就是我们寻常所说的命中缓存

命中后,加载速度是相当迅猛的。我们不能赌他的枪里没有子弹。所以我们还需要额外设计一个定时器,要让这个 16 s 16s 16s的周期稍微缓上一缓,才能够保证更新时间是处于我们所看不到的时间段内。

还记得上一篇文章的流程图吗:

25%
25%
25%
25%
第一张图片
第二张图片
第三张图片
第四张图片

我们主要是额外增加一个逻辑,让图片能够跟着时间有些修改:

计数器
同步更新
轮播图
25%
25%
25%
25%
=0
=1
=2
=3
count mod 4
count
count = 0
等待2s
更新图片
等待25%的动画结束
第一张图片
第四张图片
第三张图片
第二张图片

当然,除了mod计算,还有满 4 4 4归零的方法,可以画成这样:

计数器
同步更新
轮播图
25%
25%
25%
25%
yes
no
+1
+1
+1
count = 0
count - 4
count = 4 ?
count = 0
count = 1
count = 2
count = 3
+1
等待2s
更新图片
等待25%的动画结束
第一张图片
第四张图片
第三张图片
第二张图片

需要注意的是,在这里更新图片并不是与图片动画同步的。我这边设置的是当图片转过去之后再更改,也就是说,这里还有一个判断当前轮转索引与修改索引之间的关系。

计数器
同步更新
轮播图
25%
25%
25%
25%
count+2 mod 4
更新即触发
yes
no
+1
+1
+1
index
count = 4 ?
count = 0
count - 4
count = 0
count = 1
count = 2
count = 3
+1
等待2s
更新图片
等待25%的动画结束
第一张图片
第四张图片
第三张图片
第二张图片

修改src属性还是直接将整个img标签替换掉

在这里,主要讨论的就是更新图片过程中,我们是修改src还是直接将整个img标签替换掉。

如果你有一定的经验,你会发现,如果src相同,则会直接命中缓存,不会更新图片。对于我们目前而言似乎确实可以使用,因为我们的背景图片并不会随意修改,而是始终保持链接。 当然,如果你有更多的经验,你会想到,在GET请求中加上一个时间戳,浏览器就会误以为这是一次新的请求,能够保证始终更新图片。

而如果是整个img直接替换掉,浏览器就会开始渲染流程,能够确保本次请求是一定能够被加载的。

当然,每种方法都有自己的优势跟弊端,这就需要各位自行判断了。接下来我将以替换整个img为例进行说明。

代码实现

好了,又到了show me the code环节。

保存位置

我们还是选择source/js/utils.js文件,在我们最开始增加的addBackgroundImageDiv方法的最下面继续增加:

增加代码

// set background image url after rolling
// --------------------------------------
let count = 0, index = 0;
const imgUrls = { 'pc': [
  '/race-miku.jpg', '/masuri-miku.jpg', '/planet-miku.jpg', '/4mikus.jpg', '/84672028_p0.jpg', '/84932457_p0.png'
] }
document.onreadystatechange = function () {
  if (document.readyState === 'complete') {
    console.log('done');
    let imgChangeInterval = null;
    let imageChangeTimeOut = setTimeout(function () {
      console.log('timeout');
      if (imgChangeInterval != null) {
        clearInterval(imgChangeInterval);
        imgChangeInterval = null;
      }
      imgChangeInterval = setInterval(function () {
        index = (count + 2) % 4
        const imageDivElement = document.getElementById("image-scroller").children[count % 4];
        let sampleImg = Math.floor(Math.random() * imgUrls[DEVICES[0]].length);
        imageDivElement.innerHTML = " +
          " src='" + BASE_URL + DEVICES[0] + imgUrls[DEVICES[0]][sampleImg] + "'" +
          " style='width: 100%; height: 100%;'" +
          " alt='network broken?' />";
        console.log(`changed, now is ${count % 4} and ${imgUrls[DEVICES[0]][sampleImg]}`) // yes
        count = (count + 1) % 4;
      }, 64000 / 4);
      clearTimeout(imageChangeTimeOut);
    }, 2000);
  }
}
// well done! now images can be updated!

这些代码能够按照约定更新图片,达到更多图片的轮播效果。虽然本文展示案例的时候只用了 2 2 2个图片(有 4 4 4个是上一篇固定的),但是使用本文的代码就能够实现更多的图片了。

没什么必要的其他东西

同时,在代码中我也留下了三个节点的console.log方法,能够让各位能够感受到浏览器页面加载状态改变为complete的时候、TimeOut被触发的时候、Interval被触发的时候。

如果不出意外的话,complete输出将会很快出现,然后在 2 s 2s 2s后出现timeout,接下来每 16 s 16s 16s产生changed输出。

两篇文章的全部代码

如果你是单纯复制本篇文章中的内容,可能并不能运行起来。因为诸如DEVICESBASE_URL这类变量是上一篇文章中定义的变量。

所以,在这里,我将给出两篇文章的全部代码:

// 这是官方的自启动函数
(function() {
  const onPageLoaded = () => document.dispatchEvent(
    new Event('page:loaded', {
      bubbles: true
    })
  );
  if (document.readyState === 'loading') {
    document.addEventListener('readystatechange', onPageLoaded, { once: true });
    addBackgroundImageDiv(); // 我在这里增加了自己的内容
  } else {
    onPageLoaded();
  }

  document.addEventListener('pjax:success', onPageLoaded);
})();

// 然后就是实现自己的内容
// add our custom dynamic background image
function addBackgroundImageDiv () {
  // create dom element for background images
  // ----------------------------------------
  const opacityMask = document.createElement("div");
  opacityMask.style.background = "linear-gradient(#fff, #ffced9, #fff)";
  opacityMask.style.position = "fixed";
  opacityMask.style.top = "0";
  opacityMask.style.left = "0";
  opacityMask.style.content = "";
  opacityMask.style.width = "100%";
  opacityMask.style.height = "100%";
  opacityMask.style.opacity = "0.8";
  opacityMask.style.zIndex = "-2";
  const imageContainer = document.createElement("div");
  imageContainer.style.position = "fixed";
  imageContainer.style.top = "0";
  imageContainer.style.left = "0";
  imageContainer.style.content = "";
  imageContainer.style.width = "100%";
  imageContainer.style.height = "100%";
  imageContainer.style.zIndex = "-3";
  const imageScroller = document.createElement("div");
  imageScroller.id = "image-scroller";
  imageScroller.style.position = "fixed";
  imageScroller.style.top = "0";
  imageScroller.style.left = "0";
  imageScroller.style.content = "";
  imageScroller.style.width = "400%";
  imageScroller.style.height = "100%";
  imageScroller.style.display = "flex";
  imageContainer.style.justifyContent = "space-around";
  imageContainer.style.alignContent = "center";
  imageContainer.style.alignItems = "center";
  imageScroller.style.zIndex = "-4";
  document.body.appendChild(opacityMask);
  document.body.appendChild(imageContainer);
  document.body.appendChild(imageScroller);
  // well done! basic frames established!

  // url for background images
  // -------------------------
  const BASE_URL = 'http://images.sakebow.cn/bgimage/'
  const DEVICES = ['pc']
  const imgWindowUrl = { 'pc': [
    '/race-miku.jpg', '/masuri-miku.jpg', '/planet-miku.jpg', '/4mikus.jpg'
  ] };
  for (const imgUrlItem of imgWindowUrl['pc']) {
    const imageFrameItemContainer = document.createElement("div");
    imageFrameItemContainer.style.width = imageContainer.style.width;
    imageFrameItemContainer.style.height = "100%";
    imageFrameItemContainer.innerHTML = " +
      " src='" + BASE_URL + DEVICES[0] + imgUrlItem + "'" +
      " style='width: 100%; height: 100%;'" +
      " alt='network broken?' />";
    imageScroller.appendChild(imageFrameItemContainer);
  }
  // well done! all images ready!

  // keyframe to roll images
  // -------------------------------
  // create style element
  const imageRollStyle = document.createElement('style');
  // set animation time for all
  const EPOCH_TIME = 64;
  // set animation style for all
  const ANIMATION_DEFAULT_SETTINGS = "s linear infinite running ";
  // set keyframes into style element
  imageRollStyle.innerHTML = `@keyframes image-roll {
    0%  { left: 0; } 24% { left: 0; } 25% { left: -100%; } 49% { left: -100%; } 50% { left: -200%; }
    74% { left: -200%; } 75% { left: -300%; } 99% { left: -300%; } 100%{ left: 0; }
  }@keyframes image-translate-child-1 {
    0%  { scale: 1; opacity: 0 } 2% { scale: 1; opacity: 1; } 23% { scale: 1.1; } 25%, 100% { scale: 1.1; opacity: 0; }
  }#image-scroller>div:nth-child(1) {
    animation: ${EPOCH_TIME + ANIMATION_DEFAULT_SETTINGS} image-translate-child-1;
  }@keyframes image-translate-child-2 {
    0%, 25%  { scale: 1; opacity: 0 } 27% { scale: 1; opacity: 1; } 48% { scale: 1.1; } 50%, 100% { scale: 1.1; opacity: 0; }
  }#image-scroller>div:nth-child(2) {
    animation: ${EPOCH_TIME + ANIMATION_DEFAULT_SETTINGS} image-translate-child-2;
  }@keyframes image-translate-child-3 {
    0%, 50%  { scale: 1; opacity: 0 } 52% { scale: 1; opacity: 1; } 73% { scale: 1.1; } 75%, 100% { scale: 1.1; opacity: 0; }
  }#image-scroller>div:nth-child(3) {
    animation: ${EPOCH_TIME + ANIMATION_DEFAULT_SETTINGS} image-translate-child-3;
  }@keyframes image-translate-child-4 {
    0%, 75%  { scale: 1; opacity: 0 } 77% { scale: 1; opacity: 1; } 98% { scale: 1.1; } 100% { scale: 1.1; opacity: 0; }
  }#image-scroller>div:nth-child(4) {
    animation: ${EPOCH_TIME + ANIMATION_DEFAULT_SETTINGS} image-translate-child-4;
  }`;
  // well done! now images can be rolling with fadeIn and fadeOut style, as well as scale 1.1x slowly

  // 将style样式存放到head标签
  // ----------------------
  document.getElementsByTagName('head')[0].appendChild(imageRollStyle);
  imageScroller.style.animation = `${EPOCH_TIME + ANIMATION_DEFAULT_SETTINGS} image-roll`;
  // well done! keyframes in effect!

  // set background image url after rolling
  // --------------------------------------
  let count = 0, index = 0;
  const imgUrls = { 'pc': [
    '/race-miku.jpg', '/masuri-miku.jpg', '/planet-miku.jpg', '/4mikus.jpg', '/84672028_p0.jpg', '/84932457_p0.png'
  ] }
  document.onreadystatechange = function () {
    if (document.readyState === 'complete') {
      console.log('done');
      let imgChangeInterval = null;
      let imageChangeTimeOut = setTimeout(function () {
        console.log('timeout');
        if (imgChangeInterval != null) {
          clearInterval(imgChangeInterval);
          imgChangeInterval = null;
        }
        imgChangeInterval = setInterval(function () {
          index = (count + 2) % 4
          const imageDivElement = document.getElementById("image-scroller").children[count % 4];
          let sampleImg = Math.floor(Math.random() * imgUrls[DEVICES[0]].length);
          imageDivElement.innerHTML = " +
            " src='" + BASE_URL + DEVICES[0] + imgUrls[DEVICES[0]][sampleImg] + "'" +
            " style='width: 100%; height: 100%;'" +
            " alt='network broken?' />";
          console.log(`changed, now is ${count % 4} and ${imgUrls[DEVICES[0]][sampleImg]}`) // yes
          count = (count + 1) % 4;
        }, 64000 / 4);
        clearTimeout(imageChangeTimeOut);
      }, 2000);
    }
  }
  // well done! now images can be updated!
}

到这里,动态修改、动态显示的背景图片就实现了。

修改为开关形式

我们通过植入source/js/utils.js的形式实现了功能的植入,现在我们将这一整段拉出来,放在外面,并用config.yml控制是否打开开关。我们先看看官方示例。

你可能会怀疑哪里来的官方示例,那当然是源码中给的提示。比如NexT可以在themes/next/layout/_scripts/index.njk下看到这些内容:

{%- include 'vendors.njk' -%}

{{- next_js('comments.js') }}

{{- next_js('utils.js') }}
{%- if theme.motion.enable %}
  {{- next_js('motion.js') }}
{%- endif %}

{%- if theme.scheme === 'Muse' or theme.scheme === 'Mist' %}
  {{- next_js('schemes/muse.js') }}
{%- endif %}

{{- next_js('next-boot.js') }}
{%- if theme.bookmark.enable %}
  {{- next_js('bookmark.js') }}
{%- endif %}
{%- if theme.pjax %}
  {{- next_js('pjax.js') }}
{%- endif %}

然后去看themes/next/source/js文件夹下,还果然就是这些文件。

那么就是说,我只需要将代码放在这个文件夹下,然后再去themes/next/layout/_scripts/index.njk中引用不就好了?

于是,把上面的完整代码单独拉出来放入独立的javascript文件中,命名为add-background.js。同时,为了方便管理,把add-background.js文件放入自行创建的themes/next/source/js/custom文件夹中,然后引入:

{%- if theme.transparent.enable %}
  {{- next_js('custom/add-background.js') }}
{%- endif %}

于是,现在背景图片的轮播就能够随意开关了。只需要设置:

transparent:
  enable: true

就能够打开轮播,同样的,false能够关闭轮播。

目前还没完善的

比较尴尬的是,现在还需要长期维护这个列表。

后续的话,可能会在服务器端放一个管理器,或者在Hexo段放一个读取服务器所有文件的东西。也指不定什么时候写,总之先给一个优化思路在这里。

你可能感兴趣的:(Hexo,hexo,css,javascript)