基于React+Echart的大屏监控

3,225 阅读4分钟

基于React的大屏监控

技术栈

  • React
  • ReactRouter
  • Echarts

项目介绍

  • 此项目是为了熟悉前端可视化同时学习Echarts而写
  • 页面布局结合了Flex与Grid
  • 使用rem匹配不同屏幕大小
  • 项目中的统计图、环形图、柱状图、地图等来自于Echarts,可以实现实时更新数据
  • 此项目中数据不是真是数据,只是为了展示而编写的
  • 写完项目才发现dataV,貌似直接用来做可视化更新快一下,下次会尝试使用

预览

  1. 预览地址
  2. github地址

大屏适配

屏幕适配

  • 设计稿是16:9,理想中大屏也是16:9
  • 如果屏幕很宽,就左右剧中
  • 如果屏幕很高,就上下剧中
  1. 公式
  • Ry1im6.png
  • Wp 为页面有效宽度,Hp 为页面有效高度
  • 页面左右居中,上下居中,四周留白即可
  • 第一种情况:设备很宽,就左右剧中,页面高度为屏幕高度,页面宽度按比例计算
  • 第二种情况:设备很高,就上下剧中。页面的宽度为屏幕的宽度,页面高度按16:9计算
  1. 然后在 head 里用 JS 设置 1rem = Wp / 100(下文中会用到)
  2. 也可以设置
  • header: 定宽
header:{
    width: px(2420);
    margin: 0 auto;
    }
  • main的宽度定宽左右剧中
 main {
    width: px(2420);
    margin: 0 auto;
}

rem计算公式

Ry1jDP.png

1. rem公式

  • Ry3lvR.png
  • 一个div在页面的尺寸/页面的宽度 = div在设计稿中的尺寸 / 设计稿的宽度
  • 1rem = Wp / 100 推出: 页面宽度 = 100*rem
  • 结合以上2点,推出: div在页面中的尺寸 = (div在设计稿中的尺寸 / 设计稿的宽度) * 100 * rem
  • 假设某 div 在设计稿中长 100px,设计稿宽度 1920px,
  • 那么该 div 在页面中长为 100/1920 X 100rem
  • 最后可以写一个 px() 函数来计算 100px 对应的 rem

2. rem实际上是html里的font-size

  • 复习rem
<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) //页面高度
    const string = `<style>html{
        font-size:${pageWidth/100}px // 1rem
    }</style>
    `
    document.write(string) // 标签写在document里即可
  </script>
</head>
<body>
<div id="root"></div>
<script>
 // root.style.width = pageWidth +'px'
  root.style.height= pageHeight+ 'px'
  root.style.marginTop = (clientHeight - pageHeight) / 2  + 'px' // 多余区域的一半设为margin-top
</script>
<script type="module" src="/src/main.tsx"></script>
</body>

代码:展示一个div

  • 不能用px,要用rem
  • 怎么将px转为rem呢?
  • div在页面中的尺寸 = (div在设计稿中的尺寸 / 设计稿的宽度) * 100 * rem
  • 在scss文件中
@function px($n){
  @return $n/2420*100rem
}
  • 使用
 height: px(40);

Flex和Grid布局

  1. 为了让main占满页面
  • 层级: root> App> home >header + main
  • 每一层都设置flex布局
  flex:1; // 尽可能多占
  display: flex; //采用flex布局
  flex-direction:column; // 排列方式
  1. home包括header和main
  • html
 <div className="home">
      <header style={{backgroundImage:`url(${headerBg})`}}></header>
        <main>
            <section className="section1"></section>
            <section className="section2"></section>
            <section className="section3"></section>
            <section className="section4"></section>
            <section className="section5"></section>
        </main>
    </div>
  • css采用grid布局
@import "../shared/helper";
.home{
  flex:1;
  display: flex;
  flex-direction:column;
  > header{
    height:px(99);
    background-size:cover;
  }
  > main {
    flex: 1;
    display: grid;
    grid-template:
            "box1 box2 box4 box5" 755fr
            "box3 box3 box4 box5" 363fr / 366fr 361fr 811fr 747fr;
    > .section1 {
      grid-area: box1;
      background: pink;
    }
    > .section2 {
      grid-area: box2;
      background: lightgray;
    }
    > .section3 {
      grid-area: box3;
      background: lightblue;
    }
    > .section4 {
      grid-area: box4;
      background: lightcyan;
    }
    > .section5 {
      grid-area: box5;
      background: lightyellow;
    }
  }

}

RgkKht.png

背景和边框

  1. 边框:
section{
    &::before{
        content: '';
        position: absolute;
        left: 0;
        right: 0;
        bottom: 0;
        top: 0;
        border-radius: 4px;
        box-shadow: 
            17px 0 0 -16px #0e325f,
            -17px 0 0 -16px #0e325f,
            0 17px 0 -16px #0e325f,
            0 -17px 0 -16px #0e325f,
            9px 0 0 -8px #0d4483,
            -9px 0 0 -8px #0d4483,
            0 9px 0 -8px #0d4483,
            0 -9px 0 -8px #0d4483,;
    
          }
}

图表

  1. 使用echart
  • import * as echarts from "echarts";
  const divRef = useRef(null)
  useEffect(()=>{
        var myChart = echarts.init(divRef.current);
        myChart.setOption(createEchartsOptions({
            xAxis: {
            options
        }));
    },[])
    return (
        <div className="title bordered">
            <h2>title</h2>
            <div ref={divRef} className="chart"></div>
        </div>
    )
  1. section布局
  • 每个section可能包含多个个chart
  • Rg5ohq.png
  • section1包含chart1和chart2
            <section className="section1">
                <Chart1></Chart1>
                <Chart2></Chart2>
            </section>
  • css
    • flex布局
    • chart1 和chart2 竖向排列
    • space-between
> .section1 {
      grid-area: box1;
      display: flex;
      flex-direction: column;
      justify-content: space-between;
    }
  1. 每个chart
  • RgIuCt.png
  • 分为title区和chart区
  • chart1
    • 整体使用flex布局
    • title使用h2标签
        <div className="流量统计 bordered">
            <h2>流量统计</h2>
            <div ref={divRef} className="chart"></div>
        </div>
  • css
.流量统计{
      height:px(315); // 给定高度
      display:flex; // flex布局
      flex-direction:column;
      align-items:center;
      h2{                   // title区
        flex-shrink:0;
        border:1px solid #0a5299;
        border-bottom-left-radius:4px;
        border-bottom-right-radius:4px;
        font-size:px(22);
        line-height:px(24); // 行高
        padding:px(10) px(28);
        text-shadow: 0 0 px(3) white;
      }
      .chart{               //   chart区
        flex:1; //flex中能占多少占多少
        width:100%;  // 满宽
      } 
}

对echarts options的优化

  1. 使用echart要设定options,发现很多option都是重复的,本着封装的思想,消除重复
  2. 创建两个文件:
  • baseEchartOptions: 一些基本的echartOptions
  • createEchartsOptions: 处理基本的echartOptions和每个图表特定的options,做一个整合
  1. 代码
  • baseEchartOptions
import {px} from "./px";

export const baseEchartOptions ={
    title: {show: false},
    legend: {show: false},
    grid:{
        x:px(40),
        y:px(40),
        x2:px(40),
        y2:px(40)
    },
    textStyle:{
        fontSize:px(12),
        color:'#79839e'
    },
}

  • createEchartsOptions
    • options参数包括baseEchartOptions和每个图表特定的options
import {baseEchartOptions} from './base-echart-options';
import {px} from './px';

export const createEchartsOptions = (options) => {
    const result = { //解构options
        ...baseEchartOptions,
        ...options,
    };
    if (!(options?.xAxis?.axisLabel?.fontSize)) {
        result.xAxis = result.xAxis || {};
        result.xAxis.axisLabel = result.xAxis.axisLabel || {};
        result.xAxis.axisLabel.fontSize = px(12);
    }
    if (!(options?.yAxis?.axisLabel?.fontSize)) {
        result.yAxis = result.yAxis || {};
        result.yAxis.axisLabel = result.yAxis.axisLabel || {};
        result.yAxis.axisLabel.fontSize = px(12);
    }
    return result;
};

  1. 使用
  • import
import {createEchartsOptions} from "../shared/create-echarts-options";
  • 使用: 传入自己需要的options,交给createEchartsOptions做整合
useEffect(()=>{
        var myChart = echarts.init(divRef.current);
        myChart.setOption(createEchartsOptions({
            xAxis: {
               ...
            },
            yAxis: {
                ...
            },
            series: [...]
        }));
    },[])