数据可视化前端技术选型
- 数据可视化解决方案:
- Canvas入门
- Svg入门
- WebGL
- ZRender 入门
- D3 入门
- three.js
- Canvas 图片压缩
数据可视化解决方案:
SKia 是Chrome和Android的底层2D绘图渲染,采用c++编程
OpenGL 是2D、3D图形渲染库,可以绘制简单2D到复杂的3D图形,OpenGL常用于CAD、VR、数据可视化和游戏等众多领域
Chrome 使用Skia作为绘图引擎,向上层开放了canvas、svg、WebGL、HTML等绘图能力
Canvas入门
Canvas是h5的新特性,它允许我们使用canvas元素在网页上通过JavaScript绘制图像
入门案例:绘制点、矩形、直线和圆形
<!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>canvas练习</title>
</head>
<body>
<canvas id="canvas" width="800" height="800"></canvas>
<script>
// 为了让vscode能够提示canvas的api
/** @type {HTMLCanvasElement} */
const canvas = document.getElementById('canvas');
// 绘制正方形
const ctx = canvas.getContext('2d'); // 获取canvas对象
ctx.fillStyle = 'red'; // 填充色
ctx.fillRect(0, 0, 50, 50); // 绘制正方形
// 绘制线段
ctx.beginPath()
ctx.lineWidth = 1; // 线条宽度
ctx.strokeStyle = 'blue'; // 线条颜色
ctx.moveTo(100, 100); // 起点 (相当于画布)
ctx.lineTo(250, 75); // 终点
ctx.lineTo(350, 85); // 可使用lineTo绘制多条线段
ctx.stroke(); // 绘制线段
// 绘制圆形
ctx.beginPath()
ctx.lineWidth = 1;
ctx.strokeStyle = 'green';
ctx.fillStyle = 'red';
ctx.arc(200, 200, 50, 0, 2 * Math.PI); // x, y轴坐标, 半径,起始角度,结束角度
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>
Svg入门
Svg是一种基于XML的图像文件格式,英文全称是Scalable Vector Graphics,即可缩放的矢量图。在放大的情况下canvas会失真,svg不会。
<!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>svg 练习</title>
</head>
<body>
<svg width="800" height="800">
// 绘制矩形
<rect width="50" height="50" style="fill:red; stroke-width:1px; stroke:aqua" />
// 绘制线段
// x1 y1 起点坐标 x2 y2 终点坐标
<line x1="100" y1="100" x2="250" y2="75" style="stroke: blue; stroke-width: 1px;" />
<line x1="250" y1="75" x2="300" y2="85" style="stroke:red ; stroke-width: 1px;" />
// 绘制圆形 cx cy 中心坐标, r 半径
<circle cx="200" cy="200" r="50" stroke="green" stroke-width="2" fill="red" />
// 绘制点
<line x1="300" y1="300" x2="301" y2="301" style="stroke: blue; stroke-width: 1px;" />
</svg>
</body>
</html>
WebGL
WebGL(Web Graphics Library)是一种3D绘图协议,WebGL可以作为HTML5 canvas提供硬件3D加速渲染,这样的Web开发人员就可以借助系统显卡来在浏览器里更流畅地展示3D场景和模型了,还能创建复杂的导航和数据视觉化。
ZRender 入门
ZRender是二维绘图引擎,他提供canvas,svg,vml等多种渲染方式。ZRender也是Echarts的渲染器。
<!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>ZRender练习</title>
<script src="https://cdn.jsdelivr.net/npm/zrender@4.3.0/dist/zrender.js"></script>
</head>
<body>
<div id="content" style="width: 800px; height: 800px;"></div>
<script>
const zr = zrender.init(document.getElementById('content'));
// 矩形
const rect = new zrender.Rect({
shape: {
x: 0,
y: 0,
width: 50,
height: 50
},
style: {
fill: 'red',
lineWidth: 0
}
})
zr.add(rect);
// 圆形
const circle = new zrender.Circle({
shape: {
cx: 200,
cy: 300,
r: 40
},
style: {
fill: 'red',
stroke: '#F00'
}
});
zr.add(circle);
// 线段
const line = new zrender.Polyline({
shape: {
// 连线的点
points: [
[100, 100],
[200, 75],
[300, 85]
]
},
style: {
stroke: 'red',
lineWidth: 1
}
});
zr.add(line);
// 点
const point = new zrender.Polyline({
shape: {
// 连线的点
points: [
[300, 300],
[301, 301],
]
},
style: {
stroke: 'red',
lineWidth: 1
}
});
zr.add(point);
</script>
</body>
</html>
D3 入门
D3(Data-Driven Docment)是一个JavaScript图形库,基于Canvas、 Svg和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>d3练习</title>
<script src="https://d3js.org/d3.v5.js"></script>
</head>
<body>
<p>vue</p>
<p>react</p>
<p>angular</p>
<button id="datum">datum</button>
<button id="data">data</button>
<script>
const body = d3.select('body');
const p = body.selectAll('p');
function doDatum() {
const str = 'text'
p.datum(str);
p.text(function (d, i) {
console.log(d, i);
return `${d}-${i}`
})
}
function doData() {
const dataset = ['vue', 'react', 'angular']
p.data(dataset).text(function (d, i) {
console.log(d, i)
return `${d}-${i}`
})
}
const datum = document.getElementById('datum').addEventListener('click', function () {
doDatum();
});
const data = document.getElementById('data').addEventListener('click', function () {
doData();
});
</script>
</body>
</html>
three.js
three.js 是一种基于WebGL的JavaScript 3D图形库
<!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>three练习</title>
<script src="https://threejs.org/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>
Canvas 图片压缩
<!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>
<input type="file" id="upload">
<script>
const ACCEPT = ['image/jpg', 'image/png', 'image/jpeg'];
const MAXSIZE = 3 * 1024 * 1024;
const MAXSIZE_STR = '3MB';
const upload = document.getElementById('upload');
function convertImageToBase64(file, callback) {
let reader = new FileReader();
reader.addEventListener('load', (e) => {
console.log(reader.result)
const base64Image = e.target.result;
callback && callback(base64Image);
reader = null;
})
reader.readAsDataURL(file);
}
function compress(base64Image, callback) {
console.log(base64Image);
let maxW = 1024;
let maxH = 1024;
// 创图对象,设定高宽,进行压缩,设置分辨率,进行输出
const image = new Image();
image.addEventListener('load', (e) => {
let ratio;
let needCompress = false; // 是否需要压缩
if (maxW < image.naturalWidth) {
needCompress = true;
ratio = image.naturalWidth / 1024;
maxH = image.naturalHeight / ratio;
} // 经过处理后。图片的实际尺寸为1024 * 640
if (maxH < image.naturalHeight) {
needCompress = true;
ratio = image.naturalHeight / 1024;
maxW = image.naturalWidth / ratio;
}
// 不需要压缩
if (!needCompress) {
maxH = image.naturalHeight;
maxW = image.naturalWidth;
}
const canvas = document.createElement('canvas');
canvas.setAttribute('id', '__compress__');
canvas.width = maxW;
canvas.height = maxH;
canvas.style.visibility = 'visible';
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.9);
callback && callback(compressImage);
// const _image = new Image();
// _image.src = compressImage;
// document.body.appendChild(_image);
canvas.remove();
})
image.src = base64Image;
document.body.appendChild(image);
}
function uploadToServer(compressImage) {
console.log(compressImage);
}
upload.addEventListener('change', (e) => {
const [file] = e.target.files;
if (!file) {
return;
}
const { type: fileType, size: fileSize } = file;
// if (ACCEPT.indexOf(fileType) < 0) {
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>