kingkare 发表于 2026-2-15 01:33:18

给Hexo静态博客程序的Azhiyu(安知鱼)主题添加一个人潮汹涌模块


自定义人潮涌动主要增加了以下功能:

支持自定义人物角色图片的行数列数
支持对人物大小,密度,移动速度进行调节
支持隐藏右侧推荐大标签,扩展左侧宽度
调节home_top高度
自定义人潮涌动背景图
定义多张人物总图,每次访问时随机选取
people.js魔改
实现了可以输入任意长,宽,图片大小的功能。

也懒得对比哪里修改了,大家直接把下面覆盖自己的people.js得了(主要是忘了修改了那里了)
//Hexo\themes\anzhiyu\source\js\anzhiyu\people.js

"use strict";
function _toConsumableArray(e) {
return _arrayWithoutHoles(e) || _iterableToArray(e) || _unsupportedIterableToArray(e) || _nonIterableSpread();
}

function _nonIterableSpread() {
throw new TypeError(
    "Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a () method."
);
}

function _unsupportedIterableToArray(e, r) {
if (e) {
    if ("string" == typeof e) return _arrayLikeToArray(e, r);
    var t = Object.prototype.toString.call(e).slice(8, -1);
    return (
      "Object" === t && e.constructor && (t = e.constructor.name),
      "Map" === t || "Set" === t
      ? Array.from(e)
      : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t)
          ? _arrayLikeToArray(e, r)
          : void 0
    );
}
}

function _iterableToArray(e) {
if (("undefined" != typeof Symbol && null != e) || null != e["@@iterator"]) return Array.from(e);
}

function _arrayWithoutHoles(e) {
if (Array.isArray(e)) return _arrayLikeToArray(e);
}

function _arrayLikeToArray(e, r) {
(null == r || r > e.length) && (r = e.length);
for (var t = 0, a = new Array(r); t < r; t++) a = e;
return a;
}

function _classCallCheck(e, r) {
if (!(e instanceof r)) throw new TypeError("Cannot call a class as a function");
}

function _defineProperties(e, r) {
for (var t = 0; t < r.length; t++) {
    var a = r;
    (a.enumerable = a.enumerable || !1),
      (a.configurable = !0),
      "value" in a && (a.writable = !0),
      Object.defineProperty(e, a.key, a);
}
}

function _createClass(e, r, t) {
return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), e;
}
var peopleConfig = {
src: Array.isArray(GLOBAL_CONFIG.peoplecanvas.img)
    ? GLOBAL_CONFIG.peoplecanvas.img
    : GLOBAL_CONFIG.peoplecanvas.img,
background_image: GLOBAL_CONFIG.peoplecanvas.background_image || '',
rows: GLOBAL_CONFIG.peoplecanvas.rows || 15,
cols: GLOBAL_CONFIG.peoplecanvas.cols || 7,
scale: GLOBAL_CONFIG.peoplecanvas.scale || 0.3,
density: GLOBAL_CONFIG.peoplecanvas.density || 1.0,
speed: GLOBAL_CONFIG.peoplecanvas.speed || 1.0,
offsetY: GLOBAL_CONFIG.peoplecanvas.offsetY || 0,
},
randomRange = function (e, r) {
    return e + Math.random() * (r - e);
},
randomIndex = function (e) {
    return 0 | randomRange(0, e.length);
},
removeFromArray = function (e, r) {
    return e.splice(r, 1);
},
removeItemFromArray = function (e, r) {
    return removeFromArray(e, e.indexOf(r));
},
removeRandomFromArray = function (e) {
    return removeFromArray(e, randomIndex(e));
},
getRandomFromArray = function (e) {
    return e;
},
resetPeep = function (e) {
    var r,
      t,
      a = e.stage,
      n = e.peep,
      o = 0.5 < Math.random() ? 1 : -1,
      i = 100 - 250 * gsap.parseEase("power2.in")(Math.random()),
      s = a.height - n.height + i + peopleConfig.offsetY;
    return (
      1 == o ? ((r = -n.width), (t = a.width), (n.scaleX = 1)) : ((r = a.width + n.width), (t = 0), (n.scaleX = -1)),
      (n.x = r),
      (n.y = s),
      {
      startX: r,
      startY: (n.anchorY = s),
      endX: t,
      }
    );
},
normalWalk = function (e) {
    var r = e.peep,
      t = e.props,
      a = (t.startX, t.startY),
      n = t.endX,
      o = gsap.timeline(),
      speedMultiplier = peopleConfig.speed;
    return (
      o.timeScale(randomRange(0.5 * speedMultiplier, 1.5 * speedMultiplier)),
      o.to(
      r,
      {
          duration: 10 / speedMultiplier,
          x: n,
          ease: "none",
      },
      0
      ),
      o.to(
      r,
      {
          duration: 0.25 / speedMultiplier,
          repeat: 40,
          yoyo: !0,
          y: a - 10,
      },
      0
      ),
      o
    );
},
walks = ,
Peep = (function () {
    function a(e) {
      var r = e.image,
      t = e.rect;
      _classCallCheck(this, a),
      (this.image = r),
      this.setRect(t),
      (this.x = 0),
      (this.y = 0),
      (this.anchorY = 0),
      (this.scaleX = 1),
      (this.walk = null);
    }
    return (
      _createClass(a, [
      {
          key: "setRect",
          value: function (e) {
            (this.rect = e),
            (this.width = e),
            (this.height = e),
            (this.drawArgs = .concat(_toConsumableArray(e), ));
          },
      },
      {
          key: "render",
          value: function (e, scale) {
            var s = scale || 1;
            e.save(),
            e.translate(this.x, this.y),
            e.scale(this.scaleX * s, s),
            e.drawImage.apply(e, _toConsumableArray(this.drawArgs)),
            e.restore();
          },
      },
      ]),
      a
    );
})(),
img = document.createElement("img");
(img.onload = init), (img.src = peopleConfig.src);
let peoplecanvasEl = document.getElementById("peoplecanvas");

let ctx = peoplecanvasEl ? peoplecanvasEl.getContext("2d") : undefined,
stage = {
    width: 0,
    height: 0,
},
allPeeps = [],
availablePeeps = [],
crowd = [];

// 添加全局变量来跟踪初始化状态
let isInitializing = false;
let currentImageSrc = '';

// 修改PJAX事件监听器
document.addEventListener("pjax:success", e => {
const newPeoplecanvasEl = document.getElementById("peoplecanvas");
if (newPeoplecanvasEl && !isInitializing) {
    isInitializing = true;

    // 完全清理之前的状态
    cleanupPeopleCanvas();

    peoplecanvasEl = newPeoplecanvasEl;
    ctx = peoplecanvasEl ? peoplecanvasEl.getContext("2d") : undefined;

    // 重新选择随机图片(如果配置了多张图片)
    let newImageSrc;
    if (Array.isArray(GLOBAL_CONFIG.peoplecanvas.img)) {
      newImageSrc = GLOBAL_CONFIG.peoplecanvas.img;
    } else {
      newImageSrc = GLOBAL_CONFIG.peoplecanvas.img;
    }

    // 只有当图片真的改变时才重新加载
    if (newImageSrc !== currentImageSrc) {
      currentImageSrc = newImageSrc;
      peopleConfig.src = newImageSrc;

      // 创建新的图片对象
      const newImg = document.createElement("img");
      newImg.onload = function () {
      // 替换全局图片引用
      img.src = newImg.src;
      img.onload = null; // 清除旧的onload

      setTimeout(() => {
          if (!peoplecanvasEl || !isInitializing) return;

          initializePeopleCanvas();
          isInitializing = false;

          console.log('人潮汹涌模块重新初始化完成');
      }, 100);
      };

      newImg.onerror = function () {
      console.error('人潮汹涌图片加载失败:', newImageSrc);
      isInitializing = false;
      };

      newImg.src = newImageSrc;
    } else {
      // 图片相同,直接重新初始化
      setTimeout(() => {
      if (!peoplecanvasEl || !isInitializing) return;

      initializePeopleCanvas();
      isInitializing = false;

      console.log('人潮汹涌模块重新初始化完成(相同图片)');
      }, 100);
    }
}
});

// 新增清理函数
function cleanupPeopleCanvas() {
// 停止所有动画
crowd.forEach(function (peep) {
    if (peep.walk) {
      peep.walk.kill();
      peep.walk = null;
    }
});

// 移除事件监听器
window.removeEventListener("resize", resize);
gsap.ticker.remove(render);

// 清空所有数组
crowd.length = 0;
availablePeeps.length = 0;
allPeeps.length = 0;

// 清空画布
if (peoplecanvasEl && ctx) {
    ctx.clearRect(0, 0, peoplecanvasEl.width, peoplecanvasEl.height);
}
}

// 新增初始化函数
function initializePeopleCanvas() {
if (!peoplecanvasEl) return;

// 设置背景图片
if (peopleConfig.background_image) {
    document.documentElement.style.setProperty('--peoplecanvas-bg-image', `url(${peopleConfig.background_image})`);
}

// 重新创建人物精灵图
createPeeps();

// 重新初始化
resize();
gsap.ticker.add(render);
window.addEventListener("resize", resize);
}

// 修改原有的init函数
function init() {
if (!peoplecanvasEl || isInitializing) return;

currentImageSrc = peopleConfig.src;
initializePeopleCanvas();
}

function createPeeps() {
for (
    var e = peopleConfig.rows,
    r = peopleConfig.cols,
    t = e * r,
    a = img.naturalWidth / e,
    n = img.naturalHeight / r,
    o = 0;
    o < t;
    o++
)
    allPeeps.push(
      new Peep({
      image: img,
      rect: [(o % e) * a, ((o / e) | 0) * n, a, n],
      })
    );
}

function resize() {
if (peoplecanvasEl && peoplecanvasEl.clientWidth != 0) {
    (stage.width = peoplecanvasEl.clientWidth),
      (stage.height = peoplecanvasEl.clientHeight),
      (peoplecanvasEl.width = stage.width * devicePixelRatio),
      (peoplecanvasEl.height = stage.height * devicePixelRatio),
      crowd.forEach(function (e) {
      e.walk.kill();
      }),
      (crowd.length = 0),
      (availablePeeps.length = 0),
      availablePeeps.push.apply(availablePeeps, allPeeps),
      initCrowd();
}
}

function initCrowd() {
var targetCount = Math.floor(allPeeps.length * peopleConfig.density);
for (var i = 0; i < targetCount && availablePeeps.length; i++) {
    addPeepToCrowd().walk.progress(Math.random());
}
}

function addPeepToCrowd() {
var e = removeRandomFromArray(availablePeeps),
    r = getRandomFromArray(walks)({
      peep: e,
      props: resetPeep({
      peep: e,
      stage: stage,
      }),
    }).eventCallback("onComplete", function () {
      removePeepFromCrowd(e);
      if (crowd.length < Math.floor(allPeeps.length * peopleConfig.density)) {
      addPeepToCrowd();
      }
    });
return (
    (e.walk = r),
    crowd.push(e),
    crowd.sort(function (e, r) {
      return e.anchorY - r.anchorY;
    }),
    e
);
}

function removePeepFromCrowd(e) {
removeItemFromArray(crowd, e), availablePeeps.push(e);
}

function render() {
if (!peoplecanvasEl) return;
(peoplecanvasEl.width = peoplecanvasEl.width),
    ctx.save(),
    ctx.scale(devicePixelRatio, devicePixelRatio),
    crowd.forEach(function (e) {
      e.render(ctx, peopleConfig.scale);
    }),
    ctx.restore();
}
然后在主题配置文件中peoplecanvas项添加以下配置,这里ai生成的时候行和列搞反了,实际上rows是总图的列数,cols是总图的行数,反正能跑就懒得改了。 有些图片因为拆分后单个人物大小的问题,需要调整缩放比例和垂直偏移,如果加载出来看不到有人在跑,可能就是太高了或低了,可以先想缩放调小点,看看是高还是低,先调了垂直偏移。 具体参数搭配要看使用的总图的单个人物大小,大家多试一试找到合适的参数。
peoplecanvas:
enable: true
img:https://testingcf.jsdelivr.net/gh/kingkare/owo/img/ahugecrowd.webp
background_image: https://testingcf.jsdelivr.net/gh/kingkare/owo/img/2025-04-01.png #背景图
#下面是新增的
rows: 13 # 精灵图列数,默认15
cols: 4 # 精灵图行数,默认7
scale: 0.5 # 人物大小缩放比例 (0.1-1.0),默认1.0
density: 0.6 # 人物密度 (0.1-2.0),默认1.0
speed: 0.8 # 人物移动速度 (0.1-3.0),默认1.0
offsetY: 148 # 垂直位置偏移 (-200 到 200,负值向上,正值向下),默认0大家根据自己的来调

自定义背景图
找到这个文件修改样式 Hexo\themes\anzhiyu\source\css\_layout\home_top.styl
#peoplecanvas
    width: 100%;
    height: 100%;
    background-image: var(--peoplecanvas-bg-image, none);
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
    border-radius: 12px;找到这个文件修改样式:Hexo\themes\anzhiyu\source\js\anzhiyu\people.js 注意看里面注释的新增位置
var peopleConfig = {
    src: GLOBAL_CONFIG.peoplecanvas.img,

        //在此新增
    background_image: GLOBAL_CONFIG.peoplecanvas.background_image || '',
    //...


function init() {
if (!peoplecanvasEl) return;

// 在此新增
if (peopleConfig.background_image) {
    document.documentElement.style.setProperty('--peoplecanvas-bg-image', `url(${peopleConfig.background_image})`);
}
createPeeps(), resize(), gsap.ticker.add(render), window.addEventListener("resize", resize);
}


    setTimeout(() => {
      if (!peoplecanvasEl) return;
      
      //在此新增
      if (peopleConfig.background_image) {
      document.documentElement.style.setProperty('--peoplecanvas-bg-image', `url(${peopleConfig.background_image})`);
      }
      resize(), gsap.ticker.add(render), window.addEventListener("resize", resize);修改_config.anzhiyu.yml,设定背景图片
# 首页随便逛逛people模式 而非技能点模式,关闭后为技能点模式需要配置creativity.yml
peoplecanvas:
enable: true
img: #总图
background_image:#背景图 只要添加一行这个设置背景图地址既可
...然后执行Hexo三步命令即可!

九三郎 发表于 2026-2-15 07:53:55

曾经花了两周时间研究hexo,最后放弃了。。。。

七夏 发表于 2026-2-15 08:29:48

九三郎 发表于 2026-2-15 07:53
曾经花了两周时间研究hexo,最后放弃了。。。。

还没回老家啊
页: [1]
查看完整版本: 给Hexo静态博客程序的Azhiyu(安知鱼)主题添加一个人潮汹涌模块