本篇文章主要是看了蚂蚁金融体验部的小册所学习。
可视化入门:从 0 到 1 开发一个图表库 - 数据可视化 - 掘金小册 (juejin.cn)
小册里先介绍了什么是可视化。
可视化的定义: 将不可见或难以直接显示的数据转化为可感知的图形、符号、颜色、 纹理等,增强数据识别效率,传递有效信息”的手段就是数据可视化。
小册里设计了一个真实的数据分析任务:可视化苏菲的世界。围绕着“通过可视化苏菲的世界学习可视化”这个目的展开。
小册选择的问题是:“哲学家、哲学问题和流派的数量关系是怎样的?”,所以我们去统计了“苏菲的世界”数据集中哲学家、哲学问题和流派的数量,得到的结果如下:
| name | value |
|---|---|
| questions | 17 |
| schools | 25 |
| philosophers | 35 |
然后我们要将该结果以图形的形式展现。
canvas 实现
Canvas2D 是一种指令式的绘图系统,我们调用一些绘图指令,就可以在画布上绘制对应的视觉元素。
在html里 我们首先要创建canvas 对象 给它一个id 便于拿到这个对象,对它进行操作。
<canvas id="canvas"></canvas>
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<!-- html5 画布 -->
<canvas id="canvas"></canvas>
<script src="./index.js"></script>
<script>
const canvas = document.getElementById('canvas'); //找到画布
//设置画布样式的大小
canvas.style.width=containerWidth +'px';
canvas.style.height=containerHeight + 'px';
//清晰度 画布大小
canvas.width=containerWidth*2;
canvas.height=containerHeight*2;
const context = canvas.getContext('2d');
// 有点类似 像素
// context.scale(2,2); //抵消将画布宽高设置为样式宽高两倍的影响
// 画布从左上角开始绘画
context.translate(margin,margin) // 0 0 -> 左上角
// 遍历每个数据 拿到其相对应的属性的值
for(const index of indices){
// 拿到每个数据的填充颜色
const color = colors[index];
// 拿到每个数据画图的横坐标
const x =xs[index];
// 拿到每个数据画图的纵坐标
const barHeight = barHeights[index];
// 拿到每个数据的值
const value= values[index];
// 填充颜色
context.fillStyle =color;
// 绘制矩形
context.fillRect(x,y-barHeight,barWidth,barHeight);
context.textAlign = "center";
context.textBaseline = "middle";
context.fillStyle = "white";
context.font = "25px sans-serif";
// 文字 所在位置
// 从左上角 画起
context.fillText(value, x + barWidth / 2, y - barHeight / 2 );
}
</script>
</body>
</html>
注意 图形的x,y轴方向是这样的,然后我们图形的y轴每个数据的高度以这些数据的最大值为参照物,从左上角 画起 每个矩形都是从左上角开始绘画,所以第一个矩形应该是(0,y - barHeight,barWidth,barHeight)
矩形(x,y-barHeight,barWidth,barHeight)
数据处理
const data = [
{ name: "questions", value: 17 },
{ name: "schools", value: 25 },
{ name: "philosophers", value: 35 },
];
// canvas 画布
const chartWidth = 480; //条形图的宽度
const chartHeight = 300; //条形图的高度
const margin = 15; //条形图的外边距
//canvas 画布的大小
const containerWidth = chartWidth + margin * 2;
const containerHeight = chartHeight + margin * 2;
//取出所有的names x 坐标
// [] new Array(10) Array.from
const names = Array.from(data, (d) => d.name)
//data 对象 每个对象中的子元素调用这个函数
console.log(names)
const values = Array.from(data, (d) => d.value)
console.log(values)
// 不需要item 占位
const indices = Array.from(data,(_,i)=> i);
console.log(indices)
const step = chartWidth / names.length; // 执行几步
const barWidth = step*0.8; //x轴的偏移
//横坐标每个name的绘制起始值
const xs = Array.from(indices,(i)=> i*step)
console.log(xs)
const y = chartHeight;
const vmax = Math.max(...values);
const barHeights = Array.from(values,(v)=> chartHeight*v/vmax);
console.log(barHeights)
const nameColor ={
questions:'#5B8FF9',
philosophers:'#61DDAA',
schools:'#657898'
}
const colors = Array.from(names,(name)=> nameColor[name])
svg实现
SVG(Scalable Vector Graphics),可缩放矢量图,它是浏览器支持的一种基于 XML 语法的图像格式。相对于 Canvas2D 这种指令式的绘图系统来讲,SVG 是一种声明式的绘图系统,它的使用方式和普通的 DOM 元素非常像,所以使用起来比较简单。
js 与canvas的相同,就不再冗余了。
个人理解,利用封装好的svg 然后利用里面的rect绘制矩形,直接把需要的坐标,大小放进去就可以了。
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<svg id="container-svg"></svg>
<script src="./index.js"></script>
<script>
// svg xml 标签
//动态数据进入svg DOM 编程
// 封装了动态生成svg 绘制标签的功能
function createSVGElement(type){
return document.createElementNS('http://www.w3.org/2000/svg',type)
}
const svg = document.getElementById('container-svg');
svg.setAttribute('width',containerWidth);
svg.setAttribute('height',containerHeight);
svg.setAttribute('viewBox',[0,0,containerWidth,containerHeight]);
const g = createSVGElement('g');
g.setAttribute('transform',`translate(${margin},${margin})`);
svg.appendChild(g);
for(const index of indices){
const color = colors[index];
const x = xs[index]; //x坐标
const barHeight = barHeights[index];
const value = values[index];
const rect = createSVGElement('rect');
rect.setAttribute('x',x);
rect.setAttribute('y',y-barHeight);
rect.setAttribute('fill',color);
rect.setAttribute('width',barWidth);
rect.setAttribute('height',barHeight);
g.appendChild(rect);
const text = createSVGElement('text');
text.textContent = value;
text.setAttribute('text-anchor','middle');
text.setAttribute('fill','black');
text.setAttribute('family','sans-serif');
text.setAttribute('alignment-baseline','middle');
text.setAttribute('x',x+barWidth/2);
text.setAttribute('y',y-barHeight/2);
g.appendChild(text);
}
</script>
</body>
</html>
canvas和svg的差别
小册里是这样介绍的
SVG 的优点是方便交互,因为它也有 DOM 结构,可以方便地监听事件。但是性能方面却有所影响:如果我们要绘制的图形非常复杂,这些元素节点的数量就会非常多。而节点数量多,就会大大增加 DOM 树渲染和重绘所需要的时间。
相比来说,Canvas 交互实现就不太容易,因为对每个图形的拾取(判断鼠标点位置在哪个图形上)需要开发者自己实现(很多渲染引擎会解决这个问题,我们后面会看到),但是它的绘制性能却相对较优。
所以当数据量不大且侧重交互的情况,用 SVG 比较合适;当数据量较大的时候用 Canvas 比较合适。
个人理解,像该图形,使用遍历拿数据放数据,二者感觉差别不大。
echarts实现
ECharts 是一个使用 JavaScript 实现的开源可视化库,涵盖各行业图表,满足各种需求。 在 ECharts 中就会通过 series 去组合形成不同的可视化图表类型。 快速上手-ECharts
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<canvas id="container-canvas" width="600" height="400"></canvas>
<!-- CDN? 内容分发网络 -->
<script src="https://cdn.bootcdn.net/ajax/libs/echarts/5.4.1/echarts.min.js"></script>
<!-- SVG Canvas 底层绘制API 太麻烦,第三方组件库 -->
<script>
const chart = echarts.init(
document.getElementById('container-canvas')
)
const option = {
xAxis: {
type: 'category',
data: ['questions', 'schools', 'philosphers']
},
yAxis: {
type: 'value'
},
series: [
{
data: [17, 25, 35],
type: 'bar'
}
]
}
chart.setOption(option);
</script>
</body>
</html>