數據可視化-d3.js 1-6
1. 環境配置
1.1 配置React + TS
這里使用腳手架配置:
npx create-react-app react-typescript-demo --typescript
2. 使用D3查詢SVG
2.1 d3.select(xxx)
d3.select('#rect1'),查詢ID為'rect1'的元素,#表示后面的字
符串是一個ID。
d3.select(xxx)也可用於查詢類別,如d3.select(“.class1“),但只會返回找到的第一個元素。
2.2 d3.selectAll(xxx)
d3.selectAll('.class1'),查詢所有class是'class1'的元素。
d3.selectAll('rect'),查詢所有標簽是'
rect'的元素(rect為SVG中的矩形標簽)。
2.3 基於層級的查詢
- d3.select('#magingroup rect')
- d3.selectAll('.tick text')
- d3.selectAll("#secondgroup rect")
如: ’#secondgroup rect‘
- 首先會找到id為secondgroup的標簽。
- 進一步找到secondgroup的子標簽中是rect的。
- 仍然是對rect做查詢,只是結果通過父標簽做了篩選!
這種形式的查詢,經常會在配置坐標軸的代碼中使用,請至少熟悉其形式。
3. 使用D3設置SVG中的屬性
3.1 常見屬性
- id,class(特殊屬性,可以使用.attr設置)
- x,y,cx,cy(元的坐標)
- fill,stroke
- height,width,r(圓的半徑)
- transform -> translate,rotate,scale
3.2 element.attr(xxx)
-
設置元素的屬性:element.attr(’attr_name‘,’attr_value‘)
- react1.attr('y', '100');
- d3.select('#rect1').attr('y', '100');
-
獲取元素的屬性:element.attr(’attr_name‘);
-
注意:數值類型的數據仍在HTML中仍有字符串存儲
- 要使用+(strValue)及時轉換,如 let value = +(’233.666‘)
- 活用模板字符串,如
- let width = 666;
- attr(’transform‘,’translate(0, $(width + 100))‘)
- 模板字符串使用’xxx‘ 表示(本質上還是一個字符串)
- ${xxx}可以在字符串中嵌入程序表達式
3.3 設置圖元屬性
-
selection.attr('attrbuteName', 'value')
- 支持直接通過值來設置屬性
- 支持通過函數來設置屬性
-
selection.attr('attrbuteName', (d, i) = > {xxx})
- d為綁定給圖元的數據(即將到來)
- i為圖元的索引,是一個證書,如d3.selectAll('rect')中第幾個矩形
- 函數也可以僅使用d => {xxx},但此時函數體無法使用索引
- 即使不使用的綁定的數據(如沒有綁定數據),如需使用索引,仍需要完整的寫出(d, i) => {xxx}
4.1 屬性繼承
-
DOM
- 父節點的屬性會影響子節點
- 子節點的屬性會相對於父節點
-
活用
節點可以省掉很多冗余代碼! - d3.select(’#maingroup‘)
- .attr(’transform‘,’translate(200, 100)‘)
4. 使用D3添加&刪除SVG元素
4.1 element.append()
-
element.append()
- const myRect = svg.append('rect');
- const myRect = d3.select(’#mainsvg‘).append(’rect‘)
- const myRect = d3.select(’#mainsvg‘).append(’rect‘).attr(’x‘:’100‘)
-
鏈式添加
- const myReact = d3.select('#mainsvg').append('g').attr('id', 'maingroup')
- .append('rect').attr('fill', 'yellow')
鏈式調用:習慣這種編程習慣有助於使用D3和瀏覽D3的代碼
-
element.remove()
- 請小心使用,會移除整個標簽
-
在debug的過程中可以考慮使用’opacity‘屬性hack出移除的效果。
- element.attr('opacity', '0')
5. 比例尺
- 比例尺用於把實際的數據空間映射到屏幕的空間。
- 比例尺非常重要,會經常同時傳給坐標軸與數據!
5.1 Scale-Linear
const xScale = d3.scaleLinear().domain([min_d, max_d]).range([min, max]);
const xScale = d3.scaleLinear().domain([0, d3.max(data, daturm => datum.value)]).range([0, innerWidth]);
d3.max(數局,回調:如果提取數據的值)
d3.max: 求出數據某一屬性的最大值,比如年齡的最大值
5.2 Scale-Band
const yScale = d3.scaleBand().domain(list).range([min, max]).padding(p);
const yScale = d3.scaleBand().domain(data.map(datum => datum.name)).range([0, innerHeight]).paading(0.1)
5.3 Scale是函數
-
通過d3.scaleLinear或d3.scaleBand得到的返回值本質上是函數。
- 給出數據的值(domain)
- 返回映射后的值(range)
- .domain(xxx)和.range(xxx)可以理解為配置這個函數(功能)的過程。
-
比例尺的定義仍使用鏈式調用
const xScale = d3.scaleLinear().domain([0, d3.max(data, datum=>datum.value)]).range([0, innerWidth]);
const mapped = xScale(100);
6. 坐標軸
6.1 引入坐標軸
一個坐標軸為一個group(
),通常我們需要兩個坐標軸
- 坐標軸中包含:
- 一個
用於橫跨坐標軸的覆蓋范圍 - 若干個刻度
- 每個刻度也是一個group
- 每個刻度下屬還會包含一個
和一個 -
用於展示坐標軸的軸線,如左到右或上到下 -
用於展示坐標軸的刻度值,如實數、性名、日期
-
- (可選)一個標簽用於描述坐標軸
- 一個
定義坐標軸(獲得結果是函數)
- const yAxis = d3.axisLeft(yScale);
- const xAxis = d3.axisBottom(xScale);
- axisLeft:左側坐標軸
- axisBottom:底側坐標軸
實際配置坐標軸
- const yAxisGroup = g.append('g').call(yAxis);
- const xAxisGroup = g.append('g').call(xAxis);
實際配置后會發現
任何坐標軸在初始化之后會默認放置在坐標原點,需要進一步的平移。
關於selection.call(xxx)
函數的輸入為另一個函數。
另一個函數以selection的圖元作為輸入。
另一個函數中會根據函數體的內容修改selection對應的圖元。
定義一個空白的
6.2 配置坐標軸
可以對坐標軸的風格進行修改:d3.selectAll('.tick text').attr('font-size', '2em');
.tickSize來設置網格線
坐標軸的標簽加入不在D3接口的負責范圍內:
- 通過對應組.append('text')來人為實現
- (左)縱軸需要.att('transform', 'rotate(-90)') 來旋轉
- 縱軸坐標旋轉后, x / y會顛倒甚至取值范圍相反
- 回憶DOM:父節點的屬性會影響子節點,而坐標軸默認的'fill'屬性是'none',因此請一定手動設置文字顏色.attr('fill', 'black')
6. 繪制BarChart
6.1 定義Margin
-
SVG對於D3.js是一個’畫布‘。
-
SVG范圍外的任何內容屬於畫布之外,瀏覽器將不予顯示。
-
定義Margin
- const margin = {top: 60, right: 30, bottom: 60, left: 200}
-
計算實際操作的inner 長/寬
- const innerWidth = width - margin.left - margin.right;
- const innerHeight = height - margin.top - margin.bottom;
-
在SVG下額外定義一個組作為新的根節點
- const g = svg.append('g').attr('id', 'maingroup').attr('transform', 'translate(${margin.left}, ${margin.top})');
d3繪制的時候會貼近坐標軸繪制
7. Data-Join
本質上是將數據與圖元進行綁定。
使用Data-Join可以省去大量 根據數據設置圖元屬性 的代碼量。
對於 動態變化的數據 提供統一的接口。
以數據為中心的可視化操作
根據數據的每個屬性自動調整綁定圖元的屬性。
不再需要手動添加、'修改'、刪除圖元
會根據Data-Join的綁定自動推斷。
如果圖元的數目不等於數據的條目?
根據數據條目的數量選定相應數據的圖元。
7.1 data()
element.data(data1).attr('width', d => xScale(d.value));
7.2 key
data(data, keyFunction)
- keyFunction的返回值通常是一個字符串(string)
- keyFunction的定義根據數據,比如keyFunction = d => d.name
e.g.,selection.data(data, d => d.name)
e.g.,d3.selectAll('rect').data(data2, d => d.name).attr('width', d=>xScale(d.value))
在綁定數據給圖元時:
- keyFunction為每條輸入綁定的數據執行一次。
- keyFunction為每個包含數據的圖元執行一次。
如果圖元之前沒有綁定過任何數據,則keyFunction會報錯!
- 第一次綁定時根據索引即可
- 實際的可視化任務,圖元都是根據數據的'條'數動態添加(enter)、刪除(exit),只需要在添加時指定好DOM的ID即可。
8. Enter Update Exit
D3.js綁定數據的三個'狀態'
8.1 Enter
數據的條目多於圖元甚至沒有圖元,常用於第一次綁定數據
D3.js會自動'搞清楚'那些數據是新增的,根據新增的數據生成相應的圖元。生成圖元的占位,占位的內容需要編程者自行添加(append)
const p = maingroup.selectAll('.class').data(data).enter().append('').attr(xxx)
enter本質上生成指向父節點的指針,而apprend操作相當於在父節點后添加指針數量的圖元並將其與多出的數據綁定。
8.2 update
圖元和數據條目相同,之前的介紹均為單純的update
const p = maingroup.selectAll('.datacurve').data(data).attr(xxx).attr(xxx)
tip1:
Update作為實際可視化任務最常用的狀態,經常被單獨封裝成一個函數
tip2:updateSelection.merge(enterSelection).attr(xxx).attr(xxx)
將兩個selection合並到一起操作。
enterSelection在與updateSelection merge之前要至少已經調用了append(xxx)語句添加好圖元。
8.3 Exit
數據的條目少於圖元甚至沒有數據,常用於結束可視化
D3.js會自動'搞清楚'那些圖元是不綁定數據的
const p = maingroup.selectAll('.class').data(data).exit().remove()
8.4 Data-Join的簡介形式
.data(xxx).join(xxx)
默認enter和update的執行形式相同。
默認exit是刪除(remove)節點。
默認data-join形式簡介但不靈活。
必須需要設置enter數據的初始圖元屬性,update會每次重新設置初始值,從而導致動畫出現’奇怪‘的效果
仍支持‘定制’
.join(
enter => enter.append("text").attr("fill", "green").text(d => d),
update => (),
exit => ()
)
9. 讓數據動起來
Update經常與D3.js的動畫一起使用。
transition().duration()。
d3.selectAll('rect').data(data2, d => d.name)
.transiton().duration(3000).attr('width', d=> xScale(d.value))。
.duraton(xxx)中為毫秒,即3000表示3秒鍾。
.transition(xxx)經過調用后,后續的鏈式調用會變成數值上的漸變,漸變的時間由.duration(xxx)設定。
插值的方式由.ease(xxx)設定
如,設置數據的更新和無效數據透明,在5秒鍾內平滑完成。
let updateSelection = d3.selectAll(".dataRect").data(data2, (d: any) => {
return d.name;
});
updateSelection.transition().duration(5000).attr("width", (d) => xScale(d.value) || 0);
updateSelection.exit().transition().duration(5000).attr("opacity", 0.3);
10. 數據的讀取
d3.csv('path/to/data/csv').then(data => {xxx})
.csv函數的返回值是一個JS的'Promise'對象,'promise'對象用於執行異步操作
.then(xxx)的參數為一個函數,參數為.csv(xxx)的返回值,實際上在JavaScript異步中是一個resolve。
d3.csv(xxx)會正常向服務器請求數據,在請求並處理好之后,將結果扔給.then(xxx)中的回調函數。 resolve(data)扔給第一個回調函數,reject(error)扔給第二個回調函數。
11. Path
path元素是SVG基本形狀中最強大的一個,它不僅能創建其他基本形狀,還能創建更多其他形狀。你可以使用path元素繪制矩形(直角矩形或者圓角矩形)、圓形、橢圓、折線形、多邊形,以及一些其他的形狀,例如貝塞爾曲線、2次曲線等曲線。
path元素的形狀是通過屬性d來定義的,屬性d的值是一個”命令+參數“的序列(見下面)
path作為SVG提供的標簽之一,是實現眾多可視化方案的基礎
path可以做什么?
- 折線圖
- 地圖
- 主題河流
11.1 Path的屬性
- d。
- fill:填充顏色。
- stroke:描邊顏色。
- stroke-width:描邊寬度。
- transform=“translate(x,y)”:加了描邊后需要平移(x=stroke-width/2, y=stroke-width/2).
11.2 d屬性
-
M = moveto(M X, Y):將畫筆移動到指定的坐標位置。
-
L = lineto(L X, Y):畫直線到指定的坐標位置。
-
H = horizontal lineto(H X):畫水平線到指定的X坐標位置。
-
V = vertical lineto(V Y):畫垂直線到指定的Y坐標位置。
-
C = curveto(C X1, Y1, X2, Y2, ENDX, ENDY):三次貝塞曲線。
-
S = smooth curveto(S X2, Y2, ENDX, ENDY):平滑曲線。
-
Q = quadratic Belzier curve(Q X, Y, ENDX, ENDY):二次貝塞曲線。
-
T = smooth quadratic Belzier curveto(T, ENDX, ENDY): 映射
-
A = elliptical Arc(A RX, RY, XROTATION, FLAG1, FLAG2, X, Y):弧線
-
Z = closepath():關閉路徑
以上所有命令均允許小寫字母。大寫表示絕對定位,小寫表示相對定位。
11.3 Path生成器
- d3.line(xxx).x(xxx).y(xxxx):用於折線圖。
- d3.geoPath().projection():用於地圖。
- d3.area():用於主題河流。
- d3.arc(xxx).innerRadius(xxx).outerRadius(xxx):用於餅圖。
- d3.lineRedial(xxx).angle(xxx).radius(xxx):極坐標系版本的d3.line(xxx)
12. Interaction
12.1 Map
地圖數據的可視化與D3.js的交互
如何使用D3繪制一張地圖。
- Json數據
- TopoJson & GeoJson
D3中如何實現交互
- 什么是事件
- 事件的監聽與處理
D3-Tip
層疊式樣式表
12.1.1 Json
- JavaScript Object Notation(JSON)。
- 從數據格式上(本質上)是JavaScript的對象。保存為文件后是文本。
- 文本與JavaScript的對象可以‘對等轉換’。
- 與CSV不同,讀入后幾乎不存在‘轉換’。
12.1.2 TopoJson & GeoJson
TopoJson
- 本質上是JSON格式
- 對處理了GeoJson數據冗余的特點,節約存儲空間
- 由D3的作者Mike Bostock制定
GeoJson
- 本質上是JSON格式
- 官方:GeoJson is a format for encoding a variety of geographic data structures。
- D3.js的geoPath使用GeoJson格式的地圖數據
12.1.3 Json數據的讀取
Json數據的讀取與CSV數據的讀取非常相似
d3.json('/static/data/countries-110m.json/').then(
function(data){
xxx
}
);
讀取后會直接轉換成JavaScript的對象。
CSV數據讀取后會轉換成一個對象的數組。
注意!轉換后的GeoJson不可直接使用,需要根據比例尺進一步轉換!
D3的geoPath使用GeoJson的格式,因此需要轉換:
// convert topo-json to geo-json
worldMeta = topojson.feature(data, data.objects.countries);
12.1.4 地圖數據的可視化 - geoPath與投影
如何將地形的數據映射到畫布上?
const projection = d3.geoNaturalEarth1();
const pathGenerator = d3.geoPath().projection(projection);
類似“比例尺”,地圖要畫在多大的畫布上?
projection.fitSize([innerWidth, innerHeight], worldMeta);
12.1.5 D3與Web中的事件機制
事件的設置對應於D3中的.on('eventName', callBack)
d3.select('#someprimitive').on('click', myCallBack)
圖元.on(事件類型,觸發動作)
g.selectAll("path")
.data(worldMeta.features)
.enter()
.append("path")
.attr("stroke", "black")
.attr("stroke-width", 1)
.attr("d", (data: any) => geo(data))
.on("mouseover", function (d) {
d3.select(this)
.attr("opacity", 0.5)
.attr("stroke", "white")
.attr("stroke-width", 6);
})
.on("mouseout", function (d) {
d3.select(this)
.attr("opacity", 1)
.attr("stroke", "black")
.attr("stroke-width", 1);
});
12.1.6 標簽 d3.tip
d3之外的庫,d3.tip
12.2 Stacks
一些形狀類型可以被堆疊,將一個形狀與另一個形狀相鄰放置。例如,每月銷售的柱狀圖可以按產品類別分解為多系列的柱狀圖,將柱狀圖垂直疊加。這相當於將柱狀圖根據序數維度(如產品類別)細分,並應用顏色編碼。
堆疊圖標可以同時展示全部值和每種類別。然而,跨類別進行比較通常是比較困難,因為只有堆疊的底層是對齊的。因此,仔細選擇堆疊順序,並考慮流圖。
與餅生成器一樣,堆疊生成器不會直接生成形狀。相反,他計算位置,然后你可以傳遞給區域生成器或直接使用,如位置條。
12.2.1 d3.stack(data, [, arguments...])
根據給出的數據數組生成一個堆疊,返回代表每個序列的數組。任何附加參數都是任意的,它們只是與此對象一起傳播到訪問器。
序列由鍵訪問器決定;返回數組中的每個級數i對應於第i個鍵。每個序列都是一個點數組,其中每個點j對應於輸入數據中的第j個元素。最后將每個點表示為一個數組[y0, y1],其中y0是較低的值(基線),y1是較高的值(背線);y和y的差對應於這個點的計算值。每個系列的鍵可作為系列使用。每個序列的鍵都可以使用series.key,每個索引作為series.index。每個點的輸入數據元素都可以使用point.data。
This might be represented in JavaScript as an array of objects:
var data = [
{month: new Date(2015, 0, 1), apples: 3840, bananas: 1920, cherries: 960, dates: 400},
{month: new Date(2015, 1, 1), apples: 1600, bananas: 1440, cherries: 960, dates: 400},
{month: new Date(2015, 2, 1), apples: 640, bananas: 960, cherries: 640, dates: 400},
{month: new Date(2015, 3, 1), apples: 320, bananas: 480, cherries: 640, dates: 400}];
To produce a stack for this daa:
var stack = d3.stack()
.keys(["apples", "banans", "cherries", "dates"])
.order(d3.stackOrderNone)
.offser(d3.stackOffsetNone)
The resluting array has one element per series. Each series has one point per month, and each point has a lower and upper value defining the baseline and topline:
重新分配數組每個序列有一個元素。每個序列每個月都有一個點,每個點都有一個定義基線和背線的上下限。
[
[[ 0, 3840], [ 0, 1600], [ 0, 640], [ 0, 320]], // apples
[[3840, 5760], [1600, 3040], [ 640, 1600], [ 320, 800]], // bananas
[[5760, 6720], [3040, 4000], [1600, 2240], [ 800, 1440]], // cherries
[[6720, 7120], [4000, 4400], [2240, 2640], [1440, 1840]], // dates]
Each series in then typically passed to an aree generator to render an are chart, or used to construct rectangles for a bar chart.
12.2.2 stack.keys([keys])
如果指定了鍵,則將鍵訪問器設置為指定的函數或數組並返回此堆疊生成器。如果未指定鍵,則返回當前鍵訪問器,默認為空數組。為每個鍵生成一個序列(層)。鍵通常是字符串,但也可以是任意值。序列的鍵與每個數據點一起被傳遞給值訪問器,以計算該點的值。
12.2.3 stack.value([value])
如果指定了值,則將值訪問器設置為指定的函數或數字並返回此堆疊生成器。如果未指定值,返回當前值訪問器,它默認為
function value(d, key){
return d[key];
}
因此,默認情況下,堆棧生成器假定輸入數據是一個對象數組,每個對象用數值暴露命名屬性。
12.2.4 stack.order([order])
如果指定了順序,則將順序訪問器設置為指定的函數或數組並返回當前堆疊生成器。如果沒有指定順序,返回當前的順序訪問器,默認情況下是stackOrderNone;這使用鍵訪問器給出的順序。
如果順序是函數,它將被傳遞生成的序列數組,並且必須返回表示堆疊順序的數值索引數組。例如,默認順序定義為:
function orderNone(series) {
var n = series.length, o = new Array(n);
while (--n >= 0) o[n] = n;
return o;
}
堆疊順序在偏移量之前計算;因此,在計算順序時,所有點的較低值為零。每個序列的索引屬性也只有在計算了順序之后才設置。
12.2.5 stack.offset([offset])
如果指定偏移,則將偏移訪問器設置為指定函數並且返回當前堆疊生成器。如果沒有指定偏移,返回當前偏移訪問器,默認情況下是stackOffsetNone;它使用零基准線。
偏移函數被傳遞序列數組和順序索引數組;然后,它負責更新序列數組中的上、下值。例如,默認偏移量定義為:
function offsetNone(series, order){
if(!((n = seris.length) > 1)) return
for(var i = 1; s0, s1 = series[order[0]], n. m = s1.length; i < n; ++i){
s0 = s1, s1 = series[order[i]]
for(var j = 0; j < m; ++j){
s1[j][i] += s1[j][0] = s0[j][1]
}
}
}
13 Tree & Graph
A ''tree is a 'graph'
-
層級結構的可視化,層級結構:樹、Tree、Hierarchy
- d3.hierarchy
- '直接的'可視化方案
- d3.tree
- 更直觀的可視化方案
- d3.partition & d3.arc for 'd' of
- d3.partition & d3.arc for 'd' of
-
網絡結構的可視化
- 網絡結構:圖、Graph、Network
- D3.js: Force Simulation
13.1 層級數據可視化
13.1.1 層級數據?
- 數據格式仍為Json
- 節點可以包含'屬性'
13.1.2 D3.js的層級數據預處理
- d3.hierarchy
- 保持數據的原始結構,並將輸入層級數據轉換成D3中的hierarcy對象(result instanceof d3.hierarchy),同時引入
- height( * 不是逐層遞減)
- depth
- children(原始結構)
- parent
- data 原始數據的映射
- d3.hierarchy可作為一個‘中間結果’,繼續輸入到更多D3.js提供的數據預處理接口中。
如何將處理好的數據‘進一步’預處理?
d3.tree().size([innerHeight, innerWidth])
返回一個函數,函數接受的參數為d3.hierarchy,函數會根據設置的size將數性結構的每個節點映射到空間中‘合適’的位置。
13.1.3 得到處理后的數據做Path的'd'屬性
d3.linkHorizontal(xxx)
- d3.linkHorizontal().x(d => d.y).y(d => d.x)
- .x(xxx).y(xxx)分別表示如何在source與target中取橫縱坐標值
g.selectAll('path')
.data(root.links())
.join("path")
.attr("fill", "none")
.attr("stroke", "black")
.attr("stroke-width", 1.5)
.attr("d", d3.linkHorizontal().x(d=>d.y).y(d=>d.x));
roo.links()
返回樹形結構中存在的所有‘鏈接’,鏈接(們)以如下形式給出:
source R1 {data: {xxx}, height: 4, depth: 0, paren: null, children: Array(7), xxx}
targe R1 {data: {xxx}, height: 2, depth: 1, paren: R1, children: Array(7), xxx}
14 Force
let nodes = [{}, {}, {}, {}, {}]
let simulation = d3.forceSimulation(nodes)
定義后會發生:
補全nodes中每個節點的數據結構,包括index,x,y,vx,vy,后兩者為速度。
開始模擬粒子運動,粒子質量為1,不斷地通過內部timer觸發'tick'事件。
根據一系列的‘力’來計算每個例子的加速度、速度、位置。
'力'的數據來源: http://networkrepository.com/socfb-Caltech36.php
14.1 Force 的'force'
14.1.1 d3.forceManyBody
粒子之間兩兩的作用力,strength為正互相吸引,為負則相互排斥。
14.1.2 d3.forceCenter
指向某一個中心的力,會盡可能讓粒子向中心靠近或重合。
14.1.3 d3.forceLink
粒子之間兩兩的作用力?讓互相之間有鏈接的節點保持在某一個特定的距離,是否有鏈接需要通過圖的邊集合給出。
simulation = d3.forceSimulation(nodes)
.force('manyBody', d3.forceManyBody().strength(-30))
.force('center', d3.forceCenter(width / 2, height / 2))
.force('link', d3.forceLink(links).strength(0.1),distance(100))
14.1.4 Force: 時鍾滴tic答toc
forceSimulation會通過每次'tick'來更新當前節點的狀態,狀態包括位置、速度、加速度等。
更新后的狀態僅僅為'狀態',不會反映到任何圖元,僅僅對數據進行修改
人為設置每次tick要如何更新圖元,simulation.on('tick', ticked);
在初始化每個圖元后,只要為simulation配置了'tick'的回調,simulation會自動開始模擬。注意:simulation.stop()會停止timer的tick循環。
function ticked(){
lines
.attr('x1', d=> d.source.x)
.attr('y1', d=> d.source.y)
.attr('x2', d=> d.target.x)
.attr('y2', d=> d.target.y);
circles
.attr('cx', d=> d.x)
.attr('cy', d=> d.y)
}
15 Color-Gradient
使用顏色的"梯度"設置圖元的填充,"fill"屬性不是單純可以使用"顏色"進行填充。
Linear: 線性"梯度"。
Radial: 從中心向外擴散的"梯度"。
設置圖元的梯度填充:
- 對填充進行定義並賦予索引。
- 使用索引設置圖元的fill屬性。
<defs>
<linearGradient id="linearGrad">
<stop offset="0%" stop-color="#8dd3c7" stop-opacity="1"></stop>
<stop offset="100%" stop-color="#ffffb3" stop-opacity="1"></stop>
</linearGradient>
</defs>
<svg width="200" height="200">
<circle cx="100" cy="100" r="50" fill="url(#linearGrad)"/>
</svg>
16 添加一副圖片
添加一個純粹的圖片HTML元素:
svg.append("image").attr("id", "myimg")
.attr("x", 600).attr("y", 350)
.attr("width", 300).attr("height", 300)
.attr("preserveAspectRatio", "none")
.attr("href", "ff7.jpg")
- 一張圖片自身以link的形式給出。
- '圖片'(image)本身也是一個HTML元素。
- href表示圖片的路徑。
- preserveAspectRatio表示圖片的'對齊'與'縮放'。
16.1 perserveAspectRatio
-
此屬性表示圖片的'對齊'和'縮放'。
-
圖片下方表示這個屬性的可能取值。
-
取值不限於下方的示例。
-
meet和slice本質上是對viewbox這個屬性進行修改。
-
none
-
xMidYMid slice
-
xMidYMid meet
-
xMidYmin slice
-
xMaxYMax meet
17 Pattern
width, height:在重復下一個圖案之前應該跨過多遠。
x,y:Pattern的偏移。
patternUnit: Pattern'使用'的坐標系。
預定義的圖形、圖像通過子節點的方式(如d3.append)給出
<defs>
<pattern id="eofxu" patternUnits="userSpaceOnUse" width="20" height="20">
<circle cx="10" cy="10" r="2" fill="green" stroke="green" stroke-width="0"></circle>
</pattern>
</defs>
<circle class="mark" cx="400" cy="50" r="100" style="stroke: black; fill: url("#eofxu");"></circle>
18 Stack + Path(可以) = 主題河流
關鍵接口:
- d3.stack().offset(d3.stackOffsetWiggle)
- d3.area():設置path的d屬性
- clipPath: 動效
const area = d3.area()
.curve(d3.curveCardinal.tension(0.3))
.x(d => xScale(xValue(d.data)))
.y0(d => yScale(d[0]))
.y1(d => yScale(d[1]));
18.1 同一坐標軸的不同尺度
yScale = d3.scaleLinear()
.domain([up_max, 0, -low_max])
.range([innerHeight, innerHeight / 2, 0]])
.nice();
19 D3中的數字格式化與坐標軸的格式化
d3.format(specifier),specifier遵循下述規則
[[fill]align][sign][symbol][0][width][,][.precision][~][type]
典型的formater,'.'后的數字表示精度:
- d3.format('.2f')(666.666) 小數點后保留兩位,666.67
- d3.format('.2r')(2467) 只保留兩位有效數字 2500
- d3,format('.3s')(2366.666) 只保留三位有效數字且加以后綴 2.37K
.tickFormat(d3.format(xxx)): 根據formater來設置坐標軸上的數字格式
時間的格式化
d3.timeFormat(xxx),如 d3.timeFormat('%b-%b')
20 輔助交互的接口
20.1 Drag
21 異步機制
async:將函數轉換為異步函數,即把返回值包裝成一個Promise對象。
await:強制等待異步函數執行結束。
let newdata = await d3.csv("static/data/2019.json"。
注意await 關鍵字只能async中使用。
使用異步機制等待transition的結束
why? -> 多個transition對於同一個圖元的轉換不會自動同步。
transition.end()可以返回一個Promise對象。
使用await關鍵字可以對這個promise進行等待。
21.1 transition.tween
transition.attr v.s selection.attr。
selection.attr 設置/獲取圖元的屬性
transition.attr 使用默認的插值器在兩個給定的屬性之間進行過度(tween),例如從紅色漸變到藍色、從左邊移動到右邊 .....
,tween:介詞,之間,補間的含義。
transition.tween 使用自定義的插值器進行過度
'自定義的插值器':返回函數的函數
- 插值器本身的參數:類似於.attr中的數據'd'與索引'l'
- 返回函數的參數:從0到1的、根據時間變化的數字t
22 d3.js的局限性
D3盡管作為目前Web端最強大的可視化框架之一
三維數據的可視化。
確實存在第三方的庫可以解決: d3-3d
游戲引擎(unity, unreal) or three.js
使用three.js可視化三維場景並自由漫游