一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第22天,点击查看活动详情。
我正在参加 码上掘金体验活动,详情:show出你的创意代码块
前言
昨天简单的写了canva与svg demo来认识前端数据可视化,但是这仅仅是demo。一般来说,我们做数据可视化是为了更方便的观察数据,以便快速得到结论。那今天就简单做个小型柱状图,来帮助我们分析数据。
背景
代码bug率大家都清楚吧,我们这样简单规定一下。1级bug记5分,2级3分,3级1分,4级0.1分。bug错误率就是(1级bug数*5 + 2级bug数*3 + 3级bug数*1 + 4级bug数*0.1)/代码量。
| 1 | 2 | 3 | 4 |
|---|---|---|---|
| 5 | 3 | 1 | 0.1 |
这样我们便得到一个这样的数据。
数据格式
const data = [
{name: 'zhangsan', one: 1, two: 1, three: 0, four: 2, lines: 900},
{name: 'lisi', one: 0, two: 0, three: 1, four: 1, lines: 300},
{name: 'wangwu', one: 1, two: 0, three: 6, four: 0, lines: 1000}
]
然后我们需要对数据进行一些处理:
数据处理
// x轴name
const names = Array.from(data, item => item.name);
// y轴data
const values = Array.from(data, item => {
const value = item.one * 5 + item.two * 3 + item.three * 1 + item.four * 0.1
const result = Math.floor(value*1000/item.lines)
return result
});
// 索引
const indices = Array.from(data, (item,index) => index);
需要的数据提取出来,接下来就得计算柱状图左下顶点的位置了
计算位置
// 条形图的宽度
const chartWidth = 480;
// 条形图的高度
const chartHeight = 300;
// 条形图的外边距
const margin = 15;
// 容器的宽度
const containerWidth = chartWidth + margin * 2;
// 容器的高度
const containerHeight = chartHeight + margin * 2;
// 计算每一个条左下顶点的横坐标,位置和在数组中的index有关
const step = chartWidth / names.length;
const barWidth = step * 0.8;
const xs = Array.from(indices, i => i * step);
// 计算每一个条左下顶点的纵坐标,因为所有条底部都是对齐的,所以就是图表的高度
const y = chartHeight;
计算出来顶点位置,接下来就是处理柱状图的柱了
处理图形
const vmax = Math.max(...values);
// 高度
const barHeights = Array.from(values, item => chartHeight * (item / vmax));
//获得每一个条的颜色
const nameColor = {
'zhangsan': "#5b8ff9",
'lisi': "#61ddaa",
'wangwu': "#65789b"
};
const colors = Array.from(names, item => nameColor[item])
接下来就是数据渲染部分了
渲染
const canvas = document.getElementById("canvas");
canvas.style.width = containerWidth + "px";
canvas.style.height = containerHeight + "px";
// 讲画布宽高设置为样式宽高的两倍主要是为了解决模糊问题
canvas.width = containerWidth * 2;
canvas.height = containerHeight * 2;
// canvas.width = containerWidth;
// canvas.height = containerHeight;
const context = canvas.getContext("2d");
// 抵消将画布宽高设置为样式宽高两倍的影响
context.scale(2, 2);
// 将坐标原点移动到绘制图表的区域
context.translate(margin, margin);
for(const index of indices){
// 将需要绘制的属性取出来
const color = colors[index];
const x = xs[index];
const barHeight = barHeights[index];
const value = values[index];
const xname = names[index]
// 绘制条
context.fillStyle = color;
context.fillRect(x, y - barHeight, barWidth, barHeight);
// 绘制值
context.textAlign = "center";
context.textBaseline = "middle";
context.fillStyle = "white";
context.font = "25px PingFangSC-Regular, sans-serif";
context.fillText(value, x + barWidth / 2, y - barHeight / 2);
// x轴内容
context.textAlign = "center";
context.textBaseline = "middle";
context.fillStyle = "black";
context.font = "25px";
context.fillText(xname, x + barWidth / 2, y - 20);
}
结果预览
OK,看看成果图吧。至于为什么不用中文呢,是因为会出现乱码问题。可自行查阅资料解决。这里就不做赘述了。
由图可见,李四的代码质量最好,王五的最差,张三一般,但是也偏高。所以结果可见,李四涨薪升职级,张三不动,王五例会批评(毕业警告)。
完整代码
总结
本文我们根据上章所学手动实现了一个简易的柱状图。虽然丑,但是麻雀虽小,五脏俱全。该有的展示还是挺清楚的,一目了然。但本文仅实现了canvas版本,有兴趣读者可自行实现svg版本。