在之前的折線制作過程中,我們選擇使用的平滑曲線,這一節我們學習一下帶錨點的折線圖配合區域圖的制作方法。
準備的數據
const line0_data = [
{
type: "歷史平均",
value: [
['2016/07', 1.4],
['2016/08', 1.5],
['2016/09', 1.8],
['2016/10', 1.9],
['2016/11', 1.6],
['2016/12', 2.5],
['2017/01', 2.1],
['2017/02', 2.6],
['2017/03', 2.4],
['2017/04', 2.7]
]
},
{
type: "行業平均",
value: [
['2016/07', 1],
['2016/08', 1.3],
['2016/09', 1.2],
['2016/10', 1.7],
['2016/11', 1.4],
['2016/12', 2.2],
['2017/01', 2.1],
['2017/02', 2.4],
['2017/03', 2.0],
['2017/04', 1.8]
]
},
{
type: "當前商品",
value: [
['2016/07', 1.2],
['2016/08', 1.4],
['2016/09', 1.8],
['2016/10', 1.9],
['2016/11', 1.8],
['2016/12', 2.4],
['2017/01', 2.1],
['2017/02', 2.1],
['2017/03', 2.0],
['2017/04', 1.4]
]
}
]
繪制折線圖及圖例
上一節這部分已經詳細介紹過,這里我們直接上代碼,有問題的同學可以參考深入淺出數據可視化之道(5)
js部分
const data = line0_data;
var initWidth = 340
var initHeight = 500
var padding = { left:40, top:20, right:20, bottom: 40}
var height = initWidth - padding.top - padding.bottom
var width = initHeight - padding.left - padding.right
var svg = d3.select("body")
.append("svg")
.attr("id", "chart")
.attr("width", width)
.attr("height", height)
.style("padding-left", padding.left)
.style("padding-right", padding.right)
.style("padding-top", padding.top)
.style("padding-bottom", padding.bottom)
//添加y軸坐標軸
//y軸比例尺
let nums = [...data[0]["value"], ...data[1]["value"]].map(function(e){
return e[1]
})
let yScale = d3.scaleLinear()
.domain([0, d3.max(nums)*1.5])
.range([height , 0]);
let _yScale = d3.scaleLinear()
.domain([0, d3.max(nums)*1.5])
.range([0, height]);
//定義y軸
let yAxis = d3.axisLeft(yScale).ticks(6).tickSize(0.5);
//添加y軸
svg.append("g")
.attr("class","axis")
.attr("transform","translate(" + 0 + "," + 0 + ")")
.call(yAxis);
//添加x軸坐標軸
//x軸比例尺
let years = data[0]["value"].map(function(e){
return e[0]
})
const step = width / years.length
let xScale = d3.scaleBand()
.domain(years)
.rangeRound([0, width])
let _xScale = d3.scaleBand()
.domain([0, width])
.rangeRound(years)
//定義x軸
let xAxis = d3.axisBottom(xScale).ticks(0)
//添加x軸
svg.append("g")
.attr("class","axis-x")
.attr("transform","translate(" + "0 ," + height + ")")
.call(xAxis);
//添加
// gridlines in y axis function
function make_y_gridlines() {
return d3.axisLeft(yScale)
.ticks(6)
}
// add the Y gridlines
var grid = svg.append("g")
.attr("id", "grid")
.call(make_y_gridlines()
.tickSize(-width)
.tickFormat("")
)
//--------------以下是繪制圖形-------------
//創建一個直線生成器
var linePath = d3.line()
.x( function(d){ return xScale(d[0]) + step/2 })
.y( function(d){ return yScale(d[1])})
var colors = ["rgb(0, 188, 212)", "rgb(255, 64, 129)", '#955694']
//添加路徑
svg.append("g").selectAll("path")
.data(data)
.enter()
.append("path")
.attr("transform","translate(0, 0)")
.attr("d", function(d){
return linePath(d.value)
})
.attr("fill", "none")
.attr("stroke-width", "2px")
.attr("stroke", function(d, i){
return colors[i]
})
// 添加節點
var circles = svg.append("g")
.selectAll("g")
.data(data)
.enter()
.append("g")
.attr("class", function(d,i){return d.type})
circles.selectAll("circle")
.data(function(d){
return d.value
})
.enter()
.append("circle")
.attr("cx", function(d){
return xScale(d[0]) + step/2
})
.attr("cy", function(d){
return height - _yScale(d[1])
})
.attr("r", 4)
.attr("fill", function(d, i){
var type = d3.select(d3.select(this)._groups[0][0].parentNode).attr("class")
var ii = data.findIndex((val, index) =>{
return val.type == type
}
)
return colors[ii]
})
var cover =svg.append("g")
cover.selectAll("rect")
.data(data)
.enter()
.append("rect")
.attr("width", 10)
.attr("height", 10)
.attr("fill", function(d, i){
return colors[i]
})
.attr("transform", function(d, i){
return `translate(10, ${(i)*20})`
})
cover.selectAll("text")
.data(data)
.enter()
.append("text")
.text(function(d, i){
return d.type
})
.attr("transform", function(d, i){
return `translate(27, ${(i)*20})`
})
.attr("font-size", '12px')
.attr("dy",function(){
return '0.75em'
})
.attr("fill", function(){
return '#333'
})
// 偏移文字
d3.selectAll('.axis-x .tick text')
.attr("dy",'1em')
.attr('dx', '-2em')
style部分
body{
font-family: "helvetica";
background-color: #fff;
margin:0;
padding:100px
}
svg {
box-sizing: content-box
}
.axis path {
display: none;
}
.axis .tick line{
opacity: 0
}
.axis-x path {
stroke: #aaa;
stroke-width: 1
}
.axis-x .tick line{
stroke: #aaa;
stroke-width: 1
}
.axis-x .tick text{
transform: rotate(-30deg);
}
#grid line {
stroke: #aaa;
stroke-width: 1
}
#grid .tick:nth-child(2) {
display: none
}
#grid path {
display: none
}
.line_y .domain{
stroke: yellow;
stroke-width: 2
}
實現的效果
添加錨點的折線圖
生成區域
這里我們將學習一個新的生成器,區域生成器
首先我們先了解一下基本概念:
區域生成器主要是生成一塊區域,類似于直線生成器。
其數據訪問器有x().x0().x1().y().y0().y1()這幾項,數量比較多但是不需要同時都使用
一個簡單的區域圖
const data = [80, 90, 120, 110, 180, 220]
var areaPath = d3.area()
.x(function(d,i){return 20+i*70})
.y0(function(d, i){return height})
.y1(function(d,i){return height - d})
var area= svg.append("path")
.attr("d", areaPath(data) )
.attr('fill', "rgba(0,0,255,0.4)")
生成的效果
區域圖
結合圖示:
參數示意圖
這里我們添加的是x軸方向的區域圖,因此x只需取一個值即可。如圖示,y0代表區域圖節點底部坐標,y1代表區域圖節點頂部坐標。區域圖通過一個個類似‘節點線’的東西構成了一個完整的面積圖。
給折線圖添加區域圖
//生成河流的數據
const max_min = [
['2016/07', 0.4, 2.0],
['2016/08', 1.0, 1.8],
['2016/09', 1.1, 2.2],
['2016/11', 1.2, 2.4],
['2016/12', 2.0, 2.7],
['2017/01', 1.5, 2.5],
['2017/02', 1.9, 3.1],
['2017/03', 1.5, 3.0],
['2017/04', 1.0, 3.2]
]
// 添加區域圖
var areaPath = d3.area()
.x(function(d){ return xScale(d[0]) + step/2 })
.y0(function(d){ return yScale(d[2]) })
.y1(function(d){ return yScale(d[1]) })
var river = svg.append("path")
.attr("d", areaPath(max_min) )
.attr('fill', "rgba(0,0,255,0.2)")
最終生成的效果
折線圖與區域圖結合顯示價格波動區間