数据来自csv文件。
绘制效果图:
图片一共由几个部分构成:
- 横纵两个坐标轴,横轴为月份编号,纵轴是光照时间。
- 中间主体部分是折线,由svg的Path绘制,其中数据坐标点为svg的circle元素。
- 右上部分是图例,代表不同颜色代表的城市信息,城市纬度。颜色深浅程度代表纬度的高低。
// 构造比例尺
xScale = d3.scaleLinear()
.domain([1, 12])
.range([0, w - inner.left - inner.right])
.nice();
xAxis = d3.axisBottom(xScale);
yScale = d3.scaleLinear()
.domain([0, 400])
.range([h - inner.top - inner.bottom, 0])
.nice();
yAxis = d3.axisLeft(yScale);
绘制横轴和纵轴的构造
// 解析csv数据
d3.csv("sunshine.csv").then(function (d) {
console.log(d);
Data = d;
draw();
});
解析csv数据,js的代码不是顺序执行的,而是异步的。在加载完csv文件后才去执行后面的内容,将解析后的数据放到d中作为参数来传递,此时d为数组元素。draw为后续的绘制操作。
完整代码:
<script src="https://d3js.org/d3.v5.js"></script>
<script>
let w = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
let h = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
w *= 0.98;
h *= 0.75;
let inner = {top: 50, right: 100, bottom: 50, left: 100};
let xScale, yScale;
let xAxis, yAxis;
let Data = new Array();
let svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h)
.append("g");
// 构造比例尺
xScale = d3.scaleLinear()
.domain([1, 12])
.range([0, w - inner.left - inner.right])
.nice();
xAxis = d3.axisBottom(xScale);
yScale = d3.scaleLinear()
.domain([0, 400])
.range([h - inner.top - inner.bottom, 0])
.nice();
yAxis = d3.axisLeft(yScale);
// 解析csv数据
d3.csv("sunshine.csv").then(function (d) {
console.log(d);
Data = d;
draw();
});
function draw() {
svg.append("g")
.attr("transform", "translate(" + inner.left + "," + (h - inner.bottom) + ")")
.call(xAxis);
svg.append("g")
.attr("transform", "translate(" + inner.left + "," + inner.top + ")")
.call(yAxis);
let Color = new Array();
let H = h - inner.top - inner.bottom;
let LineData = new Array();
let City = new Array();
let ct = -1, p = 0;
// 画线函数
let Line = d3.line()
.x(function (d) {
return d.x;
})
.y(function (d) {
return d.y;
});
// 解析数据,获取城市名称,生成颜色,构造折线数据等
for (let i = 0; i < Data.length; ++i) {
if (i === 0 || Data[i].city !== Data[i - 1].city) {
++ct;
City[ct] = Data[i].city;
Color[ct] = 'rgba(' + (((ct + 1) / 6) * 128) + ',' + (((ct + 1) / 6) * 255) + ',' + 255 + ',0.8)';
p = 0;
LineData[ct] = new Array();
}
LineData[ct][p] = ({
"x": (inner.left + ((w - inner.left - inner.right) / 11) * Data[i].monthnum),
"y": (-inner.bottom + h - H * (Data[i].sunshine) / 400)
});
++p;
}
// console.log(Color);
for (let i = 0; i <= ct; ++i) {
// 绘制路径
svg.append("path")
.attr("d", Line(LineData[i]))
.attr("stroke", function () {
return Color[i];
})
.attr("stroke-width", function () {
return 3.5;
})
.attr("fill", "none");
// 用圆圈标记每个点的位置
svg.append("g")
.selectAll("circle")
.data(LineData[i])
.enter()
.append("circle")
.attr("fill", function (d) {
return Color[i];
})
.attr("cx", function (d) {
return d.x;
})
.attr("cy", function (d) {
return d.y;
})
.attr("r", 3);
}
// 画线段函数
function drawLine(x1, y1, x2, y2, l = 1, c = "black") {
svg.append("line")
.attr("x1", x1)
.attr("y1", y1)
.attr("x2", x2)
.attr("y2", y2)
.attr("stroke-width", l)
.attr("stroke", c);
}
// 添加文本的函数
function drawText(x, y, s, l = '12px', c = 'black') {
svg.append('text')
.attr('x', x)
.attr('y', y)
.style('fill', c)
.style('font-size', l)
.text(s);
}
drawText(w - inner.right * 0.9, h - inner.bottom * 0.9, 'monthnum');
drawText(inner.left * 0.75, 0.6 * inner.bottom, 'sunshine');
// 右上角添加图例以及文字说明
for (let i = 0; i <= 5; ++i) {
drawLine(w - inner.right - 100, inner.bottom + i * 15,
w - inner.right - 50, inner.bottom + i * 15, 2, Color[i]);
let str;
if (i === 0) str = 47.61;
if (i === 1) str = 41.88;
if (i === 2) str = 40.73;
if (i === 3) str = 37.73;
if (i === 4) str = 29.74;
if (i === 5) str = 25.76;
drawText(w - inner.right - 40, inner.bottom + i * 15, City[i] + " / " + str);
}
drawText(w - inner.right - 75, 0.5 * inner.bottom, 'City/Latitude', '15px');
}
</script>