什么是数据可视化?
1、基本概念
数据可视化,是关于数据视觉表现形式的科学技术研究。就是将数据转换成易于人员辨识和理解的视觉表现形式,如各种2D图表、3D图标、地图、矢量图等等,随着技术的不断进步,数据可视化的边界也在不断扩大。
2、发展历史
起源:数据可视化起源于20世界60年代诞生的计算机图形学
计算机图形学:是一种使用数学算法将二维或三维图形转化为计算机显示器的栅格形式的科学。
计算机图形学入门:https://zhihu.com/question/41468803
3、典型应用: excel,xmind,visio,数据大屏,数据报表,地图
数据可视化的入门技术
Chrome使用Skia作为绘图引擎,向上层开放了canvas,svg,WebGl,HTML等绘图能力。
1、canvas介绍
绘制点、矩形、直线和圆。
<!DOCTYPE html>
<html>
<head></head>
<body>
<canvas id="canvas" width="800" height="800"></canvas>
<script>
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');//获取到Canvas对象
ctx.fillStyle = 'red';
ctx.fillRect(0,0,50,50);
//绘制线
ctx.beginPath();
ctx.lineWidth = 2;
ctx.strokeStyle = 'blue';
ctx.moveTo(100,100);
ctx.lineTo(250,75);
ctx.lineTo(300,100);
ctx.stroke();
//绘制圆
ctx.beginPath();
ctx.lineWidth = 2;
ctx.strokeStyle = 'green';
ctx.fillStyle = 'red';
ctx.arc(200,200,50,0,2 * Math.PI);
ctx.stroke();
ctx.fill();
//绘制点
ctx.beginPath();
ctx.lineWidth = 1;
ctx.strokeStyle = 'red';
ctx.moveTo(300,300);
ctx.lineTo(301,301);
ctx.stroke();
</script>
</body>
</html>
图片压缩:
<!DOCTYPE html>
<html>
<head></head>
<body>
<input type="file" id="upload" />
<script>
const ACCEPT = ['image/jpg','image/png','image/jpeg'];
const MAXSIZE = 3*1024*1024;
const MAXSIZE_STR = '3MB';
function convertImageToBase64(file,callback){
let reader = new FileReader();
reader.addEventListener('load',function(e){
const base64Image = e.target.result;
callback && callback(base64Image);
reader = null;
})
reader.readAsDataURL(file);
}
function compress(base64Image,callback){
let maxW = 1024;
let maxH = 1024;
const image = new Image();
image.addEventListener('load',function(e){
let ratio;//图片的压缩比
let needCompress = false;//是否需要压缩
if(maxW < image.naturalWidth){
needCompress = true;
ratio = image.naturalWidth/maxW;
maxW = image.naturalWidth/ratio;
}
if(!needCompress){
maxW = image.naturalWidth;
maxH = image.naturalHeight;
}
const canvas = document.createElement('canvas');
canvas.setAttribute('id','__compress__');
canvas.width = maxW;
canvas.height = maxH;
canvas.style.visibility = 'hidden';
document.body.appendChild(canvas);
const ctx = canvas.getContext('2d');
ctx.clearRect(0,0,maxW,maxH);
ctx.drawImage(image,0,0,maxW,maxH);
const compressImage = canvas.toDataURL('image/jpeg',0.1);
callback && callback(compressImage);
const _image = new Image();
_image.src = compressImage;
document.body.append(_image);
console.log(image.src.length/_image.src.length)
canvas.remove();
})
image.src = base64Image;
document.body.appendChild(image);
}
function uploadToServer(compressImage){
console.log('upload to server ...',compressImage)
}
const upload = document.getElementById('upload');
upload.addEventListener('change',function(e){
const [file] = e.target.files;
if(!file){
return;
}
const { type:fileType,size:fileSize } = file;
if(!ACCEPT.includes(fileType)){
alert(`不支持[${fileType}]文件类型!`);
upload.value = '';
return;
}
if(fileSize > MAXSIZE){
alert(`文件超出${MAXSIZE_STR}`);
upload.value = '';
return;
}
convertImageToBase64(file,(base64Image)=>compress(base64Image,uploadToServer))
})
</script>
</body>
</html>
2、 SVG介绍
是一种基于XML的图像文件格式,它的英文全称为Scalable Vector Graphics,可缩放的矢量图形。
<!DOCTYPE html>
<html>
<head></head>
<body>
<svg width="800" height="800">
<react
width = "50"
height = "50"
style="fill:red;stroke-width:1px;stroke:rgb(0,0,0)"
/>
<line
x1="100"
y1="100"
x2="250"
y2="75"
style="stroke:blue;stroke-width:1;"
/>
<line
x1="250"
y1="75"
x2="300"
y2="100"
style="stroke:blue;stroke-width:1;"
/>
<circle
cx="200"
cy="200"
r="50"
stroke="green"
stroke-width = "2"
/>
</svg>
</body>
</html>
3、webgl 绘制一个点
<!DOCTYPE html>
<html>
<head></head>
<body>
<canvas id="canvas" width="200" height="200"></canvas>
<script>
window.onload=function (){
//顶点着色器
var VSHADER_SOURCE =
"void main() {"+
"gl_Position = vec4(0.0,0.0,0.0,1.0);"+
"gl_PointSize = 10.0; "+
"} ";
//片元着色器
var FSHADER_SOURCE =
"void main() {"+
"gl_FragColor = vec4(1.0,0.0,0.0,1.0);"+
"}";
const canvas = document.getElementById('canvas');
const gl= canvas.getContext('webgl');//获取到Canvas对象
if(!gl){
console.log("Failed");
return
}
//编译着色器
var vertShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertShader,VSHADER_SOURCE);
gl.compileShader(vertShader);
var fragShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragShader,FSHADER_SOURCE);
gl.compileShader(fragShader);
//合并程序
var shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram,vertShader);
gl.attachShader(shaderProgram,fragShader);
gl.linkProgram(shaderProgram);
gl.useProgram(shaderProgram);
//绘制一个点
gl.drawArrays(gl.POINTS,0,1);
}
</script>
</body>
</html>
4、zrender
zrender是echarts的底层渲染引擎。提供Canvas,SVG,VML等多种渲染方式
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/zrender@4.3.0/dist/zrender.js"></script>
</head>
<body>
<div id="container" style="width:800px;height:800px"></div>
<script>
const zr = zrender.init(document.getElementById('container'));
//绘制矩形
const react = new zrender.Rect({
shape:{
x:0,
y:0,
width:50,
height:50
},
style:{
fill:'red',
lineWidth:0
}
})
//绘制线
const line = new zrender.Polyline({
shape:{
points:[
[100,100],
[250,75],
[300,100]
]
},
style:{
stroke:'blue',
lineWidth:1
}
})
//绘制圆
const circle = new zrender.Circle({
shape:{
cx:200,
cy:200,
r:50
},
style:{
fill:'red',
stroke:'green',
lineWidth:2
}
})
//绘制点
const point= new zrender.Polyline({
shape:{
points:[
[300,300],
[301,301]
]
},
style:{
stroke:'blue',
lineWidth:1
}
})
zr.add(react)
zr.add(line)
zr.add(circle)
zr.add(point)
</script>
</body>
</html>
zrender使用总结:
1 引入zrender库
2 编写div容器
3 初始化zrender对象
4 初始化zrender绘图对象
5 调用zrender add方法绘图
zrender官网 https://ecomfe.github.io/zrender-doc/public/
zrender案例库 https://ecomfe.github.io/zrender-doc/public/examples
案例源码库 https://www.github.com/ecomfe/zrender-doc/blob/master/public/examples
5、D3(Data-Driven Documents) 是一个JavaScript图形库,基于Canvas,Svg和HTML。
案例库
https://www.observablehq.com/@d3/gallery
学习地址:https://www.zhuanlan.zhihu.com/p/38001672
数据绑定:
<!DOCTYPE html>
<html>
<head>
<script src="https://d3js.org/d3.v5.js"></script>
</head>
<body>
<p>Vue</p>
<p>React</p>
<p>Angular</p>
<button id="dutam">dutam</button>
<button id="data">data</button>
<script>
const body = d3.select('body')
const p = body.selectAll('p');
function doDatum(){
const str = 'Framework';
p.datum(str);
p.text(function(d,i){
return `${d}-${i}`
})
}
function doData(){
const dataset = ['Vue','React','Agular'];
p.data(dataset)
.text(function(d,i){
return `${d}-${i}`
})
}
document.getElementById('datum').addEventListener('click',function(e){
doDatum();
});
document.getElementById('data').addEventListener('click',function(e){
doData();
})
</script>
</body>
</html>
思维导图
https://www.github.com/reclay/vue-d3-tree-example
6、three.js的用法
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/three@0.116.1/build/three.js"></script>
</head>
<body>
<script>
var camera,scene,renderer;
var geometry,material,mesh;
init();
animate();
function init(){
camera = new THREE.PerspectiveCamera(70,window.innerWidth/window.innerHeight) ;
camera.position.z = 1;
scene = new THREE.Scene();
geometry = new THREE.BoxGeometry(0.2,0.2,0.2);
material = new THREE.MeshNormalMaterial();
mesh = new THREE.Mesh(geometry,material);
scene.add(mesh);
renderer = new THREE.WebGLRenderer({antialias:true});
renderer.setSize(window.innerWidth,window.innerHeight);
document.body.appendChild(renderer.domElement);
}
function animate(){
requestAnimationFrame(animate);
mesh.rotation.x += 0.01;
mesh.rotation.y += 0.02;
renderer.render(scene,camera)
}
</script>
</body>
</html>
threejs学习库:
https://www.github.com/mrdoob/three.js
静态文档地址:
https://www.github.com/mrdoob/three.js/tree/dev/docs
7、Highcharts
是一个用纯JavaScript编写的一个图标库,能够很简单便捷的在web网站或是web应用程序添加有交互性的图标,并且免费提供给个人学习、个人网站和非商业用途使用。Highcharts系列包含HighchartsJS,HighstockJS,HighmapsJS工三款软件,均为纯JavaScript编写的HTML5图表库。
能够很简单的在Web网站或是Web应用程序添加有交互性的图标,Hightcharts支持的图标类型有直线图,曲线图,区域图,柱状图,饼状图,散装点图,仪表图,气泡图,瀑布流图等多达20种图表,其中很多图表可以集成在同一个图形中形成混合图。
学习链接:https://www.youbaobao.xyz/datav-docs/guide/guide/highcharts.html
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.highcharts.com.cn/highcharts/highcharts.js"></script>
<style>
#container{
width:800px;
height:400px;
}
</style>
</head>
<body>
<div id="container"></div>
<script>
var chart = Highcharts.chart('container',{
title:{
text:'2010 ~ 2016 年太阳能行业就业人员发展情况'
},
subtitle:{
text:'数据来源:thesolarfoundation.com'
},
yAxis:{
title:{
text:'就业人数'
}
},
legend:{
layout:'vertical',
align:'righ',
verticalAlign:'middle'
},
plotOptions:{
series:{
label:{
connectorAllowed:false
},
pointStart:2010
}
},
series:[{
name:'安装 实施人员',
data:[43934,52503,57177,69658,97031,119931,137133,154175]
},{
name:'工人',
data:[24916, 24064, 29742, 29851, 32490, 30282, 38121, 40434]
}, {
name: '销售',
data: [11744, 17722, 16005, 19771, 20185, 24377, 32147, 39387]
}, {
name: '项目开发',
data: [null, null, 7988, 12169, 15112, 22452, 34400, 34227]
}, {
name: '其他',
data: [12908, 5948, 8105, 11248, 8989, 11816, 18274, 18111]
}]
})
</script>
</body>
</html>
8、Highstock
Highstock是用纯JavaScript编写的股票图表控件,可以开发股票走势或大数据量的时间轴图表。它包含多个高级导航组件:预设置数据时间范围,日期选择器、滚动条、平移、缩放功能。
案例:平安银行股价图
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="https://code.highcharts.com.cn/highstock/highstock.js"></script>
<style>
#container {
width: 800px;
height: 400px;
}
</style>
</head>
<body>
<div id="container"></div>
<script>
Highcharts.setOptions({
lang: {
rangeSelectorZoom: ''
}
});
fetch('https://data.jianshukeji.com/stock/history/000001')
.then(data => data.json())
.then(data => {
if(data.code !== 1) {
alert('读取股票数据失败!');
return false;
}
data = data.data;
var ohlc = [],
volume = [],
dataLength = data.length,
// set the allowed units for data grouping
groupingUnits = [[
'week', // unit name
[1] // allowed multiples
], [
'month',
[1, 2, 3, 4, 6]
]],
i = 0;
for (i; i < dataLength; i += 1) {
ohlc.push([
data[i][0], // the date
data[i][1], // open
data[i][2], // high
data[i][3], // low
data[i][4] // close
]);
volume.push([
data[i][0], // the date
data[i][5] // the volume
]);
}
// create the chart
var chart = Highcharts.stockChart('container', {
rangeSelector: {
selected: 1,
inputDateFormat: '%Y-%m-%d'
},
title: {
text: '平安银行历史股价'
},
xAxis: {
dateTimeLabelFormats: {
millisecond: '%H:%M:%S.%L',
second: '%H:%M:%S',
minute: '%H:%M',
hour: '%H:%M',
day: '%m-%d',
week: '%m-%d',
month: '%y-%m',
year: '%Y'
}
},
tooltip: {
split: false,
shared: true,
},
yAxis: [{
labels: {
align: 'right',
x: -3
},
title: {
text: '股价'
},
height: '65%',
resize: {
enabled: true
},
lineWidth: 2
}, {
labels: {
align: 'right',
x: -3
},
title: {
text: '成交量'
},
top: '65%',
height: '35%',
offset: 0,
lineWidth: 2
}],
series: [{
type: 'candlestick',
name: '平安银行',
color: 'green',
lineColor: 'green',
upColor: 'red',
upLineColor: 'red',
tooltip: {
},
navigatorOptions: {
color: Highcharts.getOptions().colors[0]
},
data: ohlc,
dataGrouping: {
units: groupingUnits
},
id: 'sz'
}, {
type: 'column',
data: volume,
yAxis: 1,
dataGrouping: {
units: groupingUnits
}
}]
})
})
</script>
</body>
</html>
9、antv-g2
antv官网
AntV是蚂蚁金服全新一代数据可视化解决方案,致力于提供一套简单方便、专业可靠、拥有无限可能的数据可视化服务。
G2折线图案例
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="https://unpkg.com/@antv/g2plot@latest/dist/g2plot.js"></script>
</head>
<body>
<div id="g2-chart"></div>
<script>
const data = [
{ year: '1991', value: 3 },
{ year: '1992', value: 4 },
{ year: '1993', value: 3.5 },
{ year: '1994', value: 5 },
{ year: '1995', value: 4.9 },
{ year: '1996', value: 6 },
{ year: '1997', value: 7 },
{ year: '1998', value: 9 },
{ year: '1999', value: 13 },
];
const chartDom = document.getElementById('g2-chart');
const line = new G2Plot.Line(chartDom,{
title:{
visible:true,
text:'g2折线图示例'
},
description:{
visible:true,
text:'折线图示例,这是一个副标题'
},
data,
xField:'year',
yField:'value',
point:{
visible:true,
size:5,
color:'#fff',
style:{
stroke:'#fe740c',
lineWidth:2,
fillOpacity:0.6
}
},
label:{
visible:true
},
color:'#f2740c'
});
line.render();
</script>
</body>
</html>
10、 antv-g6
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="https://gw.alipayobjects.com/os/lib/antv/g6/3.4.7/dist/g6.min.js"></script>
</head>
<body>
<div id="g6-chart"></div>
<script>
const data = {
nodes:[{
id:'node1',
x:100,
y:200,
label:'起始点',
size:60,
labelCfg:{
position:'top',
style:{
fontSize:16,
file:'#f00'
}
},
style:{
fill:'#ff0000',
stroke:'#888',
lineWidth:10
}
},{
id:'node2',
x:300,
y:200,
label:'目标点1',
size:80
},{
id:'node3',
x:500,
y:200,
label:'目标点2',
size:100
}],
edges:[{
source:'node1',
target:'node2',
label:'连接线1'
},{
source:'node2',
target:'node3',
label:'连接线2'
}]
};
const graph = new G6.Graph({
container:'g6-chart',
width:800,
height:500
})
graph.data(data);
graph.render();
</script>
</body>
</html>
11、antv-l7
全球电站分布案例:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="https://unpkg.com/@antv/l7"></script>
<style>
html,body{
padding:0;
margin:0;
width:100%;
height:100%;
}
</style>
</head>
<body>
<div id="l7-map"></div>
<script>
const scene = new L7.Scene({
id:'l7-map',
map:new L7.GaodeMap({
style:'dark',
center:[120.19382669582967,30.258134],
pitch:0,
zoom:6,
token:'e8653883477c1e38d190463340771816'
})
})
scene.on('loaded',function(){
fetch('https://gw.alipayobjects.com/os/basement_prod/337ddbb7-aa3f-4679-ab60-d64359241955.json').then(res=>res.json()).then(data=>{
const pointLayer = new L7.PointLayer({})
.source(data)
.shape('circle')
.size('capacity',[0,16])
.color('capacity',['#34B6B7',
'#4AC5AF',
'#5FD3A6',
'#7BE39E',
'#A1EDB8',
'#CEF8D6'])
.active(true)
.style({
opacity:0.5,
strokeWidth:0
})
.animate(false)
;
scene.addLayer(pointLayer);
})
})
</script>
</body>
</html>