如果有需求必須要在Canvas
上繪圖時會用上。
因為其實d3
確實沒提供直接繪圖於Canvas
的方法,但其實與Svg
不同也只是最後繪製方法,Svg
是透過一堆DOM
元素透過設定屬性,形成的圖表。Canvas
則是使用Context
透過API
進行繪圖。
所以目前的方式,d3
處理資料與模型同步的事,以及相對位置以及Scale
的計算。Canvas
負責繪圖。
const base = d3.select("#vis");
const chart = base.append("canvas")
.attr("width", 400)
.attr("height", 300);
const context = chart.node().getContext("2d");
const data = [1,2,13,20,23];
const scale = d3.scaleLinear()
.range([10, 390])
.domain([1,23]);
data.forEach(function(d, i) {
context.beginPath();
context.arc(scale(d), 75, 5, 0, 2 * Math.PI);
context.fillStyle="red";
context.fill();
context.closePath();
});
下半部就是繪圖重點,其實也只是去遍歷資料,並使用Canvas
的API
進行繪圖,
複雜範例的作法其實是在原本d3
內產出的元件使用custom
自訂元件,但仍然有對應資料的屬性。
當產玩這種假的元件之後,再讀出所有假元件的屬性,並繪製在Canvas
上。
const base = d3.select("#vis");
const chart = base.append("canvas")
.attr("width", 400)
.attr("height", 300);
const context = chart.node().getContext("2d");
const data = [1,2,13,20,23];
const detachedContainer = document.createElement("custom");
const dataContainer = d3.select(detachedContainer);
drawCustom([1,2,13,20,23]);
function drawCustom(data) {
const scale = d3.scaleLinear()
.range([10, 390])
.domain([1,23]);
// 這邊都使用假原件來模擬真實元件產生對應的屬性。
const dataBinding = dataContainer.selectAll("custom.rect")
.data(data, function(d) { return d; });
dataBinding
.attr("size", 15)
.attr("fillStyle", "green");
// 加入假元件
dataBinding.enter()
.append("custom")
.attr('class', 'rect')
.attr("x", scale)
.attr("y", 100)
.attr("size", 8)
.attr("fillStyle", "red");
// 移除假元件
dataBinding.exit()
.attr("size", 5)
.attr("fillStyle", "lightgrey");
// 呼叫Canvas繪圖方法
drawCanvas();
}
function drawCanvas() {
// clear canvas
context.fillStyle = "#fff";
context.rect(0,0,chart.attr("width"),chart.attr("height"));
context.fill();
// 撈出所有假元件,並且在對假元件們進行遍歷,之後繪製出`Canvas`畫面。
const elements = dataContainer.selectAll("custom.rect");
elements.each(function(d) {
const node = d3.select(this);
context.beginPath();
context.fillStyle = node.attr("fillStyle");
context.rect(node.attr("x"), node.attr("y"), node.attr("size"), node.attr("size"));
context.fill();
context.closePath();
});
}
其實都可以使用假元件並自己實作繪圖部分,讓d3
的資料同步與計算部分,在各種繪圖框架上使用。