Cover

事件太多,拉起時間軸傷透腦筋嗎?時間軸是常見的視覺化手法,但現成工具往往缺乏變化,手繪又曠日費時。不要緊!Labella.js 幫我們自動排版標籤,從此麻煩事就交給他,完成事情更有效率!

繪製時間軸難免得為事件標上細節,但我們常會碰到幾個問題:

  • 內容往往視資料而定,可長可短可大可小
  • 資料點有時很稀疏,有時又很密集
  • 手動對應時間點的位置很麻煩,但大多數時間軸工具例如 timeline.js 又缺乏客製的彈性

在自行排版時間軸標籤時,牽涉到的大多是操作式的無聊動作,這樣的任務便是程式自動化的最好對象。 Labella.js 是一個時間軸的標籤套件,他可以為我們標記事件在時間軸上的位置,並自動排版標籤至適當位置,除了省時省力外,排版出來的結果還不錯看!比方說,下圖為截自 Labella.js 的示範頁面

labella Screenshot

在這個範例中我們彈性的設定各種屬性,標籤便會自動依照我們的選擇擺放位置。它的輸出甚至是 SVG ,我們很輕易的就可以利用 SVG Crowbar 等工具 ( 請參考先前的:SVG Crowbar — 視覺化一鍵打包帶回家 ) 介紹把他整組下載來修改。完美!

但是,很可惜的這個範例中並沒有讓我們填事件細節的地方,也因此產生的時間軸只能做為參考,若要直接拿來用,還是得重新調整半天。沒關係, Labella.js 正如其名,是個 JavaScript 函式庫,我們可以運用他所提供的 API 來打造自己的時間軸。

( 編按:如果你不會寫程式,我們直接在文章最後提供了一個做好的時間軸標籤產生器,可以直接拉到底玩玩看喔!)

首先,將 Labella.js 引入網頁中,並準備一個 SVG 標籤 ( id 亦設為 “svg" ):

  <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/labella/1.1.2/labella.min.js"></script>
  <svg id="svg" width="800px" height="500px" viewBox="0 0 800 500"></svg>

接著以下列程式碼產生標籤排版資料:

  // 一共三組資料,對應位置在 10, 20, 30,標籤寬度 100, 80, 60
  var nodes = [
    new labella.Node(10,100),  
    new labella.Node(20,80),
    new.labella.Node(30,60),
  ];
  nodes = new labella.Force().nodes(nodes).compute().nodes();

接著我們利用 Labella 的 Renderer 函式搭配 D3.js 繪製標籤:

// 計算位置
var renderer = new labella.Renderer();
nodes = renderer.layout(nodes);
// 建立分組標籤
d3.select("#svg").selectAll("path").data(nodes).enter()
  .append("g").each(function(d,i) {
    var node = d3.select(this);
    // 繪製連線
    node.append("path").attr({
      d: renderer.generatePath,
      fill: "none", stroke: "crimson"
    });
    // 繪製標籤
    node.append("rect").rect({
      x: function(it) { return it.x - it.dx / 2 ; },
      y: function(it) { return it.y; },
      width: function(it) { return it.dx; },
      height: function(it) { return it.dy; },
      fill: "crimson"
    });
  });

產生的結果如下:

simple result from labella

 

利用 d3Kit-timeline

雖然我們成功做出了一個時間軸雛型,但還有很多沒搞定的,包含軸線、軸點、標籤文字等等問題,再這樣搞下去輕鬆都不輕鬆了。沒關係, Labella.js 作者深知民間疾苦,他將 Labella.js 搭配包裝成另一個好用的工具 — d3Kit-timeline !

d3Kit-timeline 透過包裝繪圖的細節讓我們省掉大部份上述的工,而透過 D3.js 則為我們保留了許多彈性。這裡我們直接引用 d3Kit-timeline usage 裡的程式碼簡單為大家做解說。

首先,引入必要的函式庫;除了 d3.js 與 labella.js 以外,我們需要另外取得 d3kitd3kit-timeline

  <script src="d3.min.js"></script>
  <script src="d3kit.min.js"></script>
  <script src="labella.min.js"></script>
  <script src="d3kit-timeline.min.js"></script>

接著設定我們要用的資料,這裡使用的資料是星際大戰電影各集的年代與名稱:

  var data = [
   {time: new Date(1977, 4,25), name: 'A New Hope'},
   {time: new Date(1980, 4,17), name: 'The Empire Strikes Back'},
   {time: new Date(1984, 4,25), name: 'Return of the Jedi'},
   {time: new Date(1999, 4,19), name: 'The Phantom Menace'},
   {time: new Date(2002, 4,16), name: 'Attack of the Clones'},
   {time: new Date(2005, 4,19), name: 'Revenge of the Sith'},
   {time: new Date(2015,11,18), name: 'The Force Awakens'}
  ];

然後使用 d3Kit-timeline 繪制時間軸:

  chart = new d3KitTimeline('#svg', {
    direction: 'down', // 朝下的時間軸標籤
    initialWidth: 800, labella: {maxPos: 800}, // 控制圖表寬度
    textFn: function(d) { return d.time.getFullYear() + ' - ' + d.name }
  });
  chart.data(data).resizeToFit();

產生結果如下:

labella with d3Kit-timeline

Labella UI

即便 Labella + d3Kit-timeline 相當的方便,對於不會寫程式的讀者來說仍然是無法使用,沒關係!小編將上列的範例程式碼整理成一個可以動態填入資料的線上版本,如果你想要嘗試看看,直接玩玩看吧:

這裡提供另一段範例資料供讀者參考,可以試著在上方的文字框中貼上:

  time,name
  1983/4/18,US Embassy bombing
  1983/12/12,Kuwait bombings
  1989/7/7,Jerusalem Bus Suicide Attack
  1993/2/26,WTC Bombing
  1993/3/12,Serial Blasts in Mumbai
  1994/12/24,Flight Hijacking in Algiers
  1996/6/5,Khobar Towers Bombing
  1997/11/17,Luxor Massacre
  1998/2/14,Coimbatore Bomb Attacks
  1998/8/7,US Embassy bombing
  2000/10/12,Yemeni Attack
  2000/12/22,Delhi Attack
  2000/12/24,Indonesia Bombings
  2001/9/11,911

這組範例資料的產生結果類似下圖:

sample2

我們也在其中提供了基本設定顏色的功能,但最後你都可以利用 SVG Crowbar 下載後再利用 Adobe Illustrator 或是 Vectr.com 做調整。若你未來想再使用這個小工具,你可以儲存這篇文章供參考,或直接將這個小工具的網址 ( http://infographicstw.github.io/labella-ui/ ) 加入書籤。

結語

Labella.js 為來自 Twitter 的 Krist Wongsuphasawat 開發,在其程式碼中也可以看到類似 D3.js 結構的設計,而正如 D3.js 其中強大的一面 — 「不光是給你圖表,而是幫你計算各種版面」一般, Labella 也可以想成是一個 d3.layout 的實用擴充。或者說,原本作者就是以這樣的思維來設計的吧!