iT邦幫忙

2021 iThome 鐵人賽

DAY 19
0
Modern Web

三十天成為D3.js v7 好手系列 第 19

Day19-D3 的 RWD 圖表

  • 分享至 

  • xImage
  •  

本篇大綱:window.resize、RWD 圖表、軸線刻度數量隨畫面變化增減

前兩篇看完比例尺跟軸線後,現在D3的知識拼圖就只差最後這塊啦!今天我們要學的是怎麼畫「響應式圖表」,也就是讓圖表在畫面放大或縮小時,都能夠隨著畫面去調整圖表的大小!這個章節也是輕鬆簡單的單元,只是有比較多細節需要設定,一起來看一下吧~

在之前的篇章中,我們建立的 svg 都有固定的寬高,這樣的圖表在網頁上看很OK,但如果要在手機或平板上看,就會發現圖表太大無法看

https://i.imgur.com/wcKVhbV.gif

為了使用者在手機或平板上也能夠看圖表,因此我們要對圖表進行一些設定,讓它變成下面這樣

https://i.imgur.com/9U8gPge.gif

Window.resize 事件

要設定RWD圖表的話,我們需要使用到 window 的 onresize 事件,每當 window 監聽到畫面大小有變化時,就會觸發設定的方法。首先,我們先用d3的選取器把window選起來接著使用 selection.on( )的方法監聽 resize 事件,並設定畫面 resize 的時候要做什麼事

d3.select(window).on('resize', function(){
		console.log('畫面變化')
  });

這樣一來,畫面大小變化時,就會觸發我們設定好的功能啦(請看畫面右方的 console 訊息)~
https://i.imgur.com/EEZRyud.gif

設定外容器寬度

接著,我們要來設定外容器的高度。如果我們的 svg 是包在div中的話,就是設定這個div的寬度。

<div class="RWDChart">
	<svg>......</svg>
</div>

我們使用 css 把外容器的寬度設定成百分比,讓外容器能隨著畫面變化而縮放

.RWDChart{
    width: 80%
}

設定 svg 寬高

再來設定 svg 的寬高吧!之前我們的 svg 寬高都是直接寫死

const width = 500,
      height = 400;

const svg = d3.select('.simpleChart')
              .append('svg')
              .attr('width', width)
              .attr('height', height)

現在我們要改變作法,用外容器RWD的寬度來設定 svg 的寬高。

  • svg 寬度:用 d3.selection 抓到外容器的寬度,接著用 parseInt( )將這個寬度賦予給 rwdSvgWidth 變數,接著將這個變數設定成 svg 的寬度
  • svg 高度:用剛剛設定好的 rwdSvgWidth 來設定 rwdSvgHeight 變數,並將 rwdSvgHeight 設定成 svg 的高度
const rwdSvgWidth = parseInt(d3.select('.RWDChart').style('width')),
      rwdSvgHeight = rwdSvgWidth*0.8,
      margin = 30,
      bandWidth = 20 

const svg = d3.select('.RWDChart')
              .append('svg')
              .attr('width', rwdSvgWidth)
              .attr('height', rwdSvgHeight);

這樣一來,我們的 svg 寬高就能夠隨著畫面縮放而增減啦!

RWD圖表

現在我們可以開始繪製RWD的圖表啦!由於這邊的重點是RWD,因此繪製圖表的程式碼就先不呈現,而是著重在要怎麼讓圖表RWD上

function RWDChart(){
		const rwdSvgWidth = parseInt(d3.select('.RWDChart').style('width')),
		      rwdSvgHeight = rwdSvgWidth*0.8,
		      margin = 30,
		      bandWidth = 20 
		
		const svg = d3.select('.RWDChart')
		              .append('svg')
		              .attr('width', rwdSvgWidth)
		              .attr('height', rwdSvgHeight);

		// 以下為繪製圖表的程式碼
		// ......
		// ......
}

RWDChart() // 一開始要先執行一次,才能在畫面開始時建立圖表
d3.select(window).on('resize', RWDChart); // 接著要在每次畫面變化時都重新渲染圖表

重新渲染導致圖表重複

但這時候出現一個新的問題:每次畫面變化重新渲染圖表時,我們的圖表就會一直不停重複增加!!
https://ithelp.ithome.com.tw/upload/images/20211001/20134930o66vT249BH.jpg

這是因為畫面變化時會重新渲染圖表,但原本的圖表也都還存在,導致圖表不停的往下增加。因此我們要加上一段程式,讓每次圖表繪製前,都會刪除掉原本的圖表

function RWDChart(){
		**d3.select('.RWDChart svg').remove(); // 刪除前一次建立的圖表**

		const rwdSvgWidth = parseInt(d3.select('.RWDChart').style('width')),
		      rwdSvgHeight = rwdSvgWidth*0.8,
		      margin = 30,
		      bandWidth = 20 
		
		const svg = d3.select('.RWDChart')
		              .append('svg')
		              .attr('width', rwdSvgWidth)
		              .attr('height', rwdSvgHeight);

		// 以下為繪製圖表的程式碼
		// ......
		// ......
}

RWDChart() // 一開始要先執行一次,才能在畫面開始時建立圖表
d3.select(window).on('resize', RWDChart); // 接著要在每次畫面變化時都重新渲染圖表

這樣一來,就可以解決圖表重複出現的問題啦~

軸線刻度數量隨畫面變化增減

最後,我們要講一個有趣的功能~當圖表隨畫面縮小時, 有時候坐標軸的刻度會因為圖表縮小而重疊在一起
https://ithelp.ithome.com.tw/upload/images/20211001/20134930K9IxV3Uyo2.jpg

為了不讓刻度重疊,我們要在圖表縮小時減少刻度的數量。這時候就要用到 window.innerWidth 跟 ticks 設定的方法啦!

先來設定一個 tickNumber 的變數,並且:

  • 當畫面寬度大於 450 時,tickNumber 的值是null
  • 當畫面寬度大於 450 時,tickNumber 的值是 5

最後將 tickNumber 作為參數帶給 .ticks( ) 這個方法。由於這邊不是在講解如何建立圖表,因此就只把刻度RWD的程式碼擷取出來。想看完整程式碼的人請直接到下方的 Giithub 頁面查看~

// rwd X軸的刻度
let tickNumber = window.innerWidth > 450 ? null : 5;

xAxis = d3.axisBottom(xScale)
                .ticks(tickNumber)
                .tickFormat(function (d) {
                  //調整標籤樣式
                  return `${d} 元`;
                })

這樣就完成囉!
https://i.imgur.com/4bdACrb.gif

今天的內容就到這邊。這樣一來,D3 繪圖的重要知識都已經全部講解完畢啦!接下來我們就要運用這幾天學到的知識去繪圖囉!一天一張圖表,我來了!!


Github Page 圖表與 Github 程式碼

最後附上本章的程式碼與圖表 GithubGithub Page,需要的人請自行取用~


上一篇
Day18-D3 的 Axis( ) & ticks( ) 軸線與刻度
下一篇
Day20-D3 基礎圖表:圓餅圖
系列文
三十天成為D3.js v7 好手30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言