文章目錄
1. 再談“數據綁定”
1.1 選擇元素
D3中,選擇元素的函數有兩個select
和selectAll
,兩種方法都很常用,select返回匹配選擇器的第一個元素,而selectAll返回匹配選擇器的所有元素。
選擇所需元素的方法:
d3.select("body"); //選擇body元素
d3.select("#tp1"); //選擇id爲tp1的元素
d3.select(".tp2"); //選擇類爲tp2的元素
當然,也可以對選擇集中的元素再進行選擇:
d3.select("body")
.selectAll("p");
1.2 添加、插入和刪除
d3.select
和 d3.selectAll
所返回的對象稱爲選擇集 selection。
利用 selection.attr(name,[]) 來設置或獲取選擇集的屬性。
利用 selection.text([]) 設定或獲取選擇集的文本內容。
添加元素的方法爲 selection.append(name) ,在選擇集的末尾添加一個元素,name爲元素的名稱。
插入元素的方法爲 selection.insert(name, [before]) ,name是插入元素的名稱,before爲CSS選擇器的名稱,將元素插入到指定元素之前。
刪除選擇集中的元素 selection.remove() 。
1.3 數據綁定
對於d3.select
和 d3.selectAll
返回的選擇集是沒有數據的。數據綁定的過程就是讓這些被選擇的元素中 “含有” 數據。
selection.datum(value)
選擇集中的每個元素都綁定相同的數據value。
selection.data(values, [key])
選擇集中的每一個元素分別綁定數組values中的每一項,key爲鍵值函數,用於指定綁定時的對應規則。
在data綁定之後,有三種情況,分別按駐足長度和元素數量的關係如下:
- update 數組長度 = 元素數量
- enter 數組元素 > 元素數量
- exit 數組元素 < 元素數量
通常比較常用的方法是利用selectAll選取一個空集,隨後再對enter() 部分添加元素append,即
d3.selectAll("p")
.data(d)
.enter()
.append("p")
鍵函數key:
只有在選擇集原來已經有數據的情況下,使用鍵函數纔有效果!
比如有如下內容:
<body>
<p></p>
<p></p>
<p></p>
<script src="https://d3js.org/d3.v5.js"></script>
<script>
let Data = [{name: "p1", data: 12}, {name: "p2", data: 17}, {name: "p3", data: 14}];
let p = d3.select("body").selectAll("p")
.data(Data)
.text(function (d) {
return d.name + ':' + d.data;
});
</script>
</body>
此時p中數據顯示爲:
隨後對數據進行更新:
let DataEntry = [{name: "p5", data: 17}, {name: "p4", data: 14}, {name: "p6", data: 12}];
p.data(DataEntry, d => d.data)
.text(function (d) {
return d.name + ':' + d.data;
});
可以看到此時p中的元素變爲:
可以看到data作爲鍵值,並沒有按照DataEntry中的順序進行依次綁定,而是保持原本順序不變,使鍵值對應的數據作出更新。可以利用這一性質在製作動態圖時更新數據。
2. 過渡效果
2.1 過渡啓動
d3.transition()
創建一個過渡效果。對於每個選擇集都可以使用**transition()**方法創建過渡。
transition.delay([value])
設定延遲時間。過渡經過一段時間後才發生,單位是毫秒。
transition.duration([value])
設定過渡的持續時間。單位爲毫秒。
默認情況下,如果不設置時間的話delay爲0ms,duration爲250ms。
transition.ease(value,[])
設定過渡樣式、在目標處彈跳幾次等方式。
2.2 過渡屬性
過渡前後的狀態不同,因此需要指定過渡前後元素的不同屬性。
transition.attr(name,value)
屬性name過渡到目標值value,value可以是一個函數。
例如如下代碼:
.attr("r", function (d) {
//...
})
.attr("cx", function (d) {
//...
})
.attr("cy", function (d) {
//...
})
.transition()
.delay(3000)
.duration(2000)
.attr("r", function (d) {
//...
})
.attr("fill", function (d) {
//...
});
transition.attrTween(name, tween())
將屬性 name 使用差值函數 tween() 。
例如:
svg.transition()
.duration(2000)
.attrTween("width", function (d,i,a) {
return function (t) {
return parseInt(a + t * 300);
}
})
d是被綁定的數據,i是索引號,a是width的屬性初始值。
3. 定時器
setInterval(code, millisec)
以指定的週期來執行代碼,直到 clearInterval()
被調用或者窗口被關閉。
例如:
// 調用更新函數,每0.7s更新一次
setInterval(function () {
update((++idx) % 12);
}, 700);
刪除方法:
let id = setInterval(function () {
update((++idx) % 12);
}, 700);
//...
clearInterval(id);
4. 簡單動畫製作
4.1 動態繪製circle
效果圖(含實時更新的情況):
let circles = svg.selectAll("circle")
.data(DataLine)
.enter()
.append("circle")
.attr("r", function (d) {
return 1;
})
.attr("cx", function (d) {
return w - inner.right - (d.longitude * (-1) - 60) / 70 * W;
})
.attr("cy", function (d) {
return h - inner.bottom - (d.latitude - 25) / 30 * H;
});
circles.transition()
.delay(3000)
.duration(2000)
.attr("r", function (d) {
return d.PRCP * 5;
})
.attr("fill", function (d) {
return 'rgba(0,' + Math.max(0, (175 - parseInt(d.WSF5))) + ',' + (255) + ',0.8)';
});
setInterval(function () {
add1day(); //更新idx的函數
update(idx);
}, 2000);
function update(t) {
comment.text(idx);
updateElement();
}
function updateElement() {
svg.selectAll("circle")
.data(DataEntry[sce], d => d.station)
.transition()
.duration(2000)
.attr("r", function (d) {
return d.PRCP * 5;
})
.attr("fill", function (d) {
return 'rgba(0,' + Math.max(0, (175 - parseInt(d.WSF5))) + ',' + (255) + ',0.8)';
});
}
4.2 動態繪製rect
動態條形圖代碼:
let rects = groups.data(DataEntry)
.enter().append("rect")
.attr("x", inner.left)
.attr("height", H * 0.65)
.attr("fill", function (d, i) {
return Color[i];
})
.attr("width", function (d) {
return xScale(d.sunshine);
});
// 設定更新函數
function UpdateElement() {
rects.data(DataEntry, d => d.city)
.transition()
.duration(600)
.attr("y", function (d, i) {
return i * H + inner.bottom;
})
.attr("width", function (d) {
return xScale(d.sunshine);
});
}
UpdateElement();
// 更新函數
function update(t) {
//...
comment.text(DataEntry[0].month);
UpdateElement();
}
// 調用更新函數,每0.7s更新一次
setInterval(function () {
update((++idx) % 12);
}, 700);