前端数据可视化2——canvas实现柱状图

647 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 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)/代码量

1234
5310.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,看看成果图吧。至于为什么不用中文呢,是因为会出现乱码问题。可自行查阅资料解决。这里就不做赘述了。

image.png

由图可见,李四的代码质量最好,王五的最差,张三一般,但是也偏高。所以结果可见,李四涨薪升职级,张三不动,王五例会批评(毕业警告)。

完整代码

总结

本文我们根据上章所学手动实现了一个简易的柱状图。虽然丑,但是麻雀虽小,五脏俱全。该有的展示还是挺清楚的,一目了然。但本文仅实现了canvas版本,有兴趣读者可自行实现svg版本。