标签页原来会把每个标签下面的文章全部展开。标签涨到两百多个之后,页面先是一长串标签,再接上一长串文章列表,翻下去很累。点某个标签本来就有独立页(/tags/Python/ 这种),总览页没必要再把文章重复列一遍。
这次把 themes/hiker/layout/tags.ejs 收成一个球面标签云:全部标签都在球面上,字号按文章数加权,点击跳到该标签的文章页。用的是 TagCloud.js(CodeBlanks 的 3D Tag Cloud 也是这套库),大约 7KB,不依赖 jQuery。
为什么不用 Canvas 版 TagCanvas
中间试过 TagCanvas。效果是 3D 的,但转得快,标签叠在一起发晕;更麻烦的是独立版 TagCanvas.Start() **成功时返回 undefined,失败才返回 false**。
当时用了 if (!started) 判断失败,成功也被当成失败,canvas 容器被 display: none 藏掉,页面上什么都没有。改成只在 started === false 时降级之后才能看见球体。
即便修完,旋转仍然偏晕,所以换成 TagCloud.js,并把速度设成 slow。
页面上只留标签,不留文章列表
tags.ejs 里用 JSON 把名称、链接和文章数交给前端。useHTML: true 时,数组里的每一项可以是一段 HTML,这样标签本身就是 <a>,点下去就是标签页。
1 | <script type="application/json" id="tag-cloud-data"><%- JSON.stringify(tagData) %></script> |
tagData 在模板里从 site.tags 收集:name、url_for(tag.path)、count(tag.length)。按文章数从多到少排,热门标签字号更大,范围大约 11px 到 19px,不要拉得太开,否则球心会糊成一团。
初始化:
1 | TagCloud(root, texts, { |
keep: true 表示鼠标移开后继续慢慢转,而不是突然停住。direction: 135 是初始朝右下。半径按视口收:手机约 110,平板约 150,桌面约 260。resize 时先 destroy() 再按新半径重建,横竖屏切换不用手动刷新。
容器加上 touch-action: none,手指在球上滑动时不会把整页一起拖走。
一个容易踩的返回值
如果以后又换回 TagCanvas,失败判断要写成:
1 | var started = TagCanvas.Start(canvasId, listId, options); |
!started 会把 undefined 也算失败。
和原来列表页的差别
- 总览页不再按标签重复列出文章,高度固定在一块区域里(桌面大约 460px)。
- 全部标签都在球面上,不会因为
max-height+ 滚动把后面的标签裁掉。 - 文章数多的标签更大;点击进入
/tags/<name>/,不在本页锚点展开。 - 库加载失败时,同一份数据落到一个普通的换行标签列表,避免空白页。
本地看效果:hexo s 后打开 /tags/,鼠标或手指带动球体,点一个标签应进入对应文章列表。