當前內容所在位置
- 第一部分 D3.js 基礎知識
- 第一章 D3.js 簡介
- 1.1 何為 D3.js?
- 1.2 D3 生態系統——入門須知
- 1.2.1 HTML 與 DOM
- 1.2.2 SVG - 可縮放矢量圖形 ??
- 第一部分
- 第二部分
- 【第三部分】??
- 1.2.3 Canvas 與 WebGL(精譯中 ?)
- 1.2.4 CSS(待翻譯)
- 1.2.5 JavaScript(待翻譯)
- 1.2.6 Node 與 JavaScript 框架(待翻譯)
- 1.2.7 Observable 記事本(待翻譯)
譯注
(上接 1.2.2 小節(二))
…… 關于 SVG 描邊(strokes)的位置當對齊可視化項目中的圖形時,需要特別注意:SVG 圖形繪制出的描邊是在內外邊界上平均展布的。如下圖所示,已知一個
width
屬性為40px
的矩形,令stroke-width
的值為1
,則在視覺效果上會在矩形的左右兩邊各增加寬度為0.5px
的描邊(而不是下意識地以為的那樣在各邊均增加1px
),最終實際的總寬度為41px
;若令stroke-width
的值為2
,則左右兩邊各增加1px
,以此類推。
描邊寬度
stroke-width
對 SVG 圖形實際寬度的影響
(……關于 SVG 描邊(strokes)的位置)
5 圓與橢圓
在數據可視化中常常會用到圓形。它們天然吸引眼球,使可視化效果看起來更友好、更有趣。SVG 的圓是通過 <circle />
元素繪制的,其必選屬性包括圓心位置(cx
, cy
)與半徑(r
),如圖 1.17 所示。圓的半徑是從圓心到圓周上任意一點繪制的線的長度。將以下圓形添加到示例中,令圓心位于 (530, 80)
處,且半徑為 50px
:
<circle cx="530" cy="80" r="50" />
圖 1.17 在 SVG 容器的坐標系中定位圓和橢圓并調整其大小
還可以為圓設置填充(fill
)與描邊(stroke
)屬性(attribute)。要生成圖 1.17 中的圓,令填充色為透明(transparent
)、描邊寬度為 3px
、描邊顏色為 #81c21c
(譯注:這里應該參考的是最終效果圖 1.8,圖 1.17 只是示意圖)。
同理,<ellipse />
元素必須指定圓心坐標(cx
, cy
),但不像圓有固定半徑,橢圓的半徑會變化,使其呈扁平狀——這是通過聲明橢圓的水平半徑(rx
)和垂直半徑(ry
)來實現的。將如下代碼片段添加到示例中,將在圓的下方繪制一個橢圓,其水平半徑為 50px
、垂直半徑為 30px
:
<ellipse cx="530" cy="205" rx="50" ry="30" />
6 路徑
SVG 的路徑(path)元素是目前為止所有 SVG 元素中最靈活的一種。在 D3 中,路徑被廣泛用于繪制幾乎所有的復雜形狀和曲線。這些形狀和曲線是無法用目前討論過的圖形基元(線、矩形、圓和橢圓)來表示的。
路徑元素通過聲明 d
屬性(這里的“d”代表“draw”,即“繪制”)來指示瀏覽器進行繪制。d
屬性包含一系列命令:從開始繪制路徑的位置,到使用的一系列曲線類型,一直到確定該路徑是否為一個封閉圖形為止。例如,在圖形畫廊示例中添加以下路徑元素:
<path d="M680 150 C 710 80, 725 80, 755 150 S 810 220, 840 150" fill="none" stroke="#773b9a" stroke-width="3" />
如圖 1.18 所示, d
屬性以 M680 150
開頭,表示“移動(M
)到坐標(680,150)處”。接著從起點坐標(680 150
)到 d
屬性最后一個坐標(840 150
)所指定的終點,繪制一條三階貝塞爾曲線(cubic Bézier curve)。三階貝塞爾曲線還需要設置控制點來定義曲線的陡峭程度和彎曲方向。這些控制點從字母 C
(這里的“C”代表“cubic curve”,即“三次方曲線”)之后開始,直到字母 S
(這里的“S”代表“stop”,即“停止”)之后結束。
圖 1.18 一條簡單的 SVG 路徑及其控制點
注意
如需深入了解 SVG 路徑,請參閱 MDN 的教程:https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths。
手動設置 d
屬性僅適用于簡單的圖形,隨著圖形復雜度增加,純手動編寫將會變得非常繁瑣。所幸 D3 提供了強大的圖形生成工具來專門處理 d
屬性的計算。本書第 4 章將深入討論。
路徑還有一個需要牢記的重要知識點:無論路徑元素是否閉合,只要其 fill
屬性值不為 none
或 transparent
,瀏覽器就會默認填充為黑色,如本例所示。
7 文本
內聯 SVG 圖形的最大優勢之一在于支持屏幕閱讀器訪問其包含的文本內容。這對于可訪問性而言是一大優勢。由于數據可視化通常包含多個標簽,因此有必要了解如何使用 <text>
元素來操作 SVG 文本。通過往示例中添加標簽可以了解 SVG 文本的基本工作原理。
目前為止我們討論的 SVG 圖形都使用了自閉合標簽(如 <line />
、<rect />
、<path />
……)。在使用 SVG 的 <text>
元素時,需要同時使用開標簽和閉標簽,并將待顯示文本放置在這兩個標簽之間。例如,添加一個內容為“line”的文本元素:
<text>line</text>
保存并重新加載頁面。您可能以為文本會出現在 SVG 容器的左上角,但卻看不到……這是為什么呢?默認情況下,SVG 文本的位置是參照其基線計算得來的,通過 dominant-baseline
屬性控制。如果文本基線的坐標是 (0, 0)
,如圖 1.19 所示,實際文本最終將位于 SVG 容器之外。由于任何位于 SVG 容器之外的元素都是不可見的,所以看不到文本。
圖 1.19 在 SVG 容器外定位文本
在使用 SVG 文本元素時,另一個需要考慮的問題是文本如何流動(flow)。常規 HTML 元素在頁面上的位置是按照控制內容流動的特定規則來確定的。如果在頁面中插入大量 <div>
元素,它們會自然地堆疊在一起,其內容也會回流(reflow),不會超出容器的范圍;而 SVG 文本根本不會流動,開發人員必須單獨設置每個文本元素的 x
和 y
屬性。例如通過它們將文本放在坐標點 (60, 260)
處,標簽“line”才會出現在圖形畫廊示例中 SVG 直線段的下方:
<text x="60" y="260">line</text>
作為練習,再創建一個新的文本元素,并將標簽“rect”置于矩形和正方形的下方。
至此,我們通過 x
和 y
屬性聲明了文本元素 左下角 的位置。怎樣設置文本中點位置呢?通過屬性 text-anchor
來實現:令其值為 middle
即可,如圖 1.20 所示。再比如,利用該屬性還可以將圓形的文本標簽居中。
<text x="530" y="155" text-anchor="middle">circle</text>
圖 1.20
text-anchor
屬性對 SVG 文本對齊方式的影響。默認值為 start;根據其中間位置對齊,值為 middle;設置末尾對齊,值為 end
最后,分別為橢圓和路徑元素各添加一個文本標簽。SVG 文本默認為黑色,可以通過 fill
屬性(attribute)進行更改。
8 分組元素
本小節要討論的最后一個 SVG 元素為分組元素(group element)。分組元素(即 <g>
元素)與之前討論過的其他 SVG 元素有所不同。它既沒有視覺上的圖形表示,也不存在具有邊界的空間;相反,它是 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 容器中任意移動這個組及其包含的所有元素,始終保持正方形與文本標簽之間的對齊方式不變。
使用 transform
屬性可以在 SVG 容器中平移該分組。transform
屬性比之前學過的其他屬性略顯復雜,但與 CSS 中 transform
屬性的用法相同。它接受一個或一系列變換(transformation,如平移、旋轉、縮放等)作為屬性值。平移一個分組使用 translate(x, y)
變換。如果要將 <rect>
和 <text>
元素移回原位,需要令分組元素右移 260px
并下移 175px
,即令 <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>
元素的另一個特點,是其子元素可以繼承它的樣式屬性(styling attributes)。下面舉例說明。由于前面“rect”文本標簽已經和正方形歸為同一組,故先將其余文本元簽統一放到另一個 <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>
如果令 <g>
的 fill
屬性值為 #636466
,則里面的每個 <text>
元素都將繼承相同的顏色;同理,如果添加的是 font-family
或 font-size
屬性,同一組內的文本元素也將繼承這些樣式屬性(properties)。
<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(即 Don’t Repeat Yourself,不要重復自己)原則。需要更新屬性時,在分組容器上操作也會很簡單。
恭喜您完成了本書的第一個練習!您可以在代碼清單 1.2 及源碼文件的 end 文件夾找到 SVG 圖形畫廊的完整代碼。在構建首個 D3 項目時,該練習可以作為參考。
代碼清單 1.2 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 圖形
現在該您小試牛刀了——創建如下圖所示的 SVG 圖形。您可以在源碼文件夾
02_SVG_exercise/start
中進行操作。具體要求如下:
- 創建一個響應式 SVG 容器,寬高均為
400px
(屏幕上留足空間)。- 繪制一個寬高均為
200px
的正方形。將其置于 SVG 容器的中心位置,并指定透明色填充及5px
的黑色描邊。- 在 SVG 容器的中心添加一個半徑為
100px
的圓。將其fill
屬性設置為 CSS 顏色名稱“plum”。- 繪制兩條描邊為
5px
的黑色對角線:一條從正方形的左上角畫到右下角;另一條從正方形的右上角畫到左下角。- 在正方形上方添加文本“SVG is awesome!”字樣,并將其置于 SVG 容器的中心。其他文本樣式:字號
18px
、無襯線字體。
強烈建議通過練習該 SVG 圖形來強化本小節介紹的知識點該練手項目的完整參考代碼,請參閱 附錄 D 的
D.1.1
小節或隨書源碼文件中的02_SVG_exercise/end
文件夾中。建議盡量獨立完成。
(SVG 基礎知識譯完了,內容有點多,再自己做個小結吧)
本節 SVG 基礎知識要點梳理
- 圓(
circle
)與橢圓(ellipse
):都具有圓心坐標(cx
/cy
)和半徑,只是橢圓的半徑有兩個(rx
與ry
),圓只有一個(r
);圓其實是橢圓的 特殊情況; - 路徑(
path
)是 SVG 圖形中最復雜的幾何基元,其屬性d
包含一系列命令,可借助 D3 的生成工具代替純手工賦值; - 文本(
text
)——- 在 SVG 中不隨常規 HTML 內容流動;
- 其位置是基于文本基線計算得來的,需要手動設置(
x
與y
); - 文本對齊通過
text-anchor
屬性調整,可選值:start
|middle
|end
;
- 分組(
g
)元素——- 沒有視覺圖形表示,也不占據空間;
- 放入同一分組的元素可整體移動;
- 設置在
g
上的樣式可被子元素繼承:如fill
、font-size
、font-family
等;