D3 + p5.js 筆記 - 介紹 p5

  1. 1. 為什麼選擇 p5?
    1. 1.1. canvas 1px 直線問題
  2. 2. p5 的基本構造
  3. 3. p5 的問題
    1. 3.1. 全域變數
    2. 3.2. 編輯器
  4. 4. 解決方式
  5. 5. 其他參考

先來大致介紹一下 p5.js

這是 Processing 團隊移植的 web 版本,除了 p5 之外還有一個是由 jQuery 作者自己實現,叫 Processingjs 的函式庫,可別搞混了。

為什麼選擇 p5?

以前大學時有教授教過 Processing 算是因素之一,入門上不算太難。

在圖片繪製上,p5 也簡化了一些步驟,比如畫線的時候,canvas 的畫法是

1
2
canvas.moveTo(x, y);
canvas.lineTo(x2, y2);

p5 只要一個 line 就可以解決。稍微試過 Pixi.jsEaselJS,這兩個基本上跟 canvas 原本的畫法差不了多少,畢竟定位是遊戲引擎,p5 是繪圖函式庫。

canvas 1px 直線問題

canvas 有一個渲染上的問題,很多剛碰的人大概都會遇到,在畫 1px(或其他奇數)寬的線時,在螢幕上顯示卻有 2px 寬。

簡單圖解類似這樣:

canvas

當在座標上畫線時,canvas 並不會在該座標的 右邊或左邊畫線,而是會以座標為準,向兩邊各佔用一半的 px,在這邊就各佔用了 0.5px。而 canvas 的渲染機制就會把兩邊剩下的 0.5 補上顏色,類似防鋸齒那樣,就造成畫 1px 卻有 2px 寬的情形發生。

解決方式就是向前或向後退 0.5 px,而 p5 會幫你搞定這件事,但由於 [D3] 在幫你計算刻度座標時,數值不會那麼漂亮,所以還是會有漏網之魚發生,至少 XY 軸是正常顯示的。

p5 的基本構造

基本上要先建立以下三個函數:

1
2
3
4
5
6
7
8
9
10
11
function preload() {
// 載入 csv, tsv 等檔案
}

function setup() {
// 初始化資料、建立 canvas 等
}

function draw() {
// 繪製圖形
}

其中 preload 可有可無,端看須不需要額外載入資料檔案,而絕大部分繪製的相關函數必須要在 p5 指定的函數裡呼叫,否則不會有作用。當然也有其他的函數是獨立的(比如事件偵測),但絕大部分的還是得照著規則進行。

p5 的問題

全域變數

p5 的函數都是全域變數,而一個網頁照一般的寫法也只會寫一個 preloadsetupdraw 等函數,當需要數個 canvas 時(畫圖表時有多個畫布很正常吧),創建 canvas 的程式碼全都要擠在同一個 setup 裡(無法用匿名函數分開包裝),在繪製時又會全部擠在一起一次,因而造成很大的麻煩。

編輯器

正因為 p5 函式都是全域變數不須宣告,所以當你在寫程式碼時,你的編輯器會出現一堆下劃線,提醒你變數未宣告之類的錯誤。

解決方式

new 宣告一個。

1
2
3
4
5
6
7
8
9
new p5(ctx => {
ctx.setup = () => {

}

ctx.draw = () => {

}
});

在這裡面,不管要使用什麼函數,都要用 ctx.[函數名稱] 才可使用,但也避免了瀏覽器與編輯器的問題。

其他參考