大屏可视化项

预览链接

Snipaste_2022-02-06_00-16-58

项目描述

技术栈:React + ReactRouter + Echart + Typescript

这个项目是我在学习适配大屏与 Echart 时,仿的开源项目做的。

页面布局使用了 Grid 和 Flex 布局,使用 rem 动态记算适配不同尺寸的屏幕。

项目中包含的图例有柱形图、折线图、饼图、地图和表格等。

通过这个项目,我掌握了大屏可视化项目的制作技巧。

适配屏幕

市面上大部分的显示器几乎都是16:9的尺寸,也就是1920 * 1080的分辨率。

理想中的效果:

  • 当屏幕的尺寸比例刚好是16:9时,页面能刚好全屏展示,内容占满显示器

    abxl1-8g4hn
  • 当屏幕的尺寸比例小于16:9时,页面上下留白,左右占满并上下居中,显示比例保持16:9

    aw0qr-ewt2l
  • 当屏幕尺寸比例大于16:9时,页面左右留白,上下占满并居中,显示比例保持16:9

    ajxvu-1zcut

当屏幕大小改变时,动态计算中间内容的显示比例大小,保证内容一直保持16:9

2022-2-5-dfa54 (1)

解决方案: rem

rem(font size of the root element)

是 css3 中新增的一个大小单位,即相对于根元素 font-size 值的大小。

思路就是动态的计算出页面的 fontsize 从而改变 rem 的大小。

rem 计算公式

图片1-2022

Wp 为页面有效宽度,Hp 为页面有效宽度

页面左右居中,上下居中,四周留白即可

然后在 head 里用 JS 设置 1rem = Wp / 100

element 用 rem

图片-2022-2-2

假设某 div 在设计稿中长 100px,设计稿宽度 1920px

那么该 div 在页面中长为 100/1920 * 100rem

最后可以写一个 px() 函数来计算 100px 对应的 rem

写个 scss 函数

1
2
3
@function px($n) {
@return $n / 2420 * 100rem;
}

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8"/>
<meta name="viewport"
content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no,viewport-fit=cover">
<title>大屏可视化项目</title>
</head>
<body>
<script>
const clientWidth = document.documentElement.clientWidth
const clientHeight = document.documentElement.clientHeight
window.pageWidth = clientWidth / clientHeight > 16 / 9 ? clientHeight * (16 / 9) : clientWidth
const pageHeight = pageWidth / (16 / 9)
// 设置一个 rem
const string = `
<style>
html{
font-size: ${pageWidth / 100}px
}
</style>`
document.write(string)
</script>
<div id="root"></div>
<script>
// root.style.width = pageWidth + 'px'
root.style.height = pageHeight + 'px'
root.style.marginTop = (clientHeight - pageHeight) / 2 + 'px'
</script>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>

使用 grid 布局

grid-template-areas

效果图

Snipaste_2022-02-06_00-03-20

主要代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
.home {
> main {
display: grid;
grid-template:
'box1 box2 box4 box5' 755fr
'box3 box3 box4 box5' 363fr / 366fr 361fr 811fr 747fr;
> .section1 {
grid-area: box1;
}
> .section2 {
grid-area: box2;
}
> .section3 {
grid-area: box3;
}
> .section4 {
grid-area: box4;
}
> .section5 {
grid-area: box5;
}
}
}

使用 Echart

安装 yarn add -D echarts

引入 import * as echarts from 'echarts';

chart1 效果图:

Snipaste_2022-02-06_00-33-07

chart1 代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
import React, {useEffect, useRef} from 'react';
import * as echarts from 'echarts';

const Chart1 = () => {
const px = (n) => n / 2420 * (window as any).pageWidth;
const divRef = useRef(null);
useEffect(() => {
const myChart = echarts.init(divRef.current);

myChart.setOption({
title: {show: false},
legend: {show: false},
xAxis: {
data: ['城关区', '七里河区', '西固区', '安宁区', '红古区', '永登区', '皋兰区', '榆中区', '兰州新区'],
axisTick: {show: false},
axisLabel: {
fontSize: px(12),
interval: 0,
formatter(val) {
if (val.length > 2) {
const array = val.split('');
array.splice(2, 0, '\n');
return array.join('');
} else {
return val;
}
},
}
},
yAxis: {
splitLine: {show: false},
axisLine: {lineStyle: {color: '#686a75'}},
axisLabel: {
fontSize: px(12)
}
},
grid: {
x: px(40),
y: px(40),
x2: px(40),
y2: px(40)
},
series: [
{
name: '人数',
type: 'bar',
data: [5, 20, 24, 10, 10, 32, 8, 12, 19, 6],
},
]
});
}, []);
return (
<div className="bordered 管辖统计">
<h2>案发派出所管辖统计</h2>
<div ref={divRef} className="chart"></div>
</div>
);
};
export {Chart1};

画地图

需要引入 china.json

1
import china from '../geo/china.json';

echart 中注册一个地图:

Snipaste_2022-02-06_00-43-56

参考 https://juejin.cn/post/6972416642600927246