效果: http://lucyhao.com/tags/
hexo自帶的tag cloud的標簽展現不太美觀,想能夠展現出“雲”效果的標簽。在網上找到了d3-cloud這個項目,github地址:https://github.com/jasondavies/d3-cloud demo地址:https://www.jasondavies.com/wordcloud/
hexo生成的是靜態博客,所以最后在網上看到的都是靜態的內容,也就是說,我們的看到的標簽雲也是靜態的已經生成好的內容,並不會隨着刷新頁面而重新計算生成另外樣式的標簽雲。
當然d3-cloud這個項目,提供了瀏覽器端和node端運行的版本,見它的例子,我們可以在客戶端運行,也可以在服務端作為 node運行。
如何在hexo搭建的博客系統中使用呢?
(1)hexo中提供的tag cloud.js的方法,在你的blog項目中,node_modules -> hexo ->plugins -> helper -> tagcloud.js
(2)本博客使用的是icarus主題,在主題下的layout->tags.ejs 文件中加載目錄tags下面的內容
//這里加載了標簽雲,tagcloud也就是(1)中tagcloud.js提供的功能
<div class="layout-wrap-inner tag-cloud">
<% if(site.tags.length) { %>
<%- tagcloud() %>
<% } %>
</div>
由上所知 ,要修改hexo中的tagcloud, 只要修改tag cloud.js函數就可以啦~
現在我們來看看d3-cloud這個項目,如何把它與tagcloud結合起來。 首先,d3-cloud提供了在瀏覽器和在服務端運行的方式,見他的例子node.js和browserify.js。兩個不同版本的原因,是因為在計算字符串的寬度的時候,利用了canvas的mesureText接口。在d3-cloud的 index.js源碼中可以看到,有一行代碼是
function cloudCanvas() {
return document.createElement("canvas");//生成虛擬的canvas
}
因為在計算標簽雲的時候,要保證標簽之間不重疊,需要知道標簽的寬度,高度;而js語言是不具備這個能力可以計算出來的,要不就是借助瀏覽器,生成一個dom,比如span標簽,把字符串的內容放到span中,設置span的屬性為字符串需要顯示的屬性,然后獲取span的寬度。在d3-cloud中,則是直接利用canvas的接口來實現的。所以在客戶端的版本中,瀏覽器提供的canvas功能;而在node版本中則需要 node-canvas模塊。
由於我們是在hexo的“后台”來運行標簽雲的算法,得到靜態的構造好的標簽輸出到頁面上,所以我們應該選擇用node版本。當然也可以用browserify.js版本,畢竟他就是一個運行在瀏覽器中的js, 放到博客的js中也是可以的,后續會介紹。
利用node-canvas遇到的問題
node-canvas模塊的mesureText對於中文的支持有bug,在chrome中,同樣的中文字符串"你好"的寬度是33.*;而用node-canvas的到的“你好”的寬度只有8.*
怎么辦?我投機取巧的用兩個英文字符“ab”代替一個中文字符,然后計算字符串的長度,這樣的到的長度只是近似長度。
正式開始修改hexo的到d3-cloud的標簽雲
(1)安裝需要的模塊:
$ npm install canvas --save $ npm install d3-cloud --save $ npm install d3 --save(可選)
(2)找到文件: 你的 blog項目 -> node_modules -> hexo ->plugins -> helper -> index.js
var tagcloud = require('./tagcloud');
helper.register('tagcloud', tagcloud);
helper.register('tag_cloud', tagcloud);
//修改為下面的代碼:目的是不直接修改tagcloud.js,保留代碼
var tagcloud = require('./tagcloud');
var tagcloudd3 = require('./tagcloudd3');
helper.register('tagcloud', tagcloudd3);
helper.register('tag_cloud', tagcloudd3);
(3)新建文件tagcloudd3.js :位置在blog項目 -> node_modules -> hexo ->plugins -> helper -> tagcloudd3.js
(4)tagcloudd3.js的 內容如下:
-
代碼中引用了d3 來給標簽fill顏色,可以去掉,也可以像tagcloud一樣根據是否需要顏色來設置
'use strict';
var Canvas = require("canvas");
var cloud = require("d3-cloud");
var d3 = require("d3");
var layout = cloud()//利用d3-cloud計算每個標簽的位置
.size([600, 400])
.canvas(function() { return new Canvas(1, 1); })
.padding(7)
.rotate(function() { return ~~(Math.random() * 2) * 90; })
.font("Impact")
.fontSize(function(d) { return d.size; });
var fill = d3.scale.category20();//利用d3的接口給每個標簽顏色
function tagcloudHelper(tags){
/****與tagcloud.js一樣,獲得tags 開始***/
if ((!tags || !tags.hasOwnProperty('length'))){
tags = this.site.tags;
}
if (!tags || !tags.length) return '';
var result = [];
tags = tags.sort('name', 1);
// Ignore tags with zero posts
tags = tags.filter(function(tag){
return tag.length;
});
/****與tagcloud.js一樣,獲得tags 結束***/
//計算標簽出現次數最大值,比如,博客中一共有兩個標簽,一個是hello,一個是world,hello出現2次,world 出現1次,那么maxsize就是2
var maxsize = 1;
tags.sort('length').forEach(function(tag){
var length = tag.length;
if(length > maxsize)
maxsize = length;
});
//構建傳入layout的words
var arr = [],words;
tags.forEach(function(tag){
arr.push({"name": tag.name,"num" : tag.length});
});
words = arr.map(function(d) {
var text = d.name.replace(/[^\x00-\xff]/g,"ab");//對中文的投機處理,用ab代替中文字符
return {name:d.name, text: text, size : Math.log(d.num)/(Math.log(maxsize)-Math.log(1)) * 15 + 30};//size的計算取對數,是為了讓標簽之間的大小相對平均一些。因為博客側重前端內容,所以某一些標簽會比較多,標簽最大最小次數的差距會比較大。
});
layout.words(words);
layout.start();
result.push('<svg width="600" height="400"><g transform="translate(300,200)">');
words.forEach(function(word,i){
result.push(
'<text text-anchor="middle" fill="'+fill(i)+'" transform="translate('+word.x+','+word.y+')rotate('+
word.rotate+')" style="font-size:'+word.size+'px;font-family:Impact">'+
word.name+
'</text>'
);
});
result.push('</g></svg>');
return result.join('');
}
module.exports = tagcloudHelper;
(5)運行hexo
$ hexo s
如圖所示得到自己的標簽雲:

(6)上傳自己的博客, 沒問題以后,就生成靜態博客,並上傳
$ hexo g $ hexo d
簡單說下在客戶端引用d3-cloud
(1) browserify編譯d3-cloud提供的browserify.js例子,得到tagtest.js 文件,里面把d3,d3-cloud,d3-dispatch打包到了一起
$ browserify broswerify.js > tagtest.js
(2)把index.js放到目錄: 你的主題->source -> js -> tagtest.js
(3)把index引用入到你的主題中 <%- js('js/tagtest') %>
這樣會在博客中增加很多js,個人覺得有點違背了靜態博客小而輕的感覺。。。
