最近項目組加班比較嚴重,D3的博客就一拖再拖,今天終於不用加班了,趕忙抽點時間寫完~~html
今天就將D3數據的更新及動畫寫一寫~~git
接着以前的博客寫~~github
以前寫了一個散點圖的例子,下面能夠本身寫一個柱狀圖的例子。數組
我就直接給代碼了,和散點圖差很少~~app
var margin = {top: 20, right: 20, bottom: 30, left: 40}, width = 960 - margin.left - margin.right, height = 500 - margin.top - margin.bottom; var dataset = [ 11, 12, 15, 20, 18, 17, 16, 18, 23, 25, 8, 10, 13, 19, 21, 25, 22, 18, 15, 13]; // 使用了d3.scale.ordinal() 它支持範圍分檔。與定量比例尺(如d3.scale.linear())返回連續的範圍值不一樣,序數比例尺使用的是離散範圍值,也就是輸出值是事先就肯定好的。 // 映射範圍時,可使用range(),也可使用rangeBands()。後者接收一個最小值和一個最大值,而後根據輸入值域的長度自動將其切分紅相等的塊或「檔」。0.2也就是檔間距爲每一檔寬度的20%。 var x = d3.scale.ordinal() .domain(d3.range(dataset.length)) .rangeBands([0, width], 0.2); var y = d3.scale.linear() .domain([0, d3.max(dataset, function(d) { return d; })]) .range([height, 0]); var xAxis = d3.svg.axis() .scale(x) .orient("bottom"); var yAxis = d3.svg.axis() .scale(y) .orient("left"); var svg = d3.select("body").append("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) .append("g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); svg.selectAll("rect")// 插入的不是circle了,改成rect .data(dataset) .enter() .append("rect") .attr("x", function(d,i) { return x(i); }) .attr("y", function(d) { return y(d); }) .attr("width", x.rangeBand()) .attr("height", function(d) { return height - y(d); }) .attr("fill", function(d){ return "rgb(60, 127, " + d * 10 + ")";// 根據值的大小獲取顏色 }); svg.append("g") .attr("class", "x axis") .attr("transform", "translate(0," + height + ")") .call(xAxis) .append("text") .attr("class", "label") .attr("x", width) .attr("y", -6) .style("text-anchor", "end") .text("X軸"); svg.append("g") .attr("class", "y axis") .call(yAxis) .append("text") .attr("class", "label") .attr("transform", "rotate(-90)") .attr("y", 6) .attr("dy", ".71em") .style("text-anchor", "end") .text("Y軸");
其效果以下dom
座標軸有些粗,是由於CSS沒設置,若是設置上在以前博客裏的CSS樣式,會好看一點~~svg
先添加一個事件來觸發數據的變化,在html中body標籤裏添加一個button函數
<button>Update</button>
爲button綁定事件,並在事件中添加數據的更新,代碼以下佈局
// 單擊的時候,更新數據 d3.select("button").on("click", function() { // 新數據集 dataset = [ 21, 22, 25, 10, 18, 17, 6, 8, 13, 15, 15, 20, 23, 19, 11,15, 25, 8, 25, 23 ]; // 更新全部矩形 svg.selectAll("rect") .data(dataset) .attr("y", function(d) { return y(d); }) .attr("height", function(d) { return height - y(d); }); });
運行代碼,點擊update按鈕,chart發生變化,以下動畫
若是你足夠細心的話,就會發現,顏色跟原來的同樣,沒有根據長度發生變化,只要把原來針對fill 編寫的代碼複製到事件的代碼裏。
svg.selectAll("rect") .data(dataset) .attr("y", function(d) { return y(d); }) .attr("height", function(d) { return height - y(d); }) .attr("fill", function(d){ return "rgb(60, 127, " + d * 10 + ")";// 根據值的大小獲取顏色 });
是否是顏色也跟着變化了。
你是否是以爲,就這麼變化有些太坑了,那咱們就給它加個動畫,過渡一下就OK了,其實現只需簡單的一行代碼 .transition()
注:在方法鏈上,要把這個調用插到選擇元素以後,改變任何屬性以前
svg.selectAll("rect") .data(dataset) .transition() // <-- 這是新代碼,其餘都保持不變。 .attr("y", function(d) { return y(d); }) .attr("height", function(d) { return height - y(d); }) .attr("fill", function(d){ return "rgb(60, 127, " + d * 10 + ")";// 根據值的大小獲取顏色 });
是否是有了動畫,就不會以爲變化有些忽然了~~
以後,我以爲想控制一下動畫的時間,要讓他快一點或者慢一點,其實現也只須要一行代碼 .duration(2000)
svg.selectAll("rect") .data(dataset) .transition() .duration(2000) // <-- 這是新代碼,其餘都保持不變。2000 毫秒,即2 秒 .attr("y", function(d) { return y(d); }) .attr("height", function(d) { return height - y(d); }) .attr("fill", function(d){ return "rgb(60, 127, " + d * 10 + ")";// 根據值的大小獲取顏色 });
你延長了一下動畫時間,發現它的動畫一開始很是慢,而後逐漸加速,最後在達到預約高度以前速度再次慢了下來。換句話說,動畫的速度不是線性不變的,而是有加減速變化的。
若是,我想要均勻的變化怎麼辦?
在D3 中,可使用ease() 指定不一樣的緩動類型。默認的緩動效果是"cubic-inout",產生的就是咱們剛剛看到的那種逐漸加速而後再逐漸減速的效果。
因此咱們只須要設置一下ease("linear")就能夠了。咱們要在transition() 以後、attr() 以前指定ease()。事實上,ease()在duration() 以前以後都沒問題,但先過渡再設置緩動彷佛更瓜熟蒂落。
... // 選擇元素的代碼 .transition() .duration(2000) .ease("linear") ... //attr() 的代碼
linear是線性緩動,就是沒有逐漸加速和減速的變化,全部元素都按照一個速度變化,變化到最終值時戛然而止。
除此以外,還有不少緩動函數可供選擇。下面只是幾個,不是所有
• circle
逐漸進入並加速,而後忽然中止。
• elastic
描述這個效果的一個最恰當的詞是「有彈性」。
• bounce
像皮球落地同樣反覆彈跳,慢慢停下來。
詳細能夠查看:https://github.com/mbostock/d3/wiki/Transitions#wiki-d3_ease
咱們再來想一下,若是這個動畫,我不想讓它一開始就跑起來,我想讓它晚點跑起來,好比說2秒後。
那咱們只須要在添加一個方法.delay(2000),就OK了
... .transition() .delay(2000) //2000 毫秒,即2 秒 .duration(2000) //2000 毫秒,即2 秒 ...
與使用duration() 和ease() 同樣,把delay() 放到哪裏並無十分嚴格的限制,但我更喜歡把它放在duration() 前面。由於是先設定延遲時間,而後過渡動畫纔開始計時,這樣比較符合邏輯。
上面的代碼是靜態延時,靜態延遲時間只是一種延遲方式,更有意思的是能夠動態計算延遲時間。動態延遲的一個常見用途就是建立交錯延遲的效果,讓某些過渡在另外一個過渡以前發生。交錯延遲對人的感知有利,由於當相鄰元素的變化不那麼同步時,人眼更容易注意到每一個元素的變化。要設置動態延遲,就別給delay() 傳遞靜態值,而是給它傳入一個函數,按照D3 的慣例……對,就是傳入一個匿名函數。
... .transition() .delay(function(d, i) { return i * 100; }) .duration(500) ...
在匿名函數中,與當前元素綁定的數據值是以d 傳入的,而這個元素的位置是以i傳入的。所以,這裏的意思是讓D3 循環遍歷每一個元素,把它們的動畫延遲時間設定爲i * 100,也就是後一個元素的動畫開始時間總比前一個元素晚100 毫秒。
到這裏,也許你會提出一個疑問,若是變化的時候數據超出了原來的範圍,怎麼辦?
那咱們就須要更新比例尺了,更新比例尺的代碼很簡單。
// 更新比例尺的值域 y.domain([0, d3.max(dataset)]);
今天就先到這裏,寫博客時間長了,以爲有點腰疼。
等有時間再繼續更新吧~~
繼續寫這篇博客,就不另起一篇了~~
迄今爲止,只要更新數據,咱們採用的都是「整批整包」的方式:改變數據集數組中 的值,而後從新綁定修改後的值,覆蓋原始值對DOM 元素的綁定。這種方式很是適合全部值都會改變,並且數據集長度(即數據值的數量)不變的情形。但是咱們知道,現實中的數據可沒那麼簡單。這就對代碼的靈活 性提出了更高要求,好比只更新一兩個值,或者支持增長值和減小值。
首先咱們須要在數據中插入一個值,並更新一下數軸的值域。
dataset.push(10);
x.domain(d3.range(dataset.length));
而後選出以前的元素,而後插入,插入的代碼跟剛開始的代碼很像。
// 加入…… var bars = svg.selectAll("rect") .data(dataset);// 選擇出以前的元素 bars.data(dataset) .enter() .append("rect") .attr("x", x(dataset.length - 1))//這行代碼設定了新條形的水平位置,讓它剛好位於SVG 區域的最右邊。 .attr("y", function(d) { return y(d); }) .attr("width", x.rangeBand()) .attr("height", function(d) { return height - y(d); }) .attr("fill", function(d){ return "rgb(60, 127, " + d * 10 + ")"; });
最後,再更新全部的數據就OK了
bars.transition() .duration(500) .attr("x", function(d, i) { return x(i); }) .attr("y", function(d) { return y(d); }) .attr("width", x.rangeBand()) .attr("height", function(d) { return height - y(d); }) .attr("fill", function(d){ return "rgb(60, 127, " + d * 10 + ")"; });
這樣,動態的添加一條數據就實現了,而後你會發現數據的座標軸的刻度沒有變,那就須要再去更新一下座標軸的刻度。在更新值域的代碼後面,添加以下代碼
xAxis.scale(x); svg.select("g.x.axis") .call(xAxis);
你會發現座標軸的刻度也跟着變化了~~
把每次單擊時添值到數據集,改成使用shift() 方法從數組中刪除第一個元素。
// 從數據集中刪除一個值 dataset.shift();
而後取得退出元素集,而後把它們過渡到右邊,最後,刪除它們
// 退出…… bars.exit() .transition() .duration(500) .attr("x", w) .remove();
remove() 是一特殊的過渡方法,它會在過渡完成後從DOM 中永遠地刪除元素。移除以後座標軸的更新和以前的添加元素相同。
在把數據綁定到DOM 元素時(即調用data() 時),就會發生數據聯結。默認的聯結是按照索引順序,即第一個值綁定到元素集中第一個DOM 元素,第二個值綁定到元素集中第二個DOM 元素,依此類推。若是數據值與DOM 元素的順序不同呢?那就得告訴D3 怎麼實現值和元素間的聯結或配對。好在,經過定義鍵函數(key:function),能夠指定相應的規則。
先準備數據,以前,咱們的數據集就是包含簡單數值的數組。而爲了使用鍵函數,每一個值必須有對應的鍵。
var dataset = [
{ key: 0, value: 5 }, { key: 1, value: 10 }, { key: 2, value: 13 }, { key: 3, value: 19 }, { key: 4, value: 21 }, { key: 5, value: 25 }, { key: 6, value: 22 }, { key: 7, value: 18 }, { key: 8, value: 15 }, { key: 9, value: 13 }, { key: 10, value: 11 }, { key: 11, value: 12 }, { key: 12, value: 15 }, { key: 13, value: 20 }, { key: 14, value: 18 }, { key: 15, value: 17 }, { key: 16, value: 16 }, { key: 17, value: 18 }, { key: 18, value: 23 }, { key: 19, value: 25 }
];
更新引用,許多之前用d的地方要修改成d.value,如
var y = d3.scale.linear() .domain([0, d3.max(dataset, function(d) { return d.value;})]) .range([0, height]);
連接鍵函數,在data一個數據集時,使用以下代碼,就OK了。
.data(dataset, function(d) { return d.key; })
關於數據的更新和動畫就先到這裏爲止了,以後會寫D3的交互和佈局~~