學習文獻:http://pkuwwt.github.io/d3-tutorial-cn/adding-elements.html (基礎教學)
https://juejin.im/post/5a81a8946fb9a06334266f05 (講解插值)
https://github.com/pshrmn/notes/blob/master/d3/interpolators.md (解釋插值)
今天要學習總結下d3畫餅圖曹傀,先看效果圖
上面是餅圖帶點擊動畫的捺萌,然后中間的數字可以變化毒返。這里面主要用到d3來實現效果铺遂。后面我會貼出我全部的代碼
思路流程
創(chuàng)建SVG--->添加g標簽元素--->數據轉換得到我們需要的數據--->添加path開啟動畫和畫圓--->點擊圓讓其變大--->數字慢慢增長
創(chuàng)建SVG
var svg = d3.select('#d3Circle')
.append("svg")
.attr("width", sellCircleSVGWid)
.attr("height", sellCircleSVGhei);
.select表示要選擇的函數,append創(chuàng)建SVG书劝,attr用來設置屬性值塑径,我這里設置了寬度和高度。我們創(chuàng)建的SVG是一個矢量圖的格式可無限放大而不失真尘盼。
添加g標簽
var arcs = svg.selectAll("g")
.data(piedata)//設置數據集,piedata元素集合
.enter()
.append("g")//添加g元素
.attr("transform", "translate(" + sellCircleSVGWid / 2 + "," + sellCircleSVGhei / 2 + ")");
我通過SVG再創(chuàng)建一個g標簽烦绳,并且讓它移動到圓的中心位置
selectAll表示選中所有的g標簽元素卿捎,但是我們在此之前并沒有創(chuàng)建g標簽元素,所以通過selectAll會返回一個空的集合給我径密。data用于設置數據源午阵,piedata集合里面有多少數據,就會創(chuàng)建多少個g標簽元素給我享扔。enter()表示創(chuàng)建底桂,append表示添加。
注意>迕摺籽懦!! piedata 必須是集合參數(我自己總結出來的,我試過幾次了氛魁,不是集合就不會畫圖出來)
我們最好是把g標簽創(chuàng)建出來暮顺,因為g是用來組合對象的容器。添加到g元素上的變換會應用到其所有的子元素上秀存。添加到g元素的屬性會被其所有的子元素繼承捶码。此外,g元素也可以用來定義復雜的對象应又。
數據轉換得到我們需要的數據
//創(chuàng)建圓弧生成器
var arcPath = d3.svg.arc()
.innerRadius(innerRaidus)
.outerRadius(outerRadius);
//創(chuàng)建圓弧生成器
var arcPath2 = d3.svg.arc()
.innerRadius(innerRaidus)
.outerRadius(outerRadius + 5);
var color = ['#c56b4f', '#e8b63e', '#435fd8'];
var ptDetail = ["456", "123", "256"];//數據源
var totalSales = "835";
//這里數據過濾宙项,拿到需要的數據
var pie = d3.layout.pie().value(function (d) {
return d - 0;//將字符串轉為數字
});
var dataset = [];//數據集合
for (var k in ptDetail) {
dataset.push(ptDetail[k] / totalSales);//算出占用量
}
dataset.sort(function (a, b) {//從大到小排序
return a < b ? 1 : -1
});
var piedata = pie(dataset);//拿到需要的數據
//先定義位置
var sum = 0;
piedata.forEach(function (d, i) {//定義自己的顏色乏苦,執(zhí)行實時間等
d.color = color[i];
d.position = i;
d.duration = 2000 * (d.data / d3.sum(dataset));//動畫時長2秒株扛,計算每一個弧形所用動畫時間
d.delaytime = sum;
sum += d.duration;
});
var arcPath = d3.svg.arc().innerRadius(innerRaidus).outerRadius(outerRadius); 創(chuàng)建一個弧生成器,我們把數據傳遞給它汇荐,像這樣arcPath(data)洞就,它就會返回一堆路徑數據給你,這堆數據用于path上面畫圓掀淘。
d3.layout.pie().value 主要用于數據過濾旬蟋,然后內部計算得到開始角度,結束角度
這里面的data 是原始的數據革娄,startAngle是開始弧度倾贰,endAngle是結束弧度冕碟。dataset是未進行過濾的數據源
添加path開啟動畫和畫圓
arcs.append("path")
.transition()
.delay(function (d, i) {
return d.delaytime;
})
.duration(function (d, i) {
return d.duration;
})
.attrTween("d", function (d, j) {//過度器
var i = d3.interpolate(d.startAngle, d.endAngle);
return function (t) {
d.endAngle = i(t);
return arcPath(d);
}
})
.attr("stroke-width", "2px")
.attr("fill", function (d, i) {
return d.color;
});
transition()用于開啟動畫的標志,delay()多少時間后開始執(zhí)行匆浙,duration()多少時間內完成安寺,attrTween()過渡器,這個比較難立即了吧首尼,舉一個簡單的例子說明
//下面我用書上的例子來展示
var svg = d3.select('#d3Circle')
.append("svg")
.attr("width", 200)
.attr("height", 180);
//定義SVG的寬高
var svg = d3.select('#d3Circle')
.append("svg")
.attr("width", sellCircleSVGWid)
.attr("height", sellCircleSVGhei);
var rect = svg.append("rect")
.attr("fill", "steelblue")
.attr("x", 10)
.attr("y", 10)
.attr("width", 100)
.attr("height", 30);
var rectTran = rect.transition()
.duration(2000)
.attrTween("width", function (d, i, a) {
return function (t) {
return Number(a) + t * 300;
}
});
所以我們可以理解為挑庶,數字從0到1,慢慢變化的過度软能。繼續(xù)上面的講解
var i = d3.interpolate(d.startAngle, d.endAngle);在d3里面迎捺,interpolate解釋為插值器的意思,我上面貼的鏈接也有對這個進行解釋查排,然后我對它的理解如下
t的取值為[0,1]凳枝,我們i就是作為一個函數了,如果我們duration時間很長那么通過i(t)得到的數據也會很多跋核,所以我們過度動畫就從0讀到endAngle變化了范舀。arcPath(d)拿到數據函數將其轉換為路徑值,然后賦值給d參數了罪。
點擊圓讓其變大
var isClick = false
arcs.on("click", function (d) {//點擊
d3.select(this).select("path").transition().attr("d", function (d) {
if (!isClick) {//放大效果
isClick = true;
return arcPath2(d);
} else {//還原
isClick = false;
return arcPath(d);
}
});
});
由于我上面寫了2個arcPath锭环,一個用于點擊后放大的效果的。
數字慢慢增長
function CusnumDd(el, param) {
var sum = 0;
var time = setInterval(function () {
sum += 1;
el.innerHTML = sum;
if (param <= sum) {
clearInterval(time);
el.innerHTML = (param);
}
}, 100)
}
全部代碼
<script type="text/javascript">
$(function () {
var sellCircleSVGWid = 200;//寬
var sellCircleSVGhei = 180;//高
var innerRaidus = 60;
var outerRadius = 80;
//定義SVG的寬高
var svg = d3.select('#d3Circle')
.append("svg")
.attr("width", sellCircleSVGWid)
.attr("height", sellCircleSVGhei);
/* var rect = svg.append("rect")
.attr("fill", "steelblue")
.attr("x", 10)
.attr("y", 10)
.attr("width", 100)
.attr("height", 30);
var rectTran = rect.transition()
.duration(2000)
.attrTween("width", function (d, i, a) {
return function (t) {
return Number(a) + t * 300;
}
});*/
//畫圓形圖開始
var arcPath = d3.svg.arc()
.innerRadius(innerRaidus)
.outerRadius(outerRadius);
//畫圓形圖開始
var arcPath2 = d3.svg.arc()
.innerRadius(innerRaidus)
.outerRadius(outerRadius + 5);
var color = ['#c56b4f', '#e8b63e', '#435fd8'];
var ptDetail = ["456", "123", "256"];
var totalSales = "835";
var pie = d3.layout.pie().value(function (d) {
return d - 0;
});
var dataset = [];//數據集合
for (var k in ptDetail) {
dataset.push(ptDetail[k] / totalSales);
}
dataset.sort(function (a, b) {
return a < b ? 1 : -1
});//從大到小排序
var piedata = pie(dataset);//元素集合
//先定義位置
var sum = 0;
piedata.forEach(function (d, i) {
d.color = color[i];
d.position = i;
d.duration = 2000 * (d.data / d3.sum(dataset));//動畫時長2秒泊藕,計算每一個弧形所用動畫時間
d.delaytime = sum;
sum += d.duration;
});
var arcs = svg.selectAll("g")
.data(piedata)
.enter()
.append("g")
.attr("transform", "translate(" + sellCircleSVGWid / 2 + "," + sellCircleSVGhei / 2 + ")");
arcs.append("path")
.transition()
.delay(function (d, i) {
return d.delaytime;
})
.duration(function (d, i) {
return d.duration;
})
.attrTween("d", function (d, j) {
var i = d3.interpolate(d.startAngle, d.endAngle);
return function (t) {
d.endAngle = i(t);
return arcPath(d);
}
})
.attr("stroke-width", "2px")
.attr("fill", function (d, i) {
return d.color;
});
var isClick = false
arcs.on("click", function (d) {//點擊
d3.select(this).select("path").transition().attr("d", function (d) {
if (!isClick) {
isClick = true;
return arcPath2(d);
} else {
isClick = false;
return arcPath(d);
}
});
});
var s = document.getElementById("number");
del_ff(s); //清理空格
var el = s.firstChild; //獲得s的第一個子節(jié)點
CusnumDd(el, 8);
var a = document.getElementById("dom");
del_ff(a);
console.log('獲取a的全部子節(jié)點:')
console.log(a.childNodes);//獲取a的全部子節(jié)點辅辩;
console.log('獲取a的父節(jié)點:')
console.log(a.parentNode); //獲取a的父節(jié)點;
console.log('獲取a的下一個兄弟節(jié)點:')
console.log(a.nextSibling);//獲取a的下一個兄弟節(jié)點
console.log('獲取a的上一個兄弟節(jié)點:')
console.log(a.previousSibling);//獲取a的上一個兄弟節(jié)點
console.log('獲取a的第一個子節(jié)點:')
console.log(a.firstChild);//獲取a的第一個子節(jié)點
console.log('獲取a的最后一個子節(jié)點:')
console.log(a.lastChild);//獲取a的最后一個子節(jié)點*/
});
function del_ff(elem) {
var elem_child = elem.childNodes;
for (var i = 0; i < elem_child.length;) {
if (elem_child[i].nodeName == "#text" && !/\s/.test(elem_child.nodeValue)) {
elem.removeChild(elem_child[i])
}
else {
i++;
}
}
}
/**
* 功能: 數字滾動顯示
* @param el 選擇對象
* @param param 數字參數
*/
function CusnumDd(el, param) {
var sum = 0;
var time = setInterval(function () {
sum += 1;
el.innerHTML = sum;
if (param <= sum) {
clearInterval(time);
el.innerHTML = (param);
}
}, 100)
}
</script>
html
<div id="container">
<div class="circle" id="d3Circle">
<div id="number">
<span></span>
</div>
</div>
<div id="dom">
<div></div>
<div></div>
<div></div>
</div>
</div>
css
<style type="text/css">
body, html, #allmap {
width: 100%;
height: 100%;
overflow: hidden;
margin: 0;
font-family: "微軟雅黑";
}
#container {
width: 100%;
height: 500px;
display: flex;
display: -webkit-flex;
flex-direction: column;
align-items: center;
}
.circle {
width: 100%;
height: 200px;
display: flex;
display: -webkit-flex;
flex-direction: column;
align-items: center;
justify-content: center;
position: relative;
}
#number {
color: black;
width: 60px;
height: 60px;
line-height: 60px;
text-align: center;
position: absolute;
}
</style>