本篇博客爲案例1的升級版,加入了座標軸以及動畫。
查看本篇博客代碼前,請先熟悉D3基礎,詳細請查看官網:
話不多說,我們開始吧~
首先我們來看下實現的簡單柱狀圖的樣子:
對柱狀圖進行簡單分析:
1.需要x方向座標軸,y方向座標軸
2.x方向座標軸的標尺由自定義字符串組成
3.柱體由數據驅動顯示,並且有過渡動畫(從無到有)
4.每個柱體上方顯示有對應數字
接下來是整個代碼實現(由於這個案例代碼比較簡單,所以沒有采用功能化函數分塊實現),相關注釋已經附上。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://d3js.org/d3.v5.min.js"></script> <!-- 引入D3 -->
<style>
#app { <!--畫布大小,背景顏色-->
margin: 0 auto;
width: 500px;
height: 400px;
background: #efefef;
}
.bar { <!--柱體 -->
fill: green;
}
</style>
</head>
<body>
<svg id="app"></svg>
<script>
const SVG_HEIGHT = 400; //常量
const SVG_WIDTH = 500; //常量
const MARGIN = { TOP: 30, RIGHT: 30, LEFT: 30, BOTTOM: 30 } //間距
var datalist = [20, 30, 40, 50, 15]; //模擬數據
var container = d3.select("#app");
//序數比例尺 寬度計算
const xScale = d3.scaleBand()
.domain(d3.range(datalist.length)) //[0,1,2,3,4]
.range([0, SVG_WIDTH - MARGIN.LEFT - MARGIN.RIGHT])
.paddingInner(0.1) //每個柱之間的橫向間隙
//線性比例尺 高度計算
const yScale = d3.scaleLinear()
.domain([0, d3.max(datalist)])
.range([SVG_HEIGHT - MARGIN.TOP - MARGIN.BOTTOM, 0])
//水平方向座標軸
var axisBottom = d3.axisBottom(xScale)
// container.append('g').call(axisBottom)
axisBottom(
container.append('g')
.attr('class', 'xaxis')
.attr("transform", `translate(${MARGIN.LEFT},${SVG_HEIGHT - MARGIN.TOP})`)
)
//垂直方向座標軸
var axisLeft = d3.axisLeft(yScale)
axisLeft(
container.append("g")
.attr("transform", `translate(${MARGIN.LEFT},${MARGIN.TOP})`)
)
//更新水平方向tick值
let ticks = ["一年級", "二年級", "三年級", "四年級", "五年級"];
d3.select('.xaxis').selectAll('text').data(ticks)
.text((d) => {
return d;
})
container.selectAll('rect') //尋找rect標籤
.data(datalist) //綁定數據源
.enter()
.append('rect') //添加rect標籤
// .attr('class','bar')
.classed('bar', true) //樣式名
.attr('x', function (d, i) { //x座標
return xScale(i) + MARGIN.LEFT; //根據比例尺計算加上一個常量
})
.attr('y', function (d, i) { //y座標
return SVG_HEIGHT - MARGIN.TOP;
})
.attr('width', function (d, i) { //寬度
return xScale.bandwidth();
})
.attr('height', function (d) { //高度
return 0; //一開始是0
})
//動畫,過渡
.transition()
.duration(1000)
.delay(function (d, i) {
return 500 * i;
})
.attr('y', function (d, i) {
return yScale(d) + MARGIN.TOP;
})
.attr('height', function (d) {
return SVG_HEIGHT - MARGIN.TOP - MARGIN.BOTTOM - yScale(d);
})
//文本
container.append('g').attr('class', 'textGrop') //添加一個分組
container.select('.textGrop')
.selectAll('text') //文本
.data(datalist)
.enter()
.append('text')
.attr("text-anchor", "middle") //居中
.text(function (d, i) {
return d;
})
.attr('x', function (d, i) { //文本x軸 在柱的中間
return xScale(i) + MARGIN.LEFT + xScale.bandwidth() / 2;
})
.attr('y', function (d, i) { //文本y軸
return SVG_HEIGHT - MARGIN.TOP - 10; //-10讓文本在柱體上方10px
})
//動畫過渡
.transition()
.duration(1000)
.delay(function (d, i) {
return 500 * i;
})
.attr('x', function (d, i) {
return xScale(i) + MARGIN.LEFT + xScale.bandwidth() / 2;
})
.attr('y', function (d, i) {
return yScale(d) + MARGIN.TOP - 10;
})
</script>
</body>
</html>
有問題歡迎指出~