天天看點

在hexo靜态部落格中利用d3-cloud來展現标簽雲

效果: 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
      

如圖所示得到自己的标簽雲:

在hexo靜态部落格中利用d3-cloud來展現标簽雲

(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') %>

(4)修改tagtest.js中的代碼,把words的部分修改成接受傳參的形式,在tags.ejs中用site.tags把tags的參數傳進去

這樣會在部落格中增加很多js,個人覺得有點違背了靜态部落格小而輕的感覺。。。

繼續閱讀