点击查看更新记录

更新记录

2022-06-18 需求实现

2022-06-17 需求整理

# 引言

多出去看看,见的多了,就知道该折腾啥了~

Demo 地址, Click here!

没错,这是一个闲逛其他博主的博客时,逛出来的一个需求 🤣

最近重启了自己的个人博客,告别了 WordPress 拥抱了 Hexo ,机缘巧合之下用上了 Shoka 主题,在作者的博客扒拉食用指南时,进入了 灵香轩 (Lavender) 的站点,当看到了他主题的标签云时 (比原主题的标签云更得劲儿,效果丰富还显示了标签对应文章数量),只剩下了四个字,我也想要

由于在 Lavender 的站点和网上都没有找到相关教程,于是乎开始了自己的 逆向之旅,再然后就有了这篇文章。

# 逆向工程

  1. F12 一波,窥探到了样式的秘密 修改 themes/shoka/source/css/_common/components/pages/tag-cloud.styl 即可
  2. 主题搜索 tag 关键字,在主题文件 themes/shoka/layout/page.njk 中找到了 tag cloud 的生成调用 tagcloud , 传入的即主题文件 tagcloud 相关配置
<div class="collapse wrap">
    <h2 class="item title">
        <a href="{{ url_for(site.path) }}">{{ __('menu.home') }}</a>
        <small>/</small>
        {{ _p('counter.tag_cloud', site.tags.length) }}</h2>
    <div class="tag cloud">
        {{ tagcloud({
            min_font   : theme.tagcloud.min,
            max_font   : theme.tagcloud.max,
            amount     : theme.tagcloud.amount,
            color      : true,
            start_color: theme.tagcloud.start,
            end_color  : theme.tagcloud.end})
        }}
    </div>
</div>
  1. 官网 api 找到 tagcloud 函数说明,确认这是一个内置的工具函数
  2. 本地 node_modules 下定位到了源代码原件 node_modules/hexo/lib/plugins/helper/tagcloud.js

逆向结束,拿到了源码就可以方便的进行修改了,毕竟 源码面前,了无秘密

# 实现

# 源码分析

源码的关键部分如下

function tagcloudHelper(tags, options) {
    // ...
    tags.forEach(tag => {
        const ratio = length ? sizes.indexOf(tag.length) / length : 0;
        const size = min + ((max - min) * ratio);
        let style = `font-size: ${parseFloat(size.toFixed(2))}${unit};`;
        const attr = className ? ` class="${className}-${Math.round(ratio * level)}"` : '';
        if (color) {
            const midColor = startColor.mix(endColor, ratio);
            style += ` color: ${midColor.toString()}`;
        }
        // 关键代码
        result.push(
            `<a href="${url_for.call(this, tag.path)}" style="${style}"${attr}>${transform ? transform(tag.name) : tag.name}</a>`
        );
    });
    // ...
}
function tagcloudHelperFactory(tags, options) {
    // ...
}
hexo.extend.helper.register('tagcloud', tagcloudHelperFactory);

可以得到如下 2 个结论

  1. 注册了一个 辅助函数(Helper) tagcloud
  2. 主要通过 tagcloudHelper 生成对用的 标签云 html 文本

# 具体实现

  1. 在主题文件夹 themes/shoka/scripts/helpers/ 新增文件 tagcloudplus.js
  2. 拷贝 node_modules/hexo/lib/plugins/helper/tagcloud.js 文件内容到 tagcloudplus.js
  3. 替换 tagcloud 名称为 tagcloudplus 这样我们就注册了一个名为 tagcloudplus 的辅助函数
  4. tag 数据结构内部的 length 即我们需要的标签文章数量,需要生成 文章数量角标 修改关键代码如下
// 70 行左右
result.push(
      `<a href="${url_for.call(this, tag.path)}" style="${style}"${attr}>${transform ? transform(tag.name) : tag.name}<sup>${tag.length}</sup></a>`
    );
// <sup>${tag.length}</sup> 为新增上标,如果喜欢下标则改为 <sub>${tag.length}</sub>

完整代码如下

'use strict';
const { Color, url_for } = require('hexo-util');
const { default: moize } = require('moize');
function tagcloudplusHelper(tags, options) {
    if (!options && (!tags || !Object.prototype.hasOwnProperty.call(tags, 'length'))) {
        options = tags;
        tags = this.site.tags;
    }
    if (!tags || !tags.length) return '';
    options = options || {};
    const min = options.min_font || 10;
    const max = options.max_font || 20;
    const orderby = options.orderby || 'name';
    const order = options.order || 1;
    const unit = options.unit || 'px';
    const color = options.color;
    const className = options.class;
    const level = options.level || 10;
    const { transform } = options;
    const separator = options.separator || ' ';
    const result = [];
    let startColor, endColor;
    if (color) {
        if (!options.start_color) throw new TypeError('start_color is required!');
        if (!options.end_color) throw new TypeError('end_color is required!');
        startColor = new Color(options.start_color);
        endColor = new Color(options.end_color);
    }
    // Sort the tags
    if (orderby === 'random' || orderby === 'rand') {
        tags = tags.random();
    } else {
        tags = tags.sort(orderby, order);
    }
    // Limit the number of tags
    if (options.amount) {
        tags = tags.limit(options.amount);
    }
    const sizes = [];
    tags.sort('length').forEach(tag => {
        const { length } = tag;
        if (sizes.includes(length)) return;
        sizes.push(length);
    });
    const length = sizes.length - 1;
    tags.forEach(tag => {
        const ratio = length ? sizes.indexOf(tag.length) / length : 0;
        const size = min + ((max - min) * ratio);
        let style = `font-size: ${parseFloat(size.toFixed(2))}${unit};`;
        const attr = className ? ` class="${className}-${Math.round(ratio * level)}"` : '';
        if (color) {
            const midColor = startColor.mix(endColor, ratio);
            style += ` color: ${midColor.toString()}`;
        }
        result.push(
            `<a href="${url_for.call(this, tag.path)}" style="${style}"${attr}>${transform ? transform(tag.name) : tag.name}<sup>${tag.length}</sup></a>`
        );
    });
    return result.join(separator);
}
function tagcloudplusHelperFactory(tags, options) {
    const transformArgs = () => {
        if (!options && (!tags || !Object.prototype.hasOwnProperty.call(tags, 'length'))) {
            options = tags;
            tags = this.site.tags;
        }
        return [tags.toArray(), options];
    };
    return moize(tagcloudplusHelper.bind(this), {
        maxSize: 5,
        isDeepEqual: true,
        transformArgs
    }).call(this, tags, options);
}
hexo.extend.helper.register('tagcloudplus', tagcloudplusHelperFactory);
  1. 修改主题标签云样式文件
.tag.cloud {
    text-align: center;
    a {
        display: inline-block;
        margin: 0.625rem;
        background: var(--tags-background);
        /* 增加边框及阴影 */
        padding: 0.3em 0.5em;
        border-radius: 10px;
        box-shadow: 0 7px 23px rgba(0, 0, 0, 0.2);
        &:hover {
            color: var(--grey-3) !important;
            /* hover 放大效果 */
            transform: scale(1.25, 1.25);
            color: var(--primary-color) !important;
        }
    }
}

其中颜色变量 --tags-background 定义在主题文件 themes/shoka/source/css/_colors.styl

:root {
    // ...
    /*
     * 新增
     */
    --tags-background: #edf0f7;
}
[data-theme="dark"] {
    &:root {
        // ...
        /*
        * 新增
        */
        --tags-background: #30343f;
    }
}
  1. 替换主题文件 themes/shoka/layout/page.njktagcloud 函数为我们自定义的 辅助函数 tagcloudplus
<div class="collapse wrap">
    <h2 class="item title">
        <a href="{{ url_for(site.path) }}">{{ __('menu.home') }}</a>
        <small>/</small>
        {{ _p('counter.tag_cloud', site.tags.length) }}</h2>
    <div class="tag cloud">
        {{ tagcloudplus({
            min_font   : theme.tagcloud.min,
            max_font   : theme.tagcloud.max,
            amount     : theme.tagcloud.amount,
            color      : true,
            start_color: theme.tagcloud.start,
            end_color  : theme.tagcloud.end})
        }}
    </div>
</div>

建议修改下主题配置文件的 startend 颜色代码,原始的颜色色系比较偏 青绿 和这个背景叠在一起视觉效果很不友好,下面是一个我个人觉得还行的色域范围,当然修改背景的颜色也没毛病

tagcloud:
    # All values below are same as default, change them by yourself.
    min: 16 # Minimun font size in px
    max: 22 # Maxium font size in px
    start: '#ff5ad1' # Start color (hex, rgba, hsla or color keywords)
    end: '#9968ec' # End color (hex, rgba, hsla or color keywords)
    amount: 200 # Amount of tags, change it if you have more than 200 tags
  1. hexo 三连,不出意外就能看到效果了,完结撒花~

# 结语

标签云数字角标的实现,本质上是 狸猫换太子 替换了生成处调用 系统函数 为我们 自定义的辅助函数

其实直接修改 hexo 插件下原生的 tagcloud.js 文件也可以有一样的效果,但是考虑到跨设备工作的场景,所以尽可能将修改放在了主题内部 (虽然新增文件比直接修改麻烦了点,但我认为很值),避免修改 node 的模块。

更新于 阅读次数

请我喝[茶]~( ̄▽ ̄)~*

Seiun 微信支付

微信支付

Seiun 支付宝

支付宝