<div>元素是個有故事的元素,這個元素很早就出現在html超文本標記語言中,它設計之初就是為了解決網頁頁面布局的需求。但是遺憾的是它出生后一直懷才不遇。
在我還上初中的時候,智能手機還沒有出現,更沒有平板電腦等移動設備。上網是通過擺在桌子上的計算機來完成的。
那時,大街小巷上有好多網吧。
那時,馬云剛剛辭去工作準備創業。
那時,發送郵件的操作都會出現在計算機課程中。
那時,對頁面還沒有現在的跨平臺要求。
那時,flashplayer大行其道。
那時,dreamwaver、flash、fireworks被稱為網頁三劍客!
那時,制作網頁可以不用懂的html的寫法!
第一次接觸網頁制作是在大學的專業課上,使用三劍客,通過點擊軟件菜單中的按鈕就能制作網頁,精力都放在了如何使用flash制作酷炫的交互動畫上了。
那時,對html還沒有深刻的認識,但是卻對<table></table>這個標簽有著極深的印象。
因為當時的dreamwaver通過非代碼方式生成的頁面都是使用<table>表格元素進行布局的!
也就是說,在移動智能設備誕生之前,在用戶對頁面還沒有可以適應不同屏幕比例的要求前,<table>這個本來用來做表格的元素同時兼職了<div>的頁面布局工作,而且把兼職干成了主業,讓<div>這個專業的塊元素閑置了好久。
直到智能手機,平板電腦產生后,由于對頁面的跨平臺顯示的要求的出現(這類適應多平臺的頁面布局叫做響應式布局),<table>表格制作的頁面在響應式布局大行其道的今天,用它布局的頁面開始出現代碼冗余,維護困難等諸多問題。手機端的瀏覽器在播放視頻或其他交互動畫時也不再依賴flashplayer這個給我們帶來無數反感和惱火的插件。
從此,頁面制作的世道變了,從不需要編程就能制作頁面的三劍客,變成了必須懂得相關代碼寫法才能使用的HTML+CSS+JavaScript了。dreamwaverCC版本也恢復了寫代碼做頁面的操作方式,過去的點擊加拖拽的制作方式也消失了。這讓很多不懂編程和HTML等頁面制作核心技術的從業人感到難受。
dreamwaver的老東家Adobe后來也嘗試過推出新模式下通過界面操作來制作網頁的軟件,還搞出一個叫做Muse的軟件,但是依舊沒能撬動代碼書寫的方式。
這個故事在開始學習<div>和css布局之前我都會講給學生(一群文科生)聽,我只是想告訴大家,學習任何計算機技術,我們可以從簡單易學的方式入手,但要有透過這種方式向下挖掘核心知識的決心和勇氣!對于自己從事的工作我們不能滿足于會做,還要盡量透析它的原理,這樣才能在技術換代中不會被輕易淘汰。
在我研究生階段,有一門讓我終生難忘的選修課,這門課叫做《數字娛樂技術概述》,這門課既不娛樂也不概述,但是通篇都是數字,那位年輕的教授為我們透析了游戲、影視特效的核心----計算機圖形學。
從此我開始學習數學。因為老師的一句話:從2000年到現在(2014)雖然各種軟件層出不窮,但是計算機圖形學的核心算法卻幾乎沒什么改變。
向下挖掘雖然很難,但是有必要!與各位共勉!
下面開始今天的內容。
首先,我們將之前的"第一個頁面.html"文件復制一個,叫做"塊元素學習.html"。然后把<body></body>中間的內容清空。
如圖:
下面,我們在<body></body>中間添加<div></div>標簽。示例代碼如下:
<body><div></div></body>
我們看看效果:
啦啦啦,什么都沒有!
為了讓大家可以看出來不同,我們為<div>添加邊框屬性!
我們使用style屬性為<div>添加邊框,style屬性里的代碼就是以后在CSS中使用的代碼!實際上我們已經開始接觸CSS的一些內容了。具體寫法的講解大家可以看這個教程,這里不再贅述。
示例代碼如下:(通過style = "border-style: solid;"可以為很多元素添加邊框,就不需要大家記憶或查詢不同元素的不同寫法,是不是很方便?。?/p>
<div style = "border-style: solid;">
效果如圖:
因為里面沒有內容,所以<div>的寬度是0,因此顯示的就是一條直線。下面我們向<div>中添加內容。
為了看起來花哨些,加張圖片吧!
示例圖片
示例代碼如下:
<div style = "border-style: solid;"><img src = "img/示例圖片/image4.jpg"/ style = "width:50%;"></div>
大家請按照<img>中的scr自行建立文件夾和命名吧!如果您看不懂請參照《HTML元素中的屬性2(路徑詳解)——零基礎自學網頁制作》
效果如下:
其中,我們也是使用了style的方式為<img>設置的寬度,這個設置方法在<div>中一樣使用!
代碼示例:大家注意寫法,不同的屬性都添加到style的雙引號中即可,同時使用;隔開!
<div style = "border-style: solid; width:50%;">
<img src = "img/示例圖片/image4.jpg"/ style = "width:50%;">
</div>
效果如圖:
整個邊框縮小了50%,圖片更有趣,尺寸變成了div的50%乘以自身的50%。這個特性大家要記住。
為了方便觀看,我們去掉div的width設置。同時在<div>中繼續添加<div>標簽。為了方便顯示,我們在新的<div>中添加一段文字!
示例代碼如下:
<div style = "border-style: solid;">
<img src = "img/示例圖片/image4.jpg"/ style = "width:50%;">
<div>
<p>學習網頁制作非常有趣!</p>
</div>
</div>
效果如下:
如果為了美觀,我們讓文字到圖片右邊的空間中怎么做呢?
示例代碼如下:
<div style = "float:right;"><p>學習網頁制作非常有趣!</p></div>
我們通過為新的<div>標簽中的style屬性添加float(浮動)屬性,同時設置為right(右)。
頁面效果如圖:
大家思考一下如何讓圖片與文字都靠在左邊呢?
是不是為圖片style添加float:left;同時把新<div>的float改為left?
我們試試看!
示例代碼如下:
<div style = "border-style: solid;">
<img src = "img/示例圖片/image4.jpg" style = "width:50%; float:left;"/>
<div style = "float:left;">
<p>學習網頁制作非常有趣!</p>
</div>
</div>
頁面效果:
效果完全不對,圖片和文字跑到外邊來了。
這是div布局中經常出現的一個問題!解決方案有點奇葩,既不是修改<img>屬性也不是修改<div>屬性,而是增加一組空的<div></div>標簽!給這個新的空的<div>的style設置為"clear:both"即可修正。
示例代碼如下:
<div style = "border-style: solid;">
<img src = "img/示例圖片/image4.jpg" style = "width:50%; float:left;"/>
<div style = "float:left;">
<p>學習網頁制作非常有趣!</p>
</div>
<div style = "clear:both;"></div>
</div>
頁面效果如下:
值得注意的是,如果您不使用<div>的話直接使用<img>和<p>,同時對兩個標簽的style設置為float:left,是沒有問題的,只有把它們放到<div>中才會出現上面的情況。
代碼如下:
<body>
<img src = "img/示例圖片/image4.jpg" style = "width:50%; float:left;"/>
<p style = "float:left;">學習網頁制作非常有趣!</p>
</body>
頁面顯示效果如下:
大家觀察一下,文字也變小了。至于為什么去掉<div>之后就不會出現上面那種出框的情況,而且文字也變小的問題在以后的講解中我們再深入探討!
現在希望大家可以記牢這個情況和操作,更多布局問題我們會在CSS的浮動(float)的講解中詳細說明。
疫情期間,請大家少出門,不聚會,沒事在家學學網頁制作,即抗擊疫情又提高自己!
喜歡的小伙伴請關注我,閱讀中遇到任何問題請給我留言,如有疏漏或錯誤歡迎大家斧正,不勝感激!
HTML序章(學習目的、對象、基本概念)——零基礎自學網頁制作
HTML是什么?——零基礎自學網頁制作
第一個HTML頁面如何寫?——零基礎自學網頁制作
HTML頁面中head標簽有啥用?——零基礎自學網頁制作
初識meta標簽與SEO——零基礎自學網頁制作
HTML中的元素使用方法1——零基礎自學網頁制作
HTML中的元素使用方法2——零基礎自學網頁制作
HTML元素中的屬性1——零基礎自學網頁制作
HTML元素中的屬性2(路徑詳解)——零基礎自學網頁制作
使用HTML添加表格1(基本元素)——零基礎自學網頁制作
使用HTML添加表格2(表格頭部與腳部)——零基礎自學網頁制作
使用HTML添加表格3(間距與顏色)——零基礎自學網頁制作
使用HTML添加表格4(行顏色與表格嵌套)——零基礎自學網頁制作
16進制顏色表示與RGB色彩模型——零基礎自學網頁制作
HTML中的塊級元素與內聯元素——零基礎自學網頁制作
初識HTML中的<div>塊元素——零基礎自學網頁制作
在HTML頁面中嵌入其他頁面的方法——零基礎自學網頁制作
封閉在家學網頁制作!為頁面嵌入PDF文件——零基礎自學網頁制作
HTML表單元素初識1——零基礎自學網頁制作
HTML表單元素初識2——零基礎自學網頁制作
HTML表單3(下拉列表、多行文字輸入)——零基礎自學網頁制作
HTML表單4(form的action、method屬性)——零基礎自學網頁制作
HTML列表制作講解——零基礎自學網頁制作
為HTML頁面添加視頻、音頻的方法——零基礎自學網頁制作
音視頻格式轉換神器與html視頻元素加字幕——零基礎自學網頁制作
HTML中使用<a>標簽實現文本內鏈接——零基礎自學網頁制作
何一個畫板軟件都有繪制直線的功能,如果想要基于web實現鼠標畫直線該怎么做哪?本文基于html canvas介紹一種實現鼠標繪制直線的方法,最終效果如下。
首先,我們來嘗試先做到畫單條直線,html canvas提供了畫線的方法,這里稍作封裝,draw_line方法使用canvas上下文提供的方法繪制一條直線,start_vector、end_vector使用向量來表示起點和終點。
// 構造向量[x,y]
function make_vect(x, y) {
return [x ,y];
}
// 獲取向量x坐標
function xcor_vect(vector) {
return vector[0];
}
// 獲取向量y坐標
function ycor_vect(vector) {
return vector[1];
}
function draw_line(start_vector, end_vector) {
ctx.beginPath();
//移動到起點
ctx.moveTo(xcor_vect(start_vector), ycor_vect(start_vector));
//繪制起點到終點的直線段
ctx.lineTo(xcor_vect(end_vector), ycor_vect(end_vector));
ctx.stroke();
}
有了上面的draw_line方法,只要通過監聽鼠標按下事件獲取鼠標按下時的坐標點作為起點,再獲取鼠標釋放時的坐標點作為終點,然后調用draw_line方法就可以做到畫一條直線了,示例代碼如下。
...以上代碼省略
// container為html中canvas元素id
var canvas = document.getElementById("container");
var ctx = canvas.getContext("2d");
// 監聽鼠標按下事件
canvas.onmousedown = function(event) {
//按下鼠標獲取起點
let start_point = make_vect(event.clientX, event.clientY);
// 監聽鼠標松開事件
canvas.onmouseup = function(event) {
// 釋放鼠標獲取終點
let end_point = make_vect(event.clientX, event.clientY);
draw_line(start_point, end_point);
}
}
開啟web服務后訪問頁面,選擇canvas合適位置點擊鼠標并拖動可以繪制一條直線,但是只有鼠標釋放時直線才會繪制出來,這樣的體驗顯然是不好的,我們希望直線能夠一直跟蹤鼠標的軌跡。
要跟蹤鼠標軌跡,還需要監聽鼠標移動事件,在鼠標拖動的每個時刻都將鼠標位置作為終點進行畫線,代碼如下:
// container為html中canvas元素id
var canvas = document.getElementById("container");
var ctx = canvas.getContext("2d");
var pressed = false;
// 監聽鼠標按下事件
canvas.onmousedown = function(event) {
//按下鼠標獲取起點
let start_point = make_vect(event.clientX, event.clientY);
pressed = true;
// 監聽鼠標移動事件
canvas.onmousemove = function(event) {
//只有鼠標按下時拖動才需要畫線
if (pressed) {
let end_point = make_vect(event.clientX, event.clientY);
draw_line(start_point, end_point);
}
}
// 監聽鼠標松開事件
canvas.onmouseup = function(event) {
let end_point = make_vect(event.clientX, event.clientY);
draw_line(start_point, end_point);
pressed = false;
}
}
再次測試發現結果很離譜,竟然畫出了很多條線,但值得肯定的是這樣做到了跟蹤鼠標的拖動軌跡,之所以會出現很多條線,是因為每個鼠標拖動位置的直線都已經畫在畫布上了,只要每次鼠標移動事件處理前把之前已畫的線擦除掉就可以了。
// container為html中canvas元素id
var canvas = document.getElementById("container");
var ctx = canvas.getContext("2d");
var pressed = false;
// 監聽鼠標按下事件
canvas.onmousedown = function(event) {
//按下鼠標獲取起點
let start_point = make_vect(event.clientX, event.clientY);
pressed = true;
// 監聽鼠標移動事件
canvas.onmousemove = function(event) {
//只有鼠標按下時推動才需要畫線
if (pressed) {
// 先擦除畫布
ctx.clearRect(0, 0, canvas.clientWidth, canvas.clientHeight);
let end_point = make_vect(event.clientX, event.clientY);
draw_line(start_point, end_point);
}
}
// 監聽鼠標松開事件
canvas.onmouseup = function(event) {
let end_point = make_vect(event.clientX, event.clientY);
draw_line(start_point, end_point);
pressed = false;
}
}
再次測試發現可以很方便的使用鼠標畫一條直線了,而且可以實時跟蹤鼠標的位置來調整直線直到鼠標釋放才會確定繪制哪條直線。
還有一個問題需要解決,現在畫完一條直線后,繼續畫下一條直線就會把之前的清掉,畫布上始終只有一條直線,這樣的畫板是沒什么價值的,我們需要畫布既要能夠實時跟蹤當前繪制直線的樣子,還要能夠保留以前繪制好的直線,就像本文開頭展示的效果一樣。顯然,可以通過定義一個數組來存放歷史直線的坐標數據,每次清空畫布后再把它們繪制出來就可以輕松做到想要的功能了。完整代碼如下:
D3.js 是網絡上幾乎所有最具創新性和令人興奮的信息可視化的幕后黑手。D3 代表數據驅動文檔,它是一個品牌名稱,也是多年來以某種形式提供的一類應用程序。我們可以使用這個庫來構建各種數據驅動的項目,從簡單的條形圖到動態地圖,再到復雜的空間和時間探索。當您希望在數據可視化方面獲得完全的創意和技術自由時,無論您是構建用于研究的交互式原型、頂級科技公司廣泛且完全響應的數據儀表板,還是揭示數據故事的長篇文章,D3 都是您的首選工具當用戶滾動時。
D3 是一個開源 JavaScript 庫,由 Mike Bostock 于 2011 年創建,用于為 Web 生成動態和交互式數據可視化。盡管過去幾年推出了許多新的數據可視化庫,但它們通常在底層使用 D3。這是因為 D3 與 JavaScript 一樣,非常靈活且強大。
圖 1.1 漢密爾頓中每條線的交互式可視化,這是Shirley Wu創建的 D3 項目
D3.js 的創建是為了滿足對可通過網絡訪問的復雜數據可視化的迫切需求。假設您的公司正在使用商業智能工具,但它沒有顯示您的團隊所需的數據模式。您必須構建一個自定義儀表板,根據您的特定領域量身定制,準確顯示客戶的行為方式。該儀表板需要快速、交互式且可在整個組織內共享。D3 將是此類項目的自然選擇。
或者想象一下,您被雇用來實現一個網頁,該網頁以可視化方式展示 LGBTQ+ 群體(女同性戀、男同性戀、雙性戀、跨性別者、酷兒等)的權利在過去幾十年和全世界的演變情況。此頁面包含許多隨著用戶滾動而變化的創意可視化效果。它們通過鼠標事件顯示更多信息并適應屏幕大小。D3 將是構建此類項目的首選工具。
Mike Bostock 最初創建 D3 是為了利用新興的 Web 標準,正如他所說,“避免了專有表示并提供了非凡的靈活性,暴露了 CSS3、HTML5 和 SVG 等 Web 標準的全部功能”(http: // d3js.org)。D3.js 版本 7 是這個流行庫的最新版本,它通過模塊化 D3 的各個部分來延續這一趨勢,使其與 ECMAScript 模塊(一種基于 JavaScript 的腳本語言)和現代應用程序開發完全兼容。
D3.js 使開發人員不僅能夠制作豐富的交互式應用程序,而且能夠制作樣式和服務類似于傳統 Web 內容的應用程序。這使得它們更可移植,更適合增長,并且更容易由其他團隊成員可能不知道 D3 的具體語法的大型團體維護。
圖 1.2 D3 開發人員可以訪問各種數據表示形式,地圖就是一個例子。這是由Christophe Viau創建的數字高程模型 (DEM) 地圖。
Bostock 決定廣泛處理數據并創建一個能夠像圖表一樣簡單、像網絡一樣簡單、像列表一樣簡單地呈現地圖的庫,這也意味著開發人員不需要了解以下內容的抽象和語法:一個用于地圖的庫,另一個用于動態文本內容的庫,還有另一個用于傳統圖形的庫。相反,用于運行交互式網絡可視化的代碼接近于純 JavaScript,并且也類似于表示 D3 地圖上的動態點的代碼。方法是相同的,但數據也可以是相同的,以一種方式制定用于網絡的節點和鏈路,而以另一種方式制定用于地圖上的地理空間表示。
D3 不僅可以創建復雜多樣的圖形,還可以嵌入用戶期望的高水平交互性,這對于現代 Web 開發至關重要。使用 D3,每個圖表的每個元素(從旋轉的地球儀到餅圖的切片)都以相同的方式進行交互。由于 D3 是由精通數據可視化實踐的人編寫的,因此它包含數據可視化和 Web 開發中標準的交互式組件和行為。
圖 1.3 交互性是 D3 的核心。在此網絡可視化中,鼠標交互揭示了不同組織之間的關系以及特定于所選節點的信息(https://amdufour.github.io/organizations-against-polization)。
數據可視化領域正在蓬勃發展,可用于生成數據綁定圖形的工具數量在過去十年中呈爆炸式增長。我們擁有 Excel(數據可視化的常用入口)和 Power BI(用于構建儀表板的 Microsoft 解決方案)等商業智能工具。另一方面,更有經驗的數據科學家通常會轉向 R 的 ggplot2 或 Python 的 matplotlib。
基于瀏覽器的點擊式工具(例如 Tableau、Flourish、DataWrapper、RAWGraphs 和 Google 圖表)也占據了主導地位,允許用最少的技術知識創建令人驚嘆的作品。
最后,HighCharts、Chart.js 和 D3.js 等 JavaScript 庫專門用于開發基于 Web 的交互式可視化。
而且這個列表遠非詳盡無遺......
那么,D3 在數據可視化工具的海洋中處于什么位置呢?我們何時以及如何使用它?我們可以說,雖然 D3 完全可以構建此處列出的數據可視化庫提供的任何圖表,但它通常不是構建簡單的傳統圖表或探索階段(我們調查哪種類型的可視化是)的首選選項。最適合代表我們的數據。構建 D3 項目需要時間,而 D3 在復雜、交互式和定制的項目中真正表現出色。數據可視化不僅僅是折線圖和散點圖!雖然上面提到的工具通常專注于預定義的圖表,但 D3 允許我們將數據綁定到任何圖形元素,并通過以獨特的方式組合這些視覺元素來打破常規。
圖 1.4 D3 具有 SVG 和畫布繪圖功能,允許開發人員構建自定義可視化效果,例如Elijah Meeks的樂譜表示形式。
以下是我們如何在數據可視化項目范圍內使用 D3 的示例。首先,我們從預先存在的數據集或手動收集的數據開始。在開始數據分析過程之前,我們通常會花費大量時間清理、格式化和準備數據。Python 和 R 等數據科學工具在這方面功能強大,可以幫助我們識別隱藏在數據中的故事。Excel 還可以完成簡單的數據整理和數據分析工作,并且需要較少的技術背景。我們甚至可以使用 JavaScript 和 D3 進行基本數據探索,因為它們提供了我們將在本書后面討論的統計方法。
一旦數據分析開始,通常會創建一些原型來幫助完善我們的故事。Tableau 和 RawGraphs 等工具使我們能夠快速生成此類圖表。這是非常重要的一步,在此階段創建的可視化通常并不花哨或精致。我們不想在原型設計階段過于執著于我們的想法,花費大量時間。我們可能會發現自己必須“殺死我們的寶貝”并重新開始幾次,直到我們找到最適合我們想要講述的故事的可視化效果。網絡圖可能是一個例外,直接跳到 D3 對于這些項目通常是有意義的。
最后,一旦我們知道要創建的可視化類型,就該卷起袖子,對其進行編碼,并使用 D3 對其進行完善。如今,編碼步驟通常發生在單頁應用程序 (SPA) 中,使用 React 或 Svelte 等框架。
圖 1.5 使用 D3 構建的自定義可視化的另一個示例,其中形狀與每首歌曲的不同屬性(例如持續時間、流派和節奏)成比例(https://amdufour.github.io/spotify-hits)。
您可能已經嘗試過 D3,并發現它并不容易上手。也許那是因為您希望它是一個簡單的圖表庫。一個恰當的例子是創建條形圖,我們將在第 2 章和第 3 章中進行此操作。D3 沒有一個函數來創建條形圖。相反,它有一個將<svg>容器附加到文檔對象模型 (DOM) 的函數,以及另一組附加容器的函數。<rect>每個數據點的元素。然后,我們使用比例來計算構成直方圖的矩形的長度并設置它們的屬性。最后,我們調用另一組函數,將 x 軸和 y 軸添加到條形圖中。如圖 1.6 所示,這個過程比使用 Highcharts 等專用圖表庫要長得多。但 D3 處理數據和圖形的明確方式也是它的優勢。盡管其他圖表庫允許您方便地制作折線圖和餅圖,但當您想要創建不屬于傳統圖表范圍的可視化效果或實現自定義交互時,它們很快就會崩潰。不是D3。D3 允許您構建您可以想象的任何數據驅動圖形和交互性。
圖 1.6 使用 Highcharts 與 D3.js 生成的條形圖。Highcharts 的代碼更簡單、更短,但 D3.js 更通用。
在圖 1.7 中,您可以看到我們通常如何使用 D3 進行數據可視化編碼的地圖。我們從一個數據集(通常是 CSV 或 JSON 文件)開始,然后使用 d3-fetch 模塊將此數據集加載到我們的項目中。我們通常需要執行一些操作來格式化數據。例如,我們確保數字和日期的格式正確。如果我們之前沒有這樣做,我們可能還想詢問我們的數據集以找到其主要特征。例如,提前知道其最大值和最小值通常很有幫助。然后我們準備開始構建可視化,為此我們將結合我們將在本書中學習的不同 D3 函數。最后,我們通過監聽鼠標事件來添加交互性,允許用戶過濾數據或放大可視化。
圖 1.7 如何使用 D3.js 實現數據可視化
D3.js 從來不會單獨使用,而是我們結合起來創建豐富的 Web 界面的技術和工具生態系統的一部分。與任何網頁一樣,D3 項目是在 DOM(文檔對象模型)內構建的,并利用 HTML5 的強大功能。盡管 D3 可以創建和操作傳統的 HTML 元素,例如分區 ( <div>) 和列表 ( <ul>, <ol>),但我們主要使用 SVG 圖形或在畫布(從腳本渲染位圖圖像的 HTML 元素)內生成可視化效果。然后,我們還可以使用舊的 CSS 樣式表,它可以增強 D3 項目并使其設計更易于維護,尤其是在廣泛的團隊中。
鑒于 D3 是一個 JavaScript 庫,我們自然傾向于將 D3 方法與本機 JavaScript 函數結合起來來訪問和操作數據。D3 現在完全支持 JavaScript 的 ECMAScript 2015 或 ES6 修訂版以及大多數最新更新。D3 還作為模塊提供,可以集成到我們構建 Web 項目所用的最新框架和庫中。使用這些模塊通常是首選方法,因為它不會污染我們應用程序的全局范圍。
在本節中,我們將簡要討論這些技術及其在 D3 生態系統中的作用。由于 SVG 知識是理解 D3 的基礎,因此我們將花時間更詳細地解釋您開始構建可視化所需理解的基礎知識。如果您已經熟悉 HTML、SVG 元素、CSS、JavaScript 和 JavaScript 模塊,請隨意瀏覽或跳至第 1.3 節。
與 GIF 動畫和框架成為網絡動態內容頂峰的時代相比,我們已經走過了很長一段路。在圖 1.8 中,您可以看到為什么 GIF 從未在強大的基于 Web 的數據可視化中流行起來。GIF 與設計用于使用 VML(矢量標記語言)的 infoviz 庫一樣,對于早期瀏覽器來說是必需的,但 D3 是為不再需要向后兼容性的現代瀏覽器而設計的。
圖 1.8 20 世紀 90 年代的一些例子,比如dpgraph.com,仍然存在,讓我們想起動畫 GIF 無處不在的時代。
當您登陸網頁時,要加載的第一個文件是超文本標記語言或 HTML 文件,如下例所示。瀏覽器解析 HTML 文件以構建文檔對象模型或 DOM,這是用于 Web 文檔的編程接口。我們經常將其稱為 DOM 樹,因為它由一組嵌套元素(也稱為節點或標簽)組成。在我們的示例中,<head>和 the<body>元素是<html>父元素的子元素。同樣,標簽是、the和標簽<body>的父標簽。標題也是該元素的同級元素。當您加載網頁時,您在屏幕上看到的是標記中包含的元素。
<!DOCTYPE#nbsp;html>
<html>
<head>
<meta charset="UTF-8">
<title>A simple HTML file | D3.js in Action</title>
</head>
<body>
<h1>I am a title</h1>
<div>
<p>I am a paragraph.</p>
<p>I am another paragraph.</p>
</div>
</body>
</html>
在 DOM 中,每個元素的三類信息定義了其行為和外觀:樣式、屬性和特性。樣式決定顏色、大小、邊框、不透明度等。屬性包括類、id 和交互行為,盡管某些屬性也可以確定外觀,具體取決于您正在處理的元素類型。對于 SVG 元素,屬性用于設置不同形狀的位置、大小和比例。屬性通常指的是狀態,例如復選框的“checked”屬性,如果該框被選中,則該屬性為 true;如果該框未被選中,則該屬性為 false。盡管術語“屬性”和“屬性”經常互換使用,但它們是兩個不同的東西。呈現 DOM 時,屬性顯示為初始狀態。屬性是元素的當前狀態,并且可以隨著用戶與界面交互而改變。在第2章中,我們將討論用于生成或修改HTML和SVG元素的樣式和屬性的D3方法。
DOM 還決定元素在屏幕上的繪制順序,子元素在父元素之后和內部繪制。盡管 CSS 屬性z-index使我們能夠部分控制傳統 HTML 元素繪制到屏幕上的順序,但 SVG 元素嚴格遵循它們在 DOM 中出現的順序。根據畫家的模型,之后繪制的內容出現在之前繪制的內容之上。
可擴展矢量圖形 (SVG) 的引入確實改變了網絡的面貌。幾年之內,SVG 圖形成為主要的 Web 開發工具。光柵圖形(PNG 和 JPG)由微小的像素組成,當我們放大得太近時,這些像素就會變得可見,而矢量圖形則是通過數學和幾何圖形構建的。它們在任何尺寸和任何屏幕分辨率下都能保持清晰的外觀。SVG 圖形的另一個顯著優勢是它們可以直接注入 DOM,允許開發人員操縱其元素并為其設置動畫,并使屏幕閱讀器可以訪問它們。如果構建正確,SVG 也具有高性能,其文件大小僅為等效光柵圖像的一小部分。
當使用 D3 創建數據可視化時,我們通常將 SVG 形狀注入 DOM 并修改其屬性以生成組成可視化的視覺元素。了解 SVG 的工作原理、主要 SVG 形狀及其表示屬性對于大多數 D3 項目至關重要。
本書的每一章都包含旨在支持您的學習體驗的代碼練習。我們強烈建議您“做”這本書,而不僅僅是“讀”這本書,這意味著在閱讀章節時完成練習。通過這種方式,您將保留更多信息,并很快就能構建自己的 D3 項目!
對于每個練習和項目,您都可以訪問現成的代碼文件。您可以在本書的 Github 存儲庫(https://github.com/d3js-in-action-third-edition/code-files)上找到它們。如果您熟悉 Git,則可以將存儲庫克隆到您的計算機上。您還可以下載壓縮文件。
從 Github 存儲庫下載代碼文件
每一章都有自己的文件夾,其中包含一個或多個練習,按照每章中的部分編號。練習包括一個start文件夾,其中包含入門所需的所有文件。您將在文件夾中找到練習的完整解決方案end。當您完成一章的各個部分時,您可以繼續在上一部分使用的文件中進行編碼,或者使用專用于該部分的文件夾重新開始。兩種選擇都會導致相同的結果。
讓我們開始探索矢量圖形。轉到本書提供的代碼文件。找到end中的文件夾chapter_01/SVG_Shapes_Gallery并右鍵單擊該文件index.html。在菜單中,轉到打開方式并選擇瀏覽器。我們建議使用 Chrome 或 Firefox,因為它們具有出色的檢查器工具。該文件將在新的瀏覽器選項卡中打開,并且將出現您在圖 1.9 中看到的矢量圖形。您還可以在 Github 托管項目 ( https://d3js-in-action-third-edition.github.io/svg-shapes-gallery ) 上查看這些 SVG 形狀。
圖 1.9 我們將在本節中構建的基本 SVG 形狀圖庫。
您正在查看的 SVG 圖形包含創建 D3 可視化時最常使用的形狀:線條、矩形、圓形、橢圓形、路徑和文本。
使用 D3 時,您通常會告訴庫應將哪些形狀附加到 DOM。您還負責了解需要計算哪些表示屬性才能使形狀具有您正在尋找的尺寸、顏色和位置。在下面的練習中,您將編寫創建圖 1.9 中每個 SVG 元素的代碼。我們將此練習稱為SVG 形狀圖庫。之后,您將了解入門所需的所有 SVG 基礎知識。
在您選擇的代碼編輯器中打開練習文件夾index.html中的文件。我們推薦VS Code,這是一個免費、易于使用的代碼編輯器,并且具有多種功能,對前端開發很有幫助。startSVG_Shapes_Gallery
正如您所看到的,index.html是一個簡單的 HTML 文件。如果您在瀏覽器中打開此文件(右鍵單擊該文件并在“打開方式”菜單中選擇瀏覽器),您將只會看到一個空白頁面。這是因為該<body>元素為空。在接下來的小節中,我們將向此<body>元素添加 SVG 形狀。
清單 1.1.a SVG 形狀庫練習的起始 HTML 文件
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SVG Shapes Gallery | D3.js in Action</title>
</head>
<body>
</body>
</html>
以下部分將介紹多個 SVG 元素及其屬性。作為開發人員,我們在構建項目、使用我們不熟悉的 SVG 元素或尋找 JavaScript 函數來執行特定操作時嚴重依賴在線資源。在前端開發中,MDN Web Docs ( https://developer.mozilla.org/ ) 始終是可靠且全面的資源。它包含 HTML 元素及其屬性、CSS 屬性和 JavaScript 函數的易于理解且通??删庉嫷氖纠?/span>
在 SVG 圖形的世界中,<svg></svg>容器是在其上繪制所有內容的白板。每個 SVG 形狀都嵌套在<svg>父級中。要查看其實際效果,請在元素index.html內編輯并添加 SVG 容器<body>。在瀏覽器中重新加載頁面。目前還看不到任何東西。
<body>
<svg></svg>
</body>
打開瀏覽器的檢查器工具(在瀏覽器窗口中右鍵單擊并選擇“檢查”)。在檢查器窗口中,您將看到組成頁面的 DOM。找到<svg></svg>容器,也稱為 SVG 節點。當您在檢查器中將鼠標移到它上面時,SVG 元素會在頁面上突出顯示。您可以在圖 1.5 中看到此效果。
圖 1.10 在 DOM 樹中選擇并在視口中突出顯示的 SVG 節點
默認情況下,瀏覽器為 SVG 容器提供寬度300px和高度。150px但我們也可以使用屬性來分配這些值。屬性用于提供有關 HTML 元素的附加信息。對于內聯 SVG,我們主要使用屬性來設置組成 SVG 圖形的元素和形狀的大小和位置。
例如,我們可以設置SVG 容器的寬度和高度屬性。返回到文本編輯器,并將 awidth和 aheight屬性添加到 SVG 容器。將它們的值設置為900和300并保存文件。
<svg width="900" height="300"></svg>
在瀏覽器中重新加載項目并在檢查工具中找到 SVG 節點。請注意,寬度和高度屬性現在顯示在 SVG 容器的括號內。如果將鼠標移到檢查工具 DOM 樹中的 SVG 節點上,您還會看到視口中的 SVG 容器現在的大小為 900 像素 x 300 像素。
圖1.11 SVG節點采用其屬性指定的大小
為了幫助我們查看 SVG 容器,而不必從檢查器中突出顯示它,讓我們給它一個邊框。向 SVG 容器添加樣式屬性并插入 CSS 邊框屬性。在下一個代碼片段中,我們使用 border 速記屬性創建一個寬度為 1px 的黑色實心邊框。
<svg width="900" height="300" style="border:1px solid black;"></svg>
保存文件,重新加載頁面并確認 SVG 容器周圍有邊框?,F在,調整瀏覽器窗口的大小,直到它小于 SVG 容器。您將觀察到 SVG 容器保持固定的寬度和高度,并且不適應瀏覽器窗口的大小。讓我們嘗試使 SVG 容器具有響應能力。
之前,我們將 SVG 屬性設置為絕對值 (900和300),瀏覽器將它們解釋為以像素為單位的測量值 (900px和300px)。但我們也可以使用百分比。在文本編輯器中,將寬度屬性更改為相對值“ 100%”,保存文件并重新加載頁面。
<svg width="100%" height="300" style="border:1px solid black;"></svg>
再次調整瀏覽器大小,并注意 SVG 如何獲取可用的完整寬度并保持 300 像素的固定高度。這更好,但我們失去了原來的縱橫比。
為了使內聯 SVG 具有響應能力,我們可以使用viewBox 屬性。在代碼編輯器中,從 SVG 容器中刪除width和屬性,并將它們替換為屬性。給它一個值。heightviewBox"0 0 900 300"
<svg viewBox="0 0 900 300" style="border:1px solid black;"></svg>
再次調整瀏覽器窗口的大小。你注意到了什么?SVG 容器現在可以適應任何屏幕尺寸,同時保持其縱橫比為 900:300。我們有一個響應式 SVG!
正如您所注意到的,viewBox 屬性由四個值的列表組成。前兩個數字指定 viewBox 坐標系的原點(x 和 y)。在本書中,我們將始終使用0 0,但很高興知道這些值可用于更改 SVG 容器的哪一部分在屏幕上可見。viewBox 屬性的最后兩個數字是其寬度和高度。它們定義 SVG 的縱橫比,并確保其完美縮放以適合任何容器而不變形。
安裝在容器內是這里的關鍵。到目前為止,我們的內聯 SVG 的容器是 HTML<body>元素,它通常會擴展以適應瀏覽器的視口。如果視口變得非常大,SVG 也會變得非常大。通常,我們希望 SVG 具有最大寬度,以便它不會大于頁面上的其余內容。為此,請將 SVG 容器包裝在寬度為 100%、最大寬度為 1200px 的 div 內。為簡單起見,我們將這些屬性設置為內聯樣式,但在實際項目中,這些屬性將來自 CSS 文件。請注意,我們還添加了值邊距“ 0 auto”,以使 SVG 在頁面上水平居中。
<div style="width:100%; max-width:1200px; margin:0 auto;">
<svg viewBox="0 0 900 300" style="border:1px solid black;"> ... </svg>
</div>
嘗試再次調整瀏覽器的大小,看看我們的 SVG 如何優雅地適應任何屏幕尺寸,同時尊重其容器的最大寬度。該策略有助于將 D3 可視化注入響應式網頁,我們將在本書中使用它。
既然我們知道了如何使內聯 SVG 具有響應性,那么解決 SVG 形狀在 SVG 容器內的定位方式就很重要了。SVG 容器就像一張白紙,我們可以在上面繪制矢量形狀。矢量形狀是根據基本幾何原理定義的,并參考 SVG 容器的坐標系進行定位。
SVG 坐標系與笛卡爾坐標系類似。其 2D 平面使用兩個垂直軸來確定元素的位置,稱為 x 和 y。這兩個軸源自SVG 容器的左上角,如圖 1.12 所示。意思是y軸的正方向是從上到下。記住這一點可以讓你避免一些頭痛!
圖1.12 SVG容器的坐標系和元素的位置
為了在 SVG 容器內定位元素,我們從左上角的原點開始向右移動。這將為我們提供元素的水平 (x) 位置。對于垂直 (y) 位置,我們從頂部開始向下移動。這些位置由每個 SVG 形狀的表示屬性定義。
現在,我們將了解您在構建 D3 項目時經常遇到的 SVG 形狀。我們還將討論它們的主要表現屬性。這里的目標絕不是編寫 SVG 提供的所有形狀和功能的綜合指南,而是涵蓋支持您的 D3 之旅的基礎知識。
出色的藝術家可以用矢量圖形繪制任何東西,但您可能不會因為您是一名藝術家而關注 D3。相反,您正在處理圖形并考慮更務實的目標。從這個角度來看,理解幾何基元(也稱為圖形基元)的概念至關重要。幾何基元是簡單的形狀,例如點、線、圓和矩形。這些形狀可以組合成更復雜的圖形,特別方便直觀地顯示信息。
基元對于理解您在現實世界中看到的復雜信息可視化也很有用。樹形布局,就像我們將在第 10 章中構建的那樣,當您意識到它們只是圓形和直線時,它們就不那么令人生畏了。當您將交互式時間線視為矩形和點的集合時,它們更容易理解和創建。即使是主要以多邊形、點和線形式出現的地理數據,當您將其分解為最基本的圖形結構時,也不會那么混亂。
線條元素可能是所有 SVG 形狀中最簡單的。它獲取兩個點的位置,設置為屬性,并在它們之間繪制一條直線。返回到該index.html文件,并在 SVG 容器內添加一個<line />元素。聲明其屬性x1和y1并分別賦予它們值 50 和 45。這意味著我們的線的起點位于(50, 45)SVG 容器的坐標系中。如果從 SVG 容器的左上角開始,向右移動 50 像素,向下移動 45 像素,您將遇到線條的起點。(140, 225)同樣,使用屬性x2和y2將線的端點設置為。
<svg>
<line x1="50" y1="45" x2="140" y2="225" />
</svg>
圖1.13 在SVG容器的坐標系中定位線元素
如果您保存并重新加載項目,您的線條將不可見,您可能想知道發生了什么。為了使 SVG 線條在屏幕上可見,我們還需要設置其描邊屬性,該屬性控制線條的顏色。border 屬性的值與 CSS color 屬性類似。它可以是顏色名稱 ( black, blue, ...)、RGB 顏色 ( rgb(255,0,0)) 或十六進制值 ( #808080)。向您的線條添加筆劃屬性,并為其指定您選擇的顏色(我們使用黑色)。現在它應該在屏幕上可見。
<line x1="50" y1="45" x2="140" y2="225" stroke="black" />
如果我們想設置線條的寬度,我們可以使用描邊寬度屬性。此屬性接受絕對數字(轉換為像素)或相對值 (%)。例如,以下行的 a 為stroke-width3px。如果stroke-width未聲明該屬性,瀏覽器將應用默認值 1px。
<line x1="50" y1="45" x2="140" y2="225" stroke="black" stroke-width="3" />
打開瀏覽器的檢查器工具并找到 SVG 節點及其包含的行。雙擊其中一個屬性,更改其值并觀察新值如何修改線的起點或終點。花時間嘗試不同的值,以確認您了解屬性x1、y1、x2和如何y2影響線條的位置和長度。
-20現在,為屬性賦予 值x1。你看到線的起點是如何消失的嗎?落在 SVG viewBox 之外的任何形狀或形狀部分在屏幕上都不可見。不過,該元素仍然存在于 DOM 中。我們可以訪問和操縱它。如果 SVG 中的某個元素不可見,并且您不知道為什么首先要檢查它是否在 SVG viewBox 之外!請記住,您始終可以通過使用開發人員工具檢查 DOM 來找到它。正如我們之前所做的那樣,如果將鼠標移到檢查器工具中的元素上,即使它位于 SVG viewBox 之外,它也會在視口中突出顯示。
圖 1.14 SVG 線在 SVG 容器外部時部分隱藏
為了提高效率,大多數 SVG 元素只需要一個自閉合標簽(我們使用 <line /> 而不是 <line></line>)。與其他一些 HTML 標簽一樣,SVG 元素的固有結構在自閉合標簽中提供了所有必需的信息。這對于 SVG 文本元素有所不同,其中文本放置在開始標簽和結束標簽之間。
顧名思義,矩形元素<rect />在屏幕上繪制一個矩形形狀。該<rect />元素需要四個屬性才能可見。屬性x和y聲明矩形左上角的位置,而屬性width和height分別控制其寬度和高度。<rect />在 SVG 容器中添加以下元素及其屬性。
<rect x="260" y="25" width="120" height="60" />
在我們的示例中,矩形的左上角位于SVG 容器原點的260px右側和下方。25px它的寬度為120px,高度為60px。與其他位置屬性一樣,我們可以使用百分比而不是絕對數字來設置它們的值。例如,如果我們將該width屬性設置為50%,則矩形將擴展到 SVG 容器寬度的一半。
圖 1.15 在 SVG 容器的坐標系中定位矩形并調整其大小
您可能已經注意到我們的矩形充滿了黑色。默認情況下,瀏覽器對大多數 SVG 形狀應用黑色填充。我們可以通過設置fill屬性并為其指定任何 CSS 顏色來更改該顏色。如果我們想給矩形添加邊框,我們添加一個描邊屬性。圖 1.16 顯示了一些示例。請注意,如果不聲明屬性,則矩形周圍不會繪制邊框stroke。另外,在最后一個矩形中,屬性fill-opacity和border-opacity用于使fill和stroke半透明。與 CSS 中一樣,不透明度可以設置為絕對值 ( 0.3) 或百分比 (30%)。與填充和描邊相關的所有屬性也可以從 CSS 文件設置或修改。
圖 1.16 應用于矩形 SVG 形狀的不同樣式屬性
如果您希望矩形具有圓角,則只需添加rx和ry屬性,分別是水平和垂直角半徑。這些屬性接受絕對值(以像素為單位)和相對值(百分比)。例如,下面矩形的每個角的半徑都是 20px。將此矩形添加到您的形狀庫中。
<rect x="260" y="100" width="120" height="60" rx="20" ry="20" />
此時,您可能想知道 SVG 中是否有一個可以繪制正方形的元素。我們不需要一個!在 SVG 中,我們<rect />通過賦予元素相等width和height屬性來繪制帶有元素的正方形。例如,以下<rect />元素將繪制一個 60px x 60px 的正方形。也將它添加到您的形狀庫中。
<rect x="260" y="175" width="60" height="60" />
作為參考,我們的形狀庫中現在有三種類型的 SVG 矩形:經典矩形、圓角矩形和正方形。為了好玩,我們給了它們顏色#6ba5d7并玩弄它們stroke和fill屬性。請注意,只有筆劃在正方形上可見,因為其fill屬性值為transparent或none。您的矩形應該類似于圖 1.17 中的矩形,除非您更改了它們的屬性(我們鼓勵您這樣做)!
<rect x="260" y="25" width="120" height="60" fill="#6ba5d7" />
<rect x="260" y="100" width="120" height="60" rx="20" ry="20"
? fill="#6ba5d7" />
<rect x="260" y="175" width="60" height="60" fill="transparent"
? stroke="#6ba5d7" />
當您嘗試在可視化中對齊形狀時需要記住的一點是,筆劃是均勻地繪制在 SVG 形狀的內部和外部邊界上的。如下圖所示,如果矩形的width屬性為 40px,則應用 ofstroke-width會1在視覺上向矩形的左側添加 0.5px,向右側添加 0.5px(而不是像我們本能地認為的那樣,向每邊添加 1px) ),實際總寬度為 41px。如果stroke-width是2,它會在每邊添加 1px,依此類推。
筆劃寬度對 SVG 形狀實際寬度的影響
圓形形狀經常用于數據可視化。它們自然地吸引眼球,并使可視化感覺更加友好和有趣。我們使用<circle />元素繪制 SVG 圓圈。其所需屬性是圓心的位置 ( cx , cy ) 及其半徑 ( r )。圓的半徑是從圓心到其邊界上任意點所繪制的直線的長度。將以下圓圈添加到您的形狀庫中。將其中心定位于(530, 80)并為其指定 50px 的半徑。
<circle cx="530" cy="80" r="50" />
圖 1.18 在 SVG 容器的坐標系中定位圓和橢圓并調整其大小
您還可以使用圓形的填充和描邊屬性。為了生成圖 1.18 中的效果,我們使用了透明填充和 3px 的描邊,顏色為#81c21c。
類似地,<ellipse />元素需要形狀中心位置的屬性 ( cx, cy)。圓形具有恒定的半徑,而橢圓形的半徑則不同,從而使其具有扁平形狀。我們通過聲明水平半徑 ( rx ) 和垂直半徑 ( ry ) 來創建這種扁平化效果。將下一個片段添加到您的圖庫中。它將在圓下方繪制一個橢圓,水平半徑為 50px,垂直半徑為 30px。
<ellipse cx="530" cy="205" rx="50" ry="30" />
SVG路徑是迄今為止所有 SVG 元素中最靈活的。它們在 D3 中廣泛用于繪制幾乎所有無法用迄今為止討論的形狀基元之一(直線、矩形、圓形和橢圓形)表示的復雜形狀和曲線。
d我們通過聲明其屬性(代表“draw”)來指示瀏覽器如何繪制路徑。該d屬性包含一個命令列表,從開始繪制路徑的位置到要使用的曲線類型,直到指定我們是否希望路徑成為閉合形狀。例如,將以下路徑元素添加到您的庫中。在此示例中,d屬性以M680 150開頭,表示“移動到坐標(680, 150)”。然后我們從當前點 (680, 150) 到字母 C 后面的第三個坐標 (755 150) 指定的端點繪制一條三次貝塞爾曲線。三次貝塞爾曲線需要控制點,即字母 C 后面、起點和終點之間的坐標((710, 80) 和 (725, 80))。這些控制點定義了曲線的陡峭程度。然后我們有字母 S,它代表“停止”。它的工作原理與字母 C 類似,只不過它通向曲線的端點。這里最后一條曲線的起點是(755 150),終點是(840, 150),控制點是(810, 220)。曲線可以由一個或兩個控制點定義。
<path d="M680 150 C 710 80, 725 80, 755 150 S 810 220, 840 150" fill="none"
? stroke="#773b9a" stroke-width="3" />
圖 1.19 一個簡單的 SVG 路徑及其控制點
要深入了解 SVG 路徑,請參閱 MDN 的教程:https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths。
手動編寫d屬性對于簡單的形狀是可行的,但隨著形狀變得復雜而變得乏味。幸運的是,D3 具有強大的形狀生成器,我們將在第 4 章中討論。
關于路徑要記住的另一件重要的事情是瀏覽器將以黑色填充它們,除非我們將它們的fill屬性設置為noneor transparent。即使路徑未閉合(如我們的示例中所示),情況也是如此。
<div>內聯 SVG 圖形的最大優點之一是它們可以包含可導航的文本,就像插入一個或一個元素中的任何其他 HTML 文本一樣<p>。這對于可訪問性來說是一個很大的優勢。
由于數據可視化通常包含多個標簽,因此有必要了解如何使用<text></text>元素操作 SVG 文本。讓我們向形狀庫添加標簽,以了解 SVG 文本的基本原理。
到目前為止討論的 SVG 形狀使用自閉合標簽 ( <line />, <rect />, <path />, ...)。使用 SVGtext元素時,我們需要使用開始標簽和結束標簽。我們將要顯示的文本放置在這兩個標簽之間。例如,讓我們在 SVG 中添加一個表示“line”的文本元素。
<text>line</text>
保存文件并重新加載頁面。您可能希望文本出現在 SVG 容器的左上角,但實際上卻看不到......這是為什么?默認情況下,SVG 文本的位置是參考其基線計算的,由屬性控制dominant-baseline。如果文本基線的坐標是(0, 0),您可以在圖 1.20 中看到實際文本如何最終出現在 SVG 容器之外。由于位于 SVG 容器外部的任何元素都是不可見的,因此我們看不到文本。
圖 1.20 位于 SVG 容器外部的文本
使用 SVG 文本時要考慮的另一點是文本的流動方式。常規 HTML 元素按照控制內容流的特定規則放置在頁面上。如果您將一堆<div></div>元素插入頁面中,它們會自然地堆疊在一起,并且它們的內容將重排,以便它永遠不會超出其容器。SVG 文本根本不流動,每個 SVG 元素必須單獨定位。一種方法是設置它們的x和y屬性。如果我們使用這些屬性將文本放置在 處(60, 260),標簽“line”將出現在形狀庫中 SVG 線的下方。
<text x="60" y="260">line</text>
為了練習,創建一個新的文本元素,將標簽“矩形”放置在矩形和正方形下方。
到目前為止,我們已經使用x和y屬性來聲明文本元素的左下角。但是如果我們想設置文本中點的位置怎么辦?我們可以通過使用屬性text-anchor并為其指定值來做到這一點middle。例如,我們可以使用此屬性將圓形的文本標簽居中。
<text x="530" y="155" text-anchor="middle">circle</text>
圖1.21 text-anchor屬性影響SVG文本的對齊方式。它的默認值是“開始”。為了根據中間對齊文本元素,我們應用“middle”的 text-andchor 屬性。類似地,為了根據文本的結尾對齊文本,我們應用“end”的 text-andchor 屬性。
最后為橢圓添加一個標簽,為路徑元素添加另一個標簽。默認情況下,SVG 文本為黑色。您可以使用屬性更改其顏色fill。
我們將在本節中討論的最后一個 SVG 元素是組元素。group 或<g></g>元素與我們迄今為止討論的 SVG 元素不同,因為它沒有圖形表示,也不作為有界空間存在。相反,它是元素的邏輯分組。在創建由多個形狀和文本元素組成的可視化效果時,您需要廣泛使用組。
如果我們希望正方形和“rect”標簽一起顯示并在 SVG 容器中作為一個整體移動,我們可以將它們放置在一個<g>元素內,如下例所示。請注意元素的左上角如何<rect>更改為(0, 0)。位于<text>處以(0, 85)保持其低于<rect>。
<g>
<rect x="0" y="0" width="60" height="60" />
<text x="0" y="85">rect</text>
</g>
包含正方形及其標簽的組現在顯示在 SVG 容器的左上角。我們可以將這個組及其包含的所有元素移動到 SVG 容器中任何我們想要的位置,同時保持正方形與其標簽之間的對齊。
在 SVG 容器中移動組是通過轉換屬性完成的。變換屬性比目前討論的屬性有點嚇人,但與 CSS 變換屬性相同。它采用一個變換(平移、旋轉、縮放等)或一堆變換作為值。為了移動一個組,我們使用translate(x, y)變換。如果我們想要將<rect>和<text>元素移回其原始位置,我們需要對元素應用向右 260 像素和向下 175 像素的平移。<g>為此,我們將其 Transform 屬性設置為transform="translate(260,175)"。
<g transform="translate(260,175)">
<rect x="0" y="0" width="60" height="60" />
<text x="0" y="85">rect</text>
</g>
<g> 元素的另一個有用的方面是它的子元素繼承它的屬性。為了說明這一點,讓我們將<g>元素中所有剩余的文本元素分組,除了標簽“矩形”,我們已經將其與正方形分組。
<g>
<text x="60" y="260">line</text>
<text x="530" y="155" style="text-anchor:middle">circle</text>
<text x="530" y="260" style="text-anchor:middle">ellipse</text>
<text x="730" y="260">path</text>
</g>
#636466如果我們對組應用填充屬性,<text>則該組內的每個元素將繼承相同的顏色。同樣,如果我們向組添加樣式屬性,例如使用font-family和font-size屬性,則組內的文本將繼承這些屬性。
<g fill="#636466" style="font-size:16px; font-family:monospace">
<text x="60" y="260">line</text>
<text x="530" y="155" style="text-anchor:middle">circle</text>
<text x="530" y="260" style="text-anchor:middle">ellipse</text>
<text x="730" y="260">path</text>
</g>
最后重新加載頁面并觀察組內的標簽如何繼承組的顏色和字體,而保留在該組之外的標簽則保持其原始外觀。這種將共享屬性應用于組元素的技術非常方便,可以幫助您將 DRY(不要重復自己)編碼原則應用到您的工作中。當您需要更新這些屬性時,它也會讓您的生活更輕松。
恭喜您完成了本書的第一個練習!您可以在清單 1.1.b 和編碼文件的末尾文件夾中找到形狀庫的完整代碼。當您構建第一個 D3 項目時,請使用此練習作為參考。
清單 1.1.b 用于 SVG 形狀圖庫練習的完整 HTML 文件
<!DOCTYPE html>
<html>
<head> [...] </head>
<body>
<div style="width:100%; max-width:1200px; margin:0 auto;">
<svg viewBox="0 0 900 300" style="border:1px solid black;">
<line x1="50" y1="45" x2="140" y2="225" stroke="black" />
<rect x="260" y="25" width="120" height="60" fill="#6ba5d7" />
<rect x="260" y="100" width="120" height="60" rx="20" ry="20"
? fill="#6ba5d7" />
<g transform="translate(260, 175)">
<rect x="0" y="0" width="60" height="60" fill="transparent"
? stroke="#6ba5d7" />
<text x="0" y="85">rect</text>
</g>
<circle cx="530" cy="80" r="50" fill="none" stroke="#81c21c" stroke-
? width="3" />
<ellipse cx="530" cy="205" rx="50" ry="30" fill="#81c21c" />
<path d="M680 150 C 710 80, 725 80, 755 150 S 810 220, 840 150"
? fill="none" stroke="#773b9a" stroke-width="3" />
<g fill="#636466" style="font-size:16px; font-family:monospace">
<text x="60" y="260">line</text>
<text x="530" y="155" style="text-anchor:middle">circle</text>
<text x="530" y="260" style="text-anchor:middle">ellipse</text>
<text x="730" y="260">path</text>
</g>
</svg>
</div>
</body>
</html>
現在輪到你了!創建如下圖所示的 SVG 圖形。您可以在本章代碼文件start內的文件夾中工作。02_SVG_exercise以下是一些指導原則:
· 創建一個寬度和高度均為 400 像素的響應式 SVG 容器(當屏幕上有足夠的空間時)。
· 繪制一個寬度和高度均為 200 像素的正方形。將其置于 SVG 容器的中心,并為其提供透明填充和 5px 黑色描邊。
· 在 SVG 容器的中心添加一個半徑為 100px 的圓。將其填充屬性設置為 CSS 顏色名稱“plum”。
· 繪制兩條對角黑線,筆劃為 5 像素。一個從正方形的左上角到右下角。另一個從正方形的右上角到左下角。
· 添加文本“SVG 太棒了!” 位于正方形上方并將其置于 SVG 容器的中心。為文本指定以下樣式屬性:字體大小為 18px,字體系列為 sans-serif。
我們鼓勵您構建此 SVG 圖形來強化本節中討論的概念。
02_SVG_exercise / end您可以在附錄 D 的 D.1.1 節和本章代碼文件的文件夾中找到解決方案。我們鼓勵您嘗試自己完成它。
我們已經提到過,我們通常使用 SVG 元素構建 D3 項目。有時,我們可能需要從大型數據集創建復雜的可視化效果,而傳統的 SVG 方法可能會產生性能問題。請務必記住,對于數據可視化中的每個圖形細節,D3 都會將一個或多個 SVG 元素附加到 DOM。一個典型的例子是由數千個節點和鏈接組成的大型網絡可視化。這些可能會讓你的瀏覽器喘不過氣來……盡管瀏覽器可以輕松處理的對象數量隨著性能的提高而不斷增加,但普遍接受的經驗法則是,如果滿足以下條件,我們應該考慮使用畫布而不是 SVG:可視化包含 1000 多個元素。
Canvas 是一種客戶端繪圖 API,它使用腳本(通常是 JavaScript)來創建視覺效果和動畫。它不會將 XML 元素添加到 DOM,這在從大型數據集構建可視化時可以顯著提高性能。
Canvas 還允許您使用 WebGL API 來創建 3D 對象。盡管學習 WebGL 超出了本書的范圍,但為 Web 創建 3D 數據可視化是可能的。目前主要用于實驗項目。在第 15 章中,我們將介紹如何使用畫布構建可視化并討論其優點和缺點。
CSS 代表層疊樣式表,是一種描述 DOM 元素如何在屏幕上顯示及其外觀的語言。從頁面的整體網格布局到文本使用的字體系列,再到散點圖中圓圈的顏色,CSS 可以將普通的 HTML 文件變成令人驚嘆的網頁。在 D3 項目中,我們通常使用內聯樣式或通過外部樣式表應用 CSS 樣式。
內聯樣式應用于具有該style屬性的元素,如以下示例所示。該style屬性可以在傳統的 HTML 或 SVG 元素上使用,D3 有一個方便的方法來設置或修改該屬性,我們將在第 2 章中討論。
<div style="padding:10px; background:#00ced1;"> ... </div>
<text style="font-size:16px; font-family:serif;"> ... </text>
內聯樣式僅影響應用它們的元素。如果我們想要將相同的設計傳播到多個元素,我們需要將相同的style屬性應用于每個元素(或將所有元素包裝在一起的 SVG 組)。它當然有效,但不是最有效的方法。
另一方面,外部 CSS 樣式表非常適合全局應用樣式。一種策略是要求 D3 將相同的類名添加到多個元素。然后,我們使用此類名稱作為外部樣式表中的選擇器,并將相同的樣式屬性應用于目標元素組,如以下示例所示。這種方法效率更高,尤其是在維護大型項目時。它還遵循關注點分離原則,即我們將由 JavaScript 控制的行為與由 CSS 監管的樣式分開。請注意,CSS 預處理器(例如 SASS 和 LESS)是此處描述的外部樣式表方法的一部分。
在 CSS 樣式表中:
.my-class {
font-size: 16px;
font-family: serif;
}
In the DOM:
<text class="my-class"> ... </text>
請記住,內聯樣式優先于從外部樣式表應用的樣式。在任何前端開發項目中,規劃 CSS 樣式的架構并考慮級聯順序非常重要。
D3 是一個 JavaScript 庫。它在 JavaScript 現有核心功能的基礎上添加了新方法。這意味著在使用 D3 時,具有一點 JavaScript 經驗會很有幫助。這也意味著,在構建 D3 項目時,您可以訪問所有現有的 JavaScript 功能。
在本節中,我們將解釋 D3 項目中廣泛使用的兩個 JavaScript 主題:方法鏈和對象操作。
如果您在網絡上搜索 D3 項目的示例,您會發現在同一選擇上會依次調用方法。這種技術就是我們所說的方法鏈,有助于保持代碼簡潔和可讀。
我們可以將方法鏈視為汽車裝配線。假設我們編寫了運行這樣一條裝配線的腳本。正如您在下面的示例中看到的,我們首先聲明一個car創建新Car()對象的變量。然后我們調用函數putOnHood(),將引擎蓋放在汽車頂部,然后我們繼續調用將放置車輪、輪胎和燈的函數。每個連續的調用都會添加一個元素到Car()對象,并且,一旦執行了所有方法,汽車就有了引擎蓋、車輪、輪胎和車燈。每個方法都會將更新后的汽車對象傳遞給下一個方法,從而“鏈接”。請注意,每個調用都用點分隔,并且調用方法的順序很重要。在我們的汽車裝配線示例中,我們需要先安裝車輪,然后才能將輪胎安裝到車輪上。
let car = new Car().putOnHood().putOnWheels().putOnTires().putOnLights();
現在讓我們看看如何在 D3 中使用方法鏈接。想象一下,我們想要從 DOM 中獲取所有 div 并在每個 div 中添加一個段落元素。段落元素應具有類屬性my-class并包含文本“Wow”。然后,我們要在每個段落中插入一個 span 元素,并將文本“Even More Wow”以粗體顯示。如果沒有方法鏈接,我們需要將每個操作存儲到一個常量中,然后在執行下一個操作時調用該常量,如下所示。光是看著就已經很累了……
const mySelection = d3.selectAll("div");
const myParagraphs = mySelection.append("p");
const myParagraphsWithAClass = myParagraphs.attr("class", "my-class");
const myParagraphsWithText = myParagraphsWithAClass.text("Wow");
const mySpans = myParagraphsWithText.append("span");
const mySpansWithText = mySpans.text("Even More Wow")
const myBoldSpans = mySpansWithText.style("font-weight", "900");
由于方法鏈接,相同的示例變得更加簡潔。
d3.selectAll("div").append("p").attr("class", "my-class").text("Wow")
? .append("span").text("Even More Wow").style("font-weight", "900");
在 D3 中,斷行(JavaScript 會忽略這一點)以及縮進鏈接方法是很常見的。這使得代碼更容易閱讀,并且縮進可以幫助我們看到我們正在處理哪個元素。
d3.selectAll("div")
.append("p")
.attr("class", "my-class")
.text("Wow")
.append("span")
.text("Even More Wow")
.style("font-weight", "900");
不要擔心理解前面的代碼示例的作用,盡管您完全可以從不同方法的名稱中猜出它!目前,我們只希望您熟悉如何在 JavaScript 中鏈接方法。我們將在第 2 章中介紹 D3 特定的術語。
D3 都是關于數據的,而數據通常被構造為 JavaScript 對象。了解這些對象的構造以及如何訪問和操作它們包含的數據將為您構建可視化提供巨大幫助。
我們首先討論簡單數組,它是元素列表。在與數據相關的項目中,數組通常是數字或字符串的有序列表。
const arrayOfNumbers = [17, 82, 9, 500, 40];
const arrayOfStrings = ["blue", "red", "yellow", "orange"];
數組中的每個元素都有一個數字位置,稱為索引,數組中第一個元素的索引為 0。
arrayOfNumbers[0] // => 17
arrayOfStrings[2] // => "yellow"
數組具有長度屬性,對于非稀疏數組,該屬性指定它們包含的元素數量。由于數組是零索引的,因此數組中最后一個元素的索引對應于數組長度減一。
arrayOfNumbers.length; // => 5
arrayOfStrings[arrayOfStrings.length - 1] // => "orange"
我們還可以使用方法來確定數組是否包含特定值includes()。true如果數組中的元素之一與作為參數傳遞的值完全對應,則此方法返回。否則,它返回false。
arrayOfNumbers.includes(9) // => true
arrayOfStrings.includes("pink") // => false
arrayOfStrings.includes("ellow") // => false
然而,大多數數據集并不是簡單的數字或字符串列表,它們的每個數據點通常由多個屬性組成。讓我們想象一個虛構機構的員工數據庫,如表 1.1 所示。該表包含四列:每個員工的 ID、姓名和職位,以及該員工是否在 D3 工作。
ID | 姓名 | 位置 | 與_d3一起工作 |
1 | 佐伊 | 數據分析師 | 錯誤的 |
2 | 詹姆士 | 前端開發人員 | 真的 |
3 | 愛麗絲 | 全棧開發人員 | 真的 |
4 | 休伯特 | 設計師 | 錯誤的 |
數據集中的每一行或數據點都可以由 JavaScript 對象表示,如下所示row1。
const row1 = {
id:"1",
name:"Zoe",
position:"Data analyst",
works_with_d3:false
};
我們可以使用點符號輕松訪問對象中每個屬性的值。
row1.name // => "Zoe"
row1.works_with_d3 // => false
我們還可以使用括號表示法訪問這些值。如果屬性名稱包含空格等特殊字符,或者如果我們之前將屬性名稱保存在常量或變量中,那么括號表示法會很方便。
row1["position"] // => "Data analyst"
const myProperty = "works_with_d3";
row1[myProperty] // => false
在現實生活中,數據集通常被格式化為對象數組。例如,如果我們使用 D3 加載表 1.2 中包含的數據集(正如我們將在第 3 章中學習的那樣),我們將獲得以下對象數組,可以將其保存在名為 的常量中data。
const data = [
{id:"1", name:"Zoe", position:"Data analyst", works_with_d3:false},
{id:"2", name:"James", position:"Frontend developer", works_with_d3:true},
{id:"3", name:"Alice", position:"Fullstack developer", works_with_d3:true},
{id:"4", name:"Hubert", position:"Designer", works_with_d3:false}
];
data我們可以使用循環遍歷數組中的每個元素或數據點。更具體地說,JavaScript forEach循環非常方便且易于編寫和閱讀。迭代數據集的一個常見用例是數據整理。當我們加載外部 CSV 文件時,數字通常被格式化為字符串。讓我們以data數組為例,將屬性的值id從字符串轉換為數字。
在下面的示例中,數組迭代器使d我們能夠訪問每個對象。使用點符號,我們id使用運算符將?每個值轉換為數字+。
data.forEach(d => {
d.id = +d.id;
});
JavaScript 提供了許多數組迭代器方法,可以幫助我們與數據交互,甚至在需要時重塑數據。假設我們想要將數據集中的每個員工定位到可視化上。創建一個僅包含員工姓名的簡單數組可能會派上用場,為此我們將使用map()方法。
data.map(d => d.name); // => ["Zoe", "James", "Alice", "Hubert"]
同樣,如果我們只想隔離使用 D3 的員工,我們可以使用這些filter()方法。
data.filter(d => d.works_with_d3);
// => [
{id:2, name:"James", position:"Frontend developer", works_with_d3:true},
{id:4, name:"Hubert", position:"Designer", works_with_d3:true}
];
最后,我們可以通過該方法找到id為3的員工find()。請注意,該find()方法在找到它要查找的值后停止迭代。我們只能在搜索單個數據點時使用此方法。
data.find(d => d.id === 3);
// => {id:"3", name:"Alice", position:"Fullstack developer", works_with_d3:true}
本節討論的方法遠未涵蓋 JavaScript 提供的所有數組和對象操作技術。但在處理數據時,您可能會不斷回想起它們。每當您需要找到另一種方法來訪問或操作數據時,MDN Web 文檔 ( https://developer.mozilla.org/ ) 始終是包含大量示例的可靠參考。
JavaScript 在過去十年中發生了重大變化?,F代 JavaScript 的兩個最重要的趨勢是 Node.js 的興起和 JavaScript 框架作為大多數項目標準的建立。
對于 D3 項目,我們想了解的主要 Node 技術是 NPM,即 Node Package Manager。NPM 允許您安裝“模塊”或小型 JavaScript 代碼庫以在應用程序中使用。您不必包含<script>對單個文件的一堆標記引用,并且如果模塊已構建為不是一個整體結構,則可以減少應用程序中包含的代碼量。
D3.js 版本 7 于 2021 年中期發布,利用了模塊導入的優勢。在本書中,您將看到以兩種方式之一使用 D3 的示例。我們要么加載整個 D3 庫,就像我們在第 2 章中所做的那樣,要么只包含我們需要的 D3 的各個部分,正如您將在后面的示例中看到的那樣。我們可以使用腳本標簽來做到這一點,但從第 2 部分開始,我們將使用 NPM 導入 D3 模塊,因為這被認為是當今的標準做法。如果您交付專業的 D3 項目,您可能需要熟悉它。
如果您已經參與了專業的 Web 項目,那么您也很有可能正在使用 JavaScript 框架,例如 React、Angular、Vue 或 Svelte??蚣転殚_發人員提供了使用模塊化、可重用且可測試的代碼構建 Web 項目的基礎。這些框架負責構建和更新 DOM,這也是 D3 庫所做的事情。在第 8 章中,我們將討論在 JavaScript 框架內構建 D3 可視化時避免沖突的策略。
最后,在專業的工作環境中,您可能會將 D3 與 TypeScript 結合使用。TypeScript 是 JavaScript 的語法超集,為代碼添加了類型安全性。盡管我們不會在本書中詳細討論它,但 D3 方法的類型可以使用 NPM 包 @type/d3 ( https://www.npmjs.com/package/@types/d3 ) 進行安裝。在第 8 章中,我們將在 Angular 項目中使用此類類型。
如果您在網絡上搜索 D3 項目的示例,您無疑會遇到 Observable 筆記本 ( observablehq.com )。Observable 是數據科學和可視化的協作平臺,類似于 Python 項目的 Jupyter 環境。Observable 平臺由 Mike Bostock 創建,取代了之前的在線 D3 沙箱bl.ocks.org 。所有官方的 D3 示例現在都位于 Observable 上,并且 D3 社區在那里非?;钴S。
重要的是要知道 Observable 要求您學習一種處理特定于該平臺的 D3 項目的方法。此外,您不能直接將 Observable Notebook 復制粘貼到前端開發環境中(但有多種方法可以導出和重用它們)。由于本書的重點是在類似于我們如何交付 D3 項目進行生產的環境中構建 D3 可視化,因此我們不會討論 Observable 筆記本。如果您有興趣學習 Observable,您可以在observablehq.com/tutorials找到一系列優秀的教程。您將在本書中學到的大部分技術和概念都可以轉化為 Observable 筆記本。
數據可視化從未像今天這樣流行。豐富的地圖、圖表以及系統和數據集的復雜表示不僅存在于工作場所,而且還存在于我們的娛樂和日常生活中。隨著這種流行,使用視覺手段以及美學規則來表示數據和信息的類和子類庫不斷增長,以促進易讀性和理解性。您的受眾,無論是公眾、學者還是決策者,已經習慣了我們曾經認為極其抽象和復雜的數據趨勢表示。這使得 D3 這樣的庫不僅受到數據科學家的歡迎,而且還受到記者、藝術家、學者、IT 專業人士,甚至數據可視化愛好者的歡迎。
如此豐富的選項似乎讓人不知所措,而且修改數據集以顯示在流圖、樹狀圖或直方圖中相對容易,這往往會促進這樣一種觀念:信息可視化更多地是關于風格而不是實質內容。幸運的是,完善的規則規定了針對不同系統的不同數據類型使用哪些圖表和方法。本書并不旨在涵蓋數據可視化中的所有最佳實踐,但我們將介紹其中的一些。盡管開發人員使用 D3 徹底改變了顏色和布局的使用,但大多數人希望創建支持實際問題的數據的可視化表示。
當您構建第一個可視化項目時,如果有疑問,請簡化 - 通常,呈現直方圖比小提琴圖更好,或者分層網絡布局(如樹狀圖)比力導向的網絡布局更好。視覺上更復雜的數據顯示方法往往會激發更多的興奮,但也會導致觀眾關注圖形的美觀而不是數據。創建酷炫且令人瞠目結舌的可視化并沒有什么錯,但我們永遠不應該忘記任何數據可視化的主要目標都是講述一個故事。詢問周圍的人是否理解你的可視化以及他們如何解釋它是至關重要的一步。他們需要解釋嗎?他們可以從與您的項目的互動中得出哪些結論?故事被講述了嗎?
盡管如此,為了正確部署信息可視化,您應該知道該做什么和不該做什么。您需要對您的數據和受眾有深入的了解。D3 賦予我們巨大的靈活性,但正如俗話所說,“能力越大,責任越大”。雖然知道某些圖表更適合表示特定類型的數據固然很好,但更重要的是要記住,如果不謹慎地從知情的角度構建數據可視化,則可能會攜帶錯誤信息。如果您打算設計自己的可視化,那么了解數據可視化最佳實踐是至關重要的。了解這一點的最佳方法是回顧知名設計師和信息可視化從業者的工作。盡管整個圖書館的作品都在處理這些問題,以下是我們發現的一些有用的內容,可以幫助您了解基礎知識。這些絕不是學習數據可視化的唯一文本,但它們是一個很好的起點。
在閱讀有關數據可視化的內容時要記住的一件事是,文獻通常關注靜態圖表。使用 D3,您將進行交互式動態可視化。一些交互可以使可視化不僅更具可讀性,而且更有吸引力。感覺自己是在探索而不是閱讀的用戶,即使只是將鼠標懸停在事件上幾次或簡單地單擊進行縮放,也可能會發現可視化的內容比閱讀靜態等效內容更引人注目和更令人難忘。但這種增加的復雜性需要了解用戶體驗。我們將在第 7 章中更詳細地討論這一點。
我們的第一章到此結束!盡管我們還沒有使用過 D3,但您現在已經掌握了入門所需的所有知識。當您不確定應該在可視化中使用哪個 SVG 元素或者需要提醒如何使用 JavaScript 操作數據時,請繼續返回本章。從下一章開始,我們將卷起袖子,創建 D3 可視化。
*請認真填寫需求信息,我們會在24小時內與您取得聯系。