点击查看更新记录
更新记录
2022-06-18 需求实现
2022-06-17 需求整理
# 引言
多出去看看,见的多了,就知道该折腾啥了~
没错,这是一个闲逛其他博主的博客时,逛出来的一个需求 🤣
最近重启了自己的个人博客,告别了 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 tags |
hexo
三连,不出意外就能看到效果了,完结撒花~
# 结语
标签云数字角标的实现,本质上是 狸猫换太子 替换了生成处调用 系统函数 为我们 自定义的辅助函数。
其实直接修改 hexo
插件下原生的 tagcloud.js
文件也可以有一样的效果,但是考虑到跨设备工作的场景,所以尽可能将修改放在了主题内部 (虽然新增文件比直接修改麻烦了点,但我认为很值),避免修改 node
的模块。