- 难点:地图可视化
- 地图常见用法:控件,绘图,动画
- 地图可视化:散点
- 数据可视化,是关于数据视觉表现形式的科学技术研究
就是将数据转换成易于人员辨识和理解的数据形式,比如2D,3D视图等,底层是计算机图形学
3.1前端数据可视化解决方案如下
- highcharts
- Echarts
- AntV
- three.js
- zrender
- d3
- canvas
- Svg
- WebGL
- HTML
- Chorme(Skia,OPenGL)
Skia:是chorme和Android的底层2D绘图引擎
OPenGL:2D,3D图形渲染库
1.Canvas简单介绍
- 编写canvas标签(注意指定宽高)
- 获取canvas DOM 对象
- 获取canvas 对象
- 设置绘图属性
- 调用绘图API
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Canvas</title>
</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); // 绘制矩形,0,0 XY轴坐标 50,50是宽高
// 绘制线条
ctx.beginPath();
ctx.lineWidth = 1; // 线条宽度
ctx.strokeStyle = 'blue'; // 线条填充色
ctx.moveTo(100, 100); // 起点坐标
ctx.lineTo(250, 75); // 中间点坐标
ctx.lineTo(400, 140); // 终点坐标
ctx.stroke(); // 绘制线段
// 绘制圆型
ctx.beginPath();
ctx.lineWidth = 3; // 圆形宽度
ctx.strokeStyle = 'green'; // 圆形填充色
ctx.fillStyle = 'red';
ctx.arc(300, 300, 50, 0, 2 * Math.PI); // 绘制圆形,300,300 圆心坐标 50半径
ctx.stroke(); // 绘制圆边框
ctx.fill(); // 填充圆
// 绘制点
ctx.beginPath();
ctx.lineWidth = 2; // 线条宽度
ctx.strokeStyle = 'red'; // 线条填充色
ctx.moveTo(400, 400); // 起点坐标
ctx.lineTo(401, 401); // 终点坐标
ctx.stroke(); // 绘制线段
</script>
</body>
</html>
- 最终效果
Canvas进阶案列:图片压缩
- 使用场景:前端允许上传3M的图片
- 将图片文件转成base64
// 将图片转化成base64
function converImageToBase64(file, callback) {
// 实例化fileReader对象
let reader = new FileReader();
// 文件加载完成之后
reader.addEventListener('load', function (e) {
const base64Image = e.target.result;
callback && callback(base64Image);
// 完成之后,让内存去回收 reader
reader = null;
});
reader.readAsDataURL(file);
}
2.计算压缩比,通过最大高度,最大宽度进行计算
3.使用canvas 重新绘制(压缩尺寸后的)
- 进行分辨率压缩,使用canvas.toDataURL进行二次压缩
// 图片压缩函数
// 思路:1,图片尺寸压缩,获取图片宽高 2,图片输出分辨率
function compressImage(base64Image, callback) {
let maxW = 1024;
let maxH = 1024;
// 渲染图片
const image = new Image();
// 对图片宽高进行压缩
image.addEventListener('load', function (e) {
let ratio; // 图片压缩比
let needCompress = false; // 是否需要压缩
// 获取图片的原始宽高 image.naturalWidth, image.naturalHeight
// 先判断图片宽度是否要压缩
if (maxW < image.naturalWidth) {
needCompress = true;
// 计算压缩比
ratio = image.naturalWidth / maxW;
console.log(ratio, 'ratio'); // 1.25
// 图片高度也是要同比压缩,经过压缩之后的图片宽高比例不变
maxH = image.naturalHeight / ratio;
// 经过压缩之后的图片尺寸为 1024 * 724
console.log(image.naturalWidth, image.naturalHeight, maxH, 'maxH'); // 1280 905 704
}
// 判断图片高度是否要压缩
if (maxH < image.naturalHeight) {
needCompress = true;
ratio = image.naturalHeight / maxH;
maxW = image.naturalWidth / ratio;
}
// 不需要压缩,获取图片的实际尺寸
if (!needCompress) {
maxW = image.naturalWidth;
maxH = image.naturalHeight;
}
// 最后使用canvas 重新绘制压缩图片
const canvas = document.createElement('canvas');
canvas.setAttribute('id', '_compress_');
canvas.width = maxW;
canvas.height = maxH;
// 压缩效果,实际过程中,隐藏压缩过程,设置为hidden
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);
// 将图片转换为base64,第一个是参数是图片类型,第二个,压缩参数 0-1,一般设置0.8,0.9
const compressImage = canvas.toDataURL('image/jpg', 0.1);
// 将压缩后的图片保存
callback && callback(compressImage);
canvas.remove();
// // 展示对比效果
// const imageCompressNode = new Image();
// imageCompressNode.src = compressImage;
// console.log(imageCompressNode.size);
// document.body.appendChild(imageCompressNode);
});
// image.src = base64Image;
// document.body.appendChild(image);
}
- 将图片保存并上传到服务器(传入文件及回调函数)
// 上传函数方法
function uploadToServer(compressImage) {
// 将压缩后的图片上传到服务器
console.log(compressImage, 'compressImage');
}
- 最终代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>图片压缩</title>
</head>
<body>
<input type="file" id="upload" />
</body>
<script>
// 可接受文件类型
const ACCEPT = ['image/jpg', 'image/png', 'image/jpeg'];
const MaxSize = 1024 * 1024;
const upload = document.getElementById('upload');
// 将图片转化成base64
function converImageToBase64(file, callback) {
// 实例化fileReader对象
let reader = new FileReader();
// 文件加载完成之后
reader.addEventListener('load', function (e) {
const base64Image = e.target.result;
callback && callback(base64Image);
// 完成之后,让内存去回收 reader
reader = null;
});
reader.readAsDataURL(file);
}
// 上传函数方法
function uploadToServer(compressImage) {
// 将压缩后的图片上传到服务器
console.log(compressImage, 'compressImage');
}
// 图片压缩函数 思路:1,图片尺寸压缩,获取图片宽高 2,图片输出分辨率
function compressImage(base64Image, callback) {
let maxW = 1024;
let maxH = 1024;
// 渲染图片
const image = new Image();
// 对图片宽高进行压缩
image.addEventListener('load', function (e) {
let ratio; // 图片压缩比
let needCompress = false; // 是否需要压缩
// 获取图片的原始宽高 image.naturalWidth, image.naturalHeight
// 先判断图片宽度是否要压缩
if (maxW < image.naturalWidth) {
needCompress = true;
// 计算压缩比
ratio = image.naturalWidth / maxW;
console.log(ratio, 'ratio'); // 1.25
// 图片高度也是要同比压缩,经过压缩之后的图片宽高比例不变
maxH = image.naturalHeight / ratio;
// 经过压缩之后的图片尺寸为 1024 * 724
console.log(image.naturalWidth, image.naturalHeight, maxH, 'maxH'); // 1280 905 704
}
// 判断图片高度是否要压缩
if (maxH < image.naturalHeight) {
needCompress = true;
ratio = image.naturalHeight / maxH;
maxW = image.naturalWidth / ratio;
}
// 不需要压缩,获取图片的实际尺寸
if (!needCompress) {
maxW = image.naturalWidth;
maxH = image.naturalHeight;
}
// 最后使用canvas 重新绘制压缩图片
const canvas = document.createElement('canvas');
canvas.setAttribute('id', '_compress_');
canvas.width = maxW;
canvas.height = maxH;
// 压缩效果,实际过程中,隐藏压缩过程,设置为hidden
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);
// 将图片转换为base64,第一个是参数是图片类型,第二个,压缩参数 0-1,一般设置0.8,0.9
const compressImage = canvas.toDataURL('image/jpg', 0.1);
// 将压缩后的图片保存
callback && callback(compressImage);
canvas.remove();
// // 展示对比效果
// const imageCompressNode = new Image();
// imageCompressNode.src = compressImage;
// console.log(imageCompressNode.size);
// document.body.appendChild(imageCompressNode);
});
// image.src = base64Image;
// document.body.appendChild(image);
}
upload.addEventListener('change', function (e) {
// 数组解构写法
// 等价于 const file = e.target.files[0]
const [file] = e.target.files;
if (!file) {
return;
}
const { type: fileType, size: fileSize } = file;
// 判断文件类型是否包含在里面
if (!ACCEPT.includes(fileType)) {
console.log(`不支持${fileType}文件类型`);
upload.value = '';
return;
}
// 图片容量检查
if (fileSize > MaxSize) {
console.log(`文件超出${MaxSize}`);
upload.value = '';
return;
}
// 压缩图片
/*
* 1,将文件转成base64
2,计算压缩比,通过最大高度,最大宽度进行计算
3,使用canvas 重新绘制(压缩尺寸后的)
4,进行分辨率压缩,使用canvas.toDataURL进行二次压缩
* 5,将图片保存并上传到服务器(传入文件及回调函数)
*/
converImageToBase64(file, base64Image =>
compressImage(base64Image, uploadToServer)
);
});
</script>
</html>
2.svg入门介绍
SVG是一种基于XML的图形文件格式,英文全称为Scalable Vector Graphics,意思为可缩放的矢量图形
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>SVG</title>
</head>
<body>
<svg width="800" height="800">
<rect width="50" height="50" style="fill: red"></rect>
<line
x1="100"
y1="100"
x2="250"
y2="75"
style="stroke: blue; stroke-width: 1"
></line>
<line
x1="250"
y1="75"
x2="450"
y2="175"
style="stroke: blue; stroke-width: 1"
></line>
<circle
cx="200"
cy="200"
r="50"
stroke="green"
stroke-width="2"
fill="red"
></circle>
<line
x1="300"
y1="300"
x2="301"
y2="301"
style="stroke: blue; stroke-width: 1"
></line>
</svg>
</body>
</html>
-
最终效果图
3.webgl技术分享
WebGL(web Graphics Library) 是一种3D绘图协议,WebGL可以为HTML Canvas 提供硬件3D加速渲染,这样Web开发人员就可以借助系统显卡来再浏览器里更流畅地展示3D场景和模型了,还能创建复杂的导航和数据视觉化。
4.zrender入门学习
zrender 是二维绘图引擎,它提供Canvas,SVG,VML等多种渲染方式。zrender也是Echarts的渲染器。
- 引入zrender库
- 编写div容器
- 初始化zrender对象
- 初始化zrender绘图对象
- 调用zrender add 方法绘图
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>zrender</title>
<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 rect = new zrender.Rect({
shape: {
x: 0,
y: 0,
width: 50,
height: 50,
},
style: {
fill: 'red',
lineWidth: 0,
},
});
/* 绘制一条线 */
var line = new zrender.Polyline({
shape: {
points: [
[100, 100],
[300, 200],
[150, 500],
],
},
style: {
stroke: '#0000ff',
lineWidth: 3,
},
});
/* 绘制点 */
var pot = new zrender.Polyline({
shape: {
points: [
[300, 300],
[301, 301],
],
},
style: {
stroke: 'red',
lineWidth: 1,
},
});
/* 绘制一个圆 */
var circle = new zrender.Circle({
shape: {
cx: 366,
cy: 366,
r: 50,
},
style: {
fill: '#0fa400',
stroke: '#eeaa45',
lineWidth: 2,
},
});
zr.add(rect);
zr.add(line);
zr.add(pot);
zr.add(circle);
</script>
</body>
</html>
-
最终实现效果图
5.d3入门介绍
D3 (Data-Driven Documents)是一个Javascript图形库,基于Canvas,Svg和HTML.
https://d3js.org/
// 入门学习链接
https://zhuanlan.zhihu.com/p/38001672
- 数据绑定案列
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>D3</title>
<script src="https://d3js.org/d3.v6.min.js"></script>
</head>
<body>
<p>vue</p>
<p>react</p>
<p>agular</p>
<button id="datum">datum</button>
<!-- 对整体数据源进行更新 -->
<button id="data">data</button>
</body>
<script>
// 这个body 是d3里面的document,并不是浏览器里面的document
const body = d3.select('body');
// 获取body 所有的p标签
const p = body.selectAll('p');
function doDatum() {
const str = 'Violet';
p.datum(str);
p.text(function (d, i) {
console.log(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>
</html>
-
结果如下
6.Three.js入门介绍
Three.js是一个基于WebGL的javascript 3D图形库
- 案列:旋转正方体
7.highcharts入门介绍
Highcharts 是一个用纯javaScript编写的图表库,能够很简单便捷的在web网站或是web应用程序添加有交互性的图表,它包括Highcharts JS,Highstock JS,Highmaps JS共三款软件,均为纯js编写的HTML5图库。
https://www.highcharts.com.cn/demo/highcharts
8.antv入门介绍
Antv 是蚂蚁金服全新一代数据可视化方案,致力于提供一套简单方便,专业可靠,无限可能的数据可视化最佳实践。
- 它包括以下解决方案:
- G2:可视化引擎
- G2Plot:图表库
- G6:图可视化引擎-思维导图,关系图等
- Graphin:基于G6的图分析组件
- F2:移动可视化方案
- ChartCube:AntV图表在线制作
- L7:地理空间数据可视化
https://www.highcharts.com.cn/demo/highcharts
8.1 antv-g2入门
https://antv-g2.gitee.io/zh/examples/gallery
- 实现代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>g2实现折线图</title>
<script src="https://gw.alipayobjects.com/os/lib/antv/g2/4.1.16/dist/g2.min.js"></script>
</head>
<body>
<div id="container"></div>
</body>
<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 },
];
// 创建G2图表示例化
const chart = new G2.Chart({
container: 'container',
autoFit: true,
height: 500,
});
chart.data(data);
chart.axis('value', {
label: {
formatter: val => {
return +val + 'k';
},
},
});
chart.scale({
year: {
range: [0, 1],
},
value: {
min: 0,
nice: true,
},
});
chart.tooltip({
showCrosshairs: true, // 展示 Tooltip 辅助线
shared: true,
});
chart.line().position('year*value').label('value').color('#ff5957');
chart.point().position('year*value').color('#ff5957');
chart.render();
</script>
</html>
-
最终结果
8.2 antv-g6入门
- 节点demo
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>g6图表demo</title>
<script src="https://gw.alipayobjects.com/os/lib/antv/g6/3.7.1/dist/g6.min.js"></script>
</head>
<body>
<div id="mountNode"></div>
<script>
// 定义数据源
const data = {
// 点集
nodes: [
{
id: 'node1',
label: '开始',
x: 100,
y: 200,
size: 80,
labelCfg: {
position: 'center',
style: {
fontSize: 16,
fill: 'red',
},
},
},
{
id: 'node2',
label: '连接',
x: 300,
y: 200,
},
{
id: 'node3',
label: '结束',
x: 500,
y: 200,
},
{
id: 'node4',
label: '结束4',
x: 300,
y: 400,
},
],
// 边集
edges: [
// 表示一条从 node1 节点连接到 node2 节点的边
{
source: 'node1',
target: 'node2',
label: '连接线1',
},
{
source: 'node2',
target: 'node3',
label: '连接线2',
},
{
source: 'node1',
target: 'node4',
label: '连接线3',
},
],
};
// 创建
const graph = new G6.Graph({
container: 'mountNode', // 指定图画布的容器 id,与第 9 行的容器对应
// 画布宽高
width: 800,
height: 500,
defaultNode: {
shape: 'circle',
size: [100],
color: '#5B8FF9',
style: {
fill: '#9EC9FF',
lineWidth: 3,
},
labelCfg: {
style: {
fill: 'yellow',
fontSize: 20,
},
},
},
defaultEdge: {
style: {
stroke: '#e2e2e2',
},
},
});
// 读取数据
graph.data(data);
// 渲染图
graph.render();
</script>
</body>
</html>
-
最终结果
8.3 antv-L7入门
- 绘制气泡地图
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>L7demo</title>
<script src="https://unpkg.com/@antv/l7"></script>
</head>
<body>
<div id="map"></div>
</body>
<script>
const scene = new L7.Scene({
id: 'map',
map: new L7.GaodeMap({
pitch: 0, // 角度
style: 'white',
center: [96.99215001469588, 29.281597225674773],
zoom: 12,
maxZoom: 10,
}),
});
// 地图绘制完成,即scence 加载完成,在绘制点
scene.on('loaded', () => {
fetch(
'https://gw.alipayobjects.com/os/basement_prod/337ddbb7-aa3f-4679-ab60-d64359241955.json'
)
.then(res => res.json())
.then(data => {
data.features = data.features.filter(item => {
return item.properties.capacity > 800;
});
// 气泡图通过 PointLayer 对象实例化,
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,
});
scene.addLayer(pointLayer);
});
});
</script>
</html>
-
最终结果