恭喜你進展到第 17 天,來到旅程的中央。相信你已經在 js 荒漠中,學會不少技能,並且找到一些綠洲。現在開始我們要往後半段前進,首先會花個幾天在"使用 js 繪製圖表"這座小鎮停留,快跟我一起出發吧!
話說電腦繪圖有兩種模式:向量和點陣圖。用 adobe illustrator 畫出來的是向量圖, photoshop 畫出來的是點陣圖。向量圖的特色是,即使畫完以後突然回心轉意想要放大縮小,圖也不會模糊,非常適合猶豫不絕的天秤座。
svg 也是畫向量圖的一種格式,可用在 2D 圖中。( 3D 可使用 canvas ) svg 本身可以直接用 html 標籤的方式,在網頁上放入一個畫布,再在標籤底下畫圖。你可以把以下的程式碼貼到 html 中,看看會跑出什麼?
<svg width="75" height="75">
<circle cx="35" cy="35" r="30" stroke="black" stroke-width="5" fill="red">
</svg>
現在讓我們仔細解釋一下上面。
我先設定我要一張 75x75 大小的 svg 畫布,接著在畫布上放一個圓,圓心在 x 軸 35 、 y 軸 35 的位置,半徑長30。圓的邊要是黑色的,圓的肉是紅色的,邊寬 5 。當然,如果你的畫布太小、圓太大,或圓心的位置比較偏,那可能會出現 1/4 圓等等其他的圖案。
相信應該不難理解,那就可以繼續推進下一步: D3.js 了。
D3.js 可以讓我們在 js 中動態放入 svg 檔(記得登場無數次的 innerHTML 嗎?),也可以選取 html 標籤並更改它們的樣式。要使用這麼方便的工具,要記得在你的 html 裡載入 <script src="https://d3js.org/d3.v5.min.js"></script>
,並放在 </body>
上方,比較容易讀取到。
使用 codepen 練習的朋友們,請點右上方 settings > JS 並貼上上面的網址,按下 Save&Close 唷!
接下來我會介紹幾個用法,但因為 D3.js 能玩的實在太多,建議大家可以抽空閱讀 D3.js 的 API ,裡面有各式各樣的語法和使用方法。
要一次選取所有 html 標籤,去更改 css 樣式嗎?你可以用 d3.js 做到!寫法如下:
d3.selectAll("要選的標籤").style("項目","改成什麼");
範例:
d3.selectAll("p").style("color","blue"); //把所有p段落選起來,調整css style中的顏色,變成藍色
如果只是要改第一個標籤,寫法為:
d3.select("要選的標籤").style("項目","改成什麼");
範例:
d3.select("p").style("color","blue") //只選第一個p段落,調整css style中的顏色,把它變成藍色
其中選取的標籤也可以是自訂的 class 名稱,但要記得加上點。例如:.select(".header")
。
selectAll 和 select 當然也能夠合併使用。如果覺得太過於抽象,可以把這個想成用繪圖軟體時會去選圖層下面的物件,而圖層下有時還會再有圖層,選取時就會需要先單獨點某個圖層,再去全選下面的物件。以實際範例來說,我可以先選擇 body 標籤裡的 svg ,然後全選裡頭所有 rect 。
d3.select("body").select("svg").selectAll("rect")
接著終於要進入重頭戲了!前面介紹完如何幫標籤改屬性之後,現在要說怎麼為標籤加入 svg 畫布。
語法很簡單:
d3.select(".要加在哪個標籤裡")
.append('要新增的東西')
.attr('要加的屬性','詳細資料');
在 .append 裡的東西可以是 svg 畫布、圓,或是其他圖形。請看下面的範例:
// 繪製SVG
d3.select('.chart-d3')
.append('svg')
.attr('width',"75")
.attr('height',"75");
// 畫圓
d3.select('.chart-d3 svg')
.append('circle')
.attr('cx','35')
.attr('cy','35')
.attr('r','30')
.attr('stroke','black')
.attr('fill','red')
.attr('stroke-width','5');
寫法為:
d3.select('')
.transition().duration(動畫時間為幾毫秒).delay(延遲幾毫秒產生).attr('要加的屬性','詳細資料')
在上面的圖片已經創建後,在下方寫下下面的程式碼,則動畫會延遲兩秒後,讓肉在五秒內慢慢變成粉紅色。
d3.select('.chart-d3 svg circle').transition().attr('fill','black');
好像越來越有模有樣哩!在今天的最後,我們要來試畫看看長條圖。
假設有四個候選人,分別得到 30 、 50 、 100 、 120 票,我們可能會先把這些資料儲存在陣列中,等到需要的時候才把它提領出來,放到畫面上。像這種需要導入資料的狀況,就可以使用 .data 。以條列式 ul li 為例,首先在 html 放要安插資料的標籤。
<ul class = "list"></ul>
.data 使用起來跟上述大同小異,語法為 .data(要導入的陣列).enter() 。請看下列程式碼:
const vote = ['30','50','100','120']
d3.select('.list') //選html裡的list
.selectAll('li') //選所有底下的li標籤
.data(vote) //導入vote資料
.enter()
.append('li') //動態加入li標籤
.text(function(d){ //d是上面30、50等資料
return d; //把30、50等資料依序帶進相對位置
})
這段程式碼的重點是,即使 html 的 ul 中沒有 li ,當寫 .selectAll('li') 時,系統還是會先幫你新增相對的欄位出來,放入資料,之後在下面用 .append 動態加入 li 標籤即可。欸...怎麼有種先上車後補票的港覺
進入重點,要畫長條圖可以這樣寫:
<div class = "chart"></div>
.chart div {
display: inline-block;
background: blue;
width: 50px;
margin-right: 5px;
vertical-align: bottom;
color: #fff;
text-align: center;
}
var vote = [
{
"name":"tom",
"num":"50"
},
{
"name":"mary",
"num":"80"
},
];
d3.select('.chart') //選html裡的chart
.selectAll('div') //選所有底下的div標籤
.data(vote) //導入vote資料
.enter()
.append('div') //動態加入div標籤
.html(function(d){ //d是上面name、num裡的資料
return d.name+'<br>'+d.num; //長條圖上會各自出現名字及票數
})
.style("height",function(d){
return d.num+'px'; //長條圖高度用d的num的數據加上px畫出來
})
不知道今天的內容對你而言吸收的是否流暢?明天我們會繼續進行 C3.js ,如果還是似懂非懂也歡迎你觀看下面的學習與參考資料,找到適合自己的教學文章或影片喔!
JS 學徒特訓班教學影片
六角學院 D3.js、C3.js 資料視覺化教學:https://www.youtube.com/watch?v=0PQ8VOsyjzw
D3.js API: https://github.com/d3/d3/blob/master/API.md
SVG 基本圖形 - 圓形 circle: https://abgne.tw/svg/svg-getting-started/svg-shape-circle.html
SVG D3.js - 淺談 D3.js 的資料處理: https://www.oxxostudio.tw/articles/201411/svg-d3-01-data.html
特別感謝回答我疑惑的:
Tim Hsu
SeanLiu
William Kang