# 引言
多出去看看,见的多了,就知道该折腾啥了~
没错,这是一个闲逛其他博主的博客时,逛出来的一个需求 🤣
最近重启了自己的个人博客,告别了 WordPress 拥抱了 Hexo,机缘巧合之下用上了 Shoka 主题,在作者的博客扒拉食用指南时,进入了 灵香轩(Lavender)的站点,当看到了他主题的标签云时(比原主题的标签云更得劲儿,效果丰富还显示了标签对应文章数量),只剩下了四个字,我也想要~
由于在 Lavender 的站点和网上都没有找到相关教程,于是乎开始了自己的 逆向之旅,再然后就有了这篇文章。
# 逆向工程
- F12 一波,窥探到了样式的秘密 修改
themes/shoka/source/css/_common/components/pages/tag-cloud.styl即可 - 主题搜索
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>- 官网
api找到 tagcloud函数说明,确认这是一个内置的工具函数 - 本地
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个结论
- 注册了一个 辅助函数(Helper)
tagcloud - 主要通过
tagcloudHelper生成对用的 标签云html文本
# 具体实现
- 在主题文件夹
themes/shoka/scripts/helpers/新增文件tagcloudplus.js - 拷贝
node_modules/hexo/lib/plugins/helper/tagcloud.js文件内容到tagcloudplus.js - 替换
tagcloud名称为tagcloudplus这样我们就注册了一个名为tagcloudplus的辅助函数 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);- 修改主题标签云样式文件
.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;
}
}- 替换主题文件
themes/shoka/layout/page.njk中tagcloud函数为我们自定义的 辅助函数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>建议修改下主题配置文件的 start 和 end 颜色代码,原始的颜色色系比较偏 青绿 和这个背景叠在一起视觉效果很不友好,下面是一个我个人觉得还行的色域范围,当然修改背景的颜色也没毛病
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 tagshexo三连,不出意外就能看到效果了,完结撒花~
# 结语
标签云数字角标的实现,本质上是 狸猫换太子 替换了生成处调用 系统函数 为我们 自定义的辅助函数。
其实直接修改 hexo 插件下原生的 tagcloud.js 文件也可以有一样的效果,但是考虑到跨设备工作的场景,所以尽可能将修改放在了主题内部(虽然新增文件比直接修改麻烦了点,但我认为很值),避免修改 node 的模块。

有什么想法,欢迎留下评论。