likes
comments
collection
share

你不知道的Echarts的奇技淫巧

作者站长头像
站长
· 阅读数 4

开场诗

-荆溪白石出,

- 天寒红叶稀。

- 山路元无雨。

- 空翠湿人衣。

一、序言

大家好,这里是流光的频道,又到了和大家分享的时间,最近公司在写大屏可视化的项目,在工作中遇到了一个比较奇葩的要求,再写代码过程中也遇到了很多坑,现在来和大家分享一下。

最终效果

你不知道的Echarts的奇技淫巧

效果图.gif

1.具体需求

因为是测试用例所以有的数据没有补全大家只看效果就行不用在意数据。 功能大体是这样的: 1.点击周、月、年不同的按钮展现不同的数据,周是当前周,月是上个月25号到今天,年是这一年的12个月。 2.数据由主矿、配矿、焦炭三部分组成,其中主矿和配矿是展示在一条柱状图上用不同的颜色按比例展示,并且在柱状图上方展示他们两个值的和。 3.当点击月按钮时因为展示的是25号到当天数据太多做一个滑动效果,并且确保最右边展示的是当前天的数据。

当时接到这个需求的时候一头问号,大屏还能这么搞???虽然和产品经理进行了据理力争但是最终还是拗不过甲方爸爸。

你不知道的Echarts的奇技淫巧

二、解决思路

1.周、月、年点击切换数据好搞就是用useState存储数据特点击切换的时候更改数据就可以,至于时间范围根据后端返回的就可以。 2.按比例展示经过对echarts的学习发现series对象里的stack属性可以办到至于展示两个的和我可以在后面设置一个透明的柱状图来进行计算。 3.滑动效果的话可以用dataZoom组件即可。 既然方案已经确定那直接开干!!!

三、具体实现

1.周、月、年按钮和主题框架的实现

 const AlarmLight = props => {
  const [elect, setElect] = useState(null);
  const [type, setType] = useState([]);
  const [value, setValue] = useState(null);
  const [week, setWeek] = useState([]);
  const [mouth, setMouth] = useState([]);
  const [year, setYear] = useState([]);
  const [weekvalue, setWeekvalue] = useState();
  const [monthvalue, setMonthvalue] = useState([13020.5, 11050.2, 14800.6, 15002.6, 19200.7]); //
  const [yearvalue, setYearvalue] = useState([132000.5, 115000.2, 148000.6, 152000.6, 192000.7]);
  const [weekback, setWeekback] = useState(true);
  const [mouthback, setMouthback] = useState(false);
  const [yearback, setYearback] = useState(false);
  const [chartKey, setChartKey] = useState(0); // 用于强制重新挂载组件
   
  const showModalweek = () => {
    setType(week);
    setValue(weekvalue);
    setWeekback(true);
    setMouthback(false);
    setYearback(false);
  };
  const showModalmouth = () => {
    setType(mouth);
    setValue(monthvalue);
    setWeekback(false);
    setMouthback(true);
    setYearback(false);
  };
  const showModalyear = () => {
    setType(year);
    setValue(yearvalue);
    setWeekback(false);
    setMouthback(false);
    setYearback(true);
  };
  //   const onEvents = {
  //     click: onChartClick
  //   };
  useEffect(() => {
    warehouseScreenService
      .gettopValue()
      .then(res => {
        console.log(res);
        setWeekvalue(res.electric);
        setMonthvalue(res.electricmonth);
        setYearvalue(res.electricyear);
        setType(res.electrictime);
        setWeek(res.electrictime);
        
        setMouth(res.electrictimemonth);
        setYear(res.electrictimeyear);
        setValue(res.electric);
        // setElect(res.electric);
      })
      .catch(err => {
        notification.warning({
          description: err.message
        });
      });
  }, []);
  useEffect(() => {
    // 通过改变 key 强制 ReactECharts 重新挂载
    setChartKey(prevKey => prevKey + 1);
  }, [value]);
  return (
    <>
      <ReactECharts
        option={getDailyProductionChart(value, type)}
        style={{ width: '100% ', height: '100%', marginTop: '3%' }}
        key={chartKey} // 使用 key 强制重新挂载
        // onEvents={onEvents}
      ></ReactECharts>
      <div className={style2.topleftmy}>
        <Button
          key="save"
          onClick={showModalweek}
          className={`textButton ${weekback ? 'textButton2' : ''}`}
        >

        </Button>
        <Button
          key="save"
          onClick={showModalmouth}
          className={`textButton ${mouthback ? 'textButton2' : ''}`}
        >

        </Button>
        <Button
          key="save"
          onClick={showModalyear}
          className={`textButton ${yearback ? 'textButton2' : ''}`}
        >

        </Button>
      </div>
    </>
  );
};

特殊事项

1.点击按钮变换颜色:我采用的是通过一个变量值的true和false来切换他的css如上图所示css代码如下

.textButton {
    height: 50%;
    /* background-color: #1B3059 !important; */
    background-color: #586A92 !important;
    opacity: 0.5;
    
    color: white !important;
    border: 1px solid white;
    .ant-btn{
        background-color: '#1B3059' !important;
      }
  }

  .textButton2 {
    height: 50%;
    /* background-color: #1B3059 !important; */
    background-color: #153B73 !important;
    opacity: 0.5;
    font-weight: bold;
    color: white !important;
    border: 1px solid white;
    .ant-btn{
        background-color: '#1B3059' !important;
      }
  }

2.这里有个细节是当我点击按钮的时候他并不会切换值也就是常说的组件不会刷新: 这是因为在React中,当组件的状态或属性发生变化时,React通常会尝试在虚拟DOM中更新组件,而不是将其完全卸载然后重新挂载,以提高性能。这样的更新方式可以更高效地实现页面更新,因为它避免了不必要的卸载和重新挂载过程。这也就导致我传输的值更改之后他因为我并没有进行组建的更也就不会重新夹加载所以我通过设置key来实现他的强制重新加载,具体代码如上图所示。

2.柱状图按比例分配

通过重写时间框的ranges方法自定义时间范围

  const option = {
    backgroundColor: '',
    tooltip: {
      trigger: 'axis',
      axisPointer: {
        lineStyle: {
          color: {
            type: 'linear',
            x: 0,
            y: 0,
            x2: 0,
            y2: 1,
            colorStops: [
              {
                offset: 0,
                color: 'rgba(0, 255, 233,0)'
              },
              {
                offset: 0.5,
                color: 'rgba(255, 255, 255,1)'
              },
              {
                offset: 1,
                color: 'rgba(0, 255, 233,0)'
              }
            ],
            global: false
          }
        }
      },
      renderMode: 'html',
      backgroundColor: 'rgba(15, 45, 83, 0.9)',
      borderWidth: 0,
      borderColor: 'rgba(15, 45, 83, 0.9)',
      textStyle: {
        color: '#fff'
      }
    },
    grid: {
      top: '30%',
      left: '13%',
      right: '12%',
      bottom: '10%',
      containLabel: false
    },
    xAxis: [
      {
        type: 'category',
        // name: '(月)',
        axisLine: {
          show: true,
          lineStyle: {
            color: '#d1e6eb'
          }
        },
        axisLabel: {
          // 设置 interval
          interval: type != null ? (type.length <= 12 ? 0 : 'auto') : 0,
          color: '#d1e6eb'
        },
        splitArea: {
          color: '#f00',
          lineStyle: {
            color: '#f00'
          }
        },
        splitLine: {
          show: false
        },
        axisTick: {
          show: false
        },
        boundaryGap: true,
        data: type
      }
    ],

    yAxis: {
      type: 'value',
      name: '(吨)',
      splitNumber: 4,
      axisLine: {
        show: false, //y轴竖线去掉
        lineStyle: {
          color: '#d1e6eb'
        }
      },
      axisLabel: {
        show: true,
        textStyle: {
          color: '#d1e6eb'
        }
      },
      axisTick: {
        show: false
      },
      splitLine: {
        lineStyle: {
          type: 'dashed',
          color: '#39608F'
        }
      }
    },
    legend: {
      data: ['主矿', '配矿', '焦炭'],
      // right: 210, // 图例距离右边界10像素
      icon: 'path://M30 10 L50 30 L30 50 L10 30 Z', // 自定义图例图标为菱形
      top: 50, // 图例距离上边界10像素
      textStyle: {
        color: 'white'
      }
    },
    series: [
      {
        name: '主矿',
        type: 'bar',
        stack: '总量',
        barGap: '0%', // 将间隔设置为 0%
        barWidth: '15%',
        // stack: 'Total',
        symbol: 'diamond',
        label: {
          show: false,
          position: 'top',
          textStyle: {
            color: '#fff'
          }
        },
        smooth: true, //平滑
        lineStyle: {
          width: 2,
          color: '#6EE1FA'
        },
        emphasis: {
          focus: 'series'
        },
        data: value
      },

      {
        name: '配矿',
        type: 'bar',
        smooth: false,

        stack: '总量',
        symbol: 'diamond',
        barWidth: '15%',
        barGap: '0%', // 将间隔设置为 0%
        // barGap: '80%', // 增大间隔
        // barCategoryGap: '2%',
        symbolSize: 10,
        lineStyle: {
          normal: {
            color: '#6EEBA1',
            // shadowColor: 'rgba(0, 0, 0, .3)',
            shadowBlur: 0
            // shadowOffsetY: 5,
            // shadowOffsetX: 5
          }
        },
        label: {
          show: false,
          position: 'inside',
          textStyle: {
            color: '#fff'
          }
        },

        itemStyle: {
          color: '#CC7D63',
          borderColor: '#fff',
          borderWidth: 0
        },
        areaStyle: {
        },
        data: value2
      },
      {
        name: '总矿数',
        type: 'bar',
        barGap: '0%', // 将间隔设置为 0%
        //barGap: '0%', // 将间隔设置为 0%
        // stack: '总量',
        // stack: '总量',
        itemStyle: {
          color: 'rgba(0,0,0,0)' // 完全透明
        },
        smooth: false,
        symbol: 'diamond',
        barWidth: '5%',
        // barGap: '2%',
        // barGap: '10%', // 增大间隔
        // barCategoryGap: '10%',
        tooltip: {
          show: false // 鼠标悬停时不显示 tooltip
        },
        symbolSize: 10,
        label: {
          show: true,
          position: 'top',
          offset: [-10, 0], // 可能需要调整这里的值
          textStyle: {
            color: '#fff'
          }
        },
        areaStyle: {
        },
        data: sumalldata
      },
      {
        name: '焦炭',
        type: 'bar',
        smooth: false,
        symbol: 'circle',
        barGap: '0%', // 将间隔设置为 0%
        barWidth: '15%',
        // barGap: '80%', // 增大间隔
        // barCategoryGap: '10%',
        symbolSize: 10,
        lineStyle: {
          normal: {
            color: '#CC7D63',
            shadowBlur: 0
          }
        },
        label: {
          show: true,
          position: 'top',
          // offset: [0, -30], // 向上提 10px
          textStyle: {
            color: '#fff'
          }
        },

        itemStyle: {
          // color: '#CC7D63',
          color: '#6EEBA1',

          borderColor: '#fff',
          borderWidth: 0
          // shadowColor: 'rgba(0, 0, 0, .3)',
          // shadowBlur: 0,
          // shadowOffsetY: 6,
          // shadowOffsetX: 2
        },
        areaStyle: {
        },
        data: value3
      }
    ]
  };

通过stack属性为两个柱形图设置一样的stack属性他们会自动按比分配,并多设置一个series把他的值计算成两个柱子的和并把barGap设置为0即可。

2.zoom组件的使用

先安装zomm组件下面是他的一些基础属性,其中的startValue和endValue属性是为了配合我接下来展示效果设置的值,默认是可以为0。而且我这是修改了样式把滚动条隐藏,大家可以直接复制即可。

       option.dataZoom = [
        //给x轴设置滚动条
        {
          type: 'slider', //slider表示有滑动块的,inside表示内置的
          startValue: listoverstart, //可用于设置开始显示的柱子的长度
          endValue: listoverend, //可用于设置结束显示的柱子的长度
          // start: 0, //默认为0  可设置滚动条从在后进行展示
          // end: 20, //默认为100
          show: true,
          xAxisIndex: [0],
          handleSize: 0, //滑动条的 左右2个滑动条的大小
          height: 2, //组件高度
          left: '5%', //左边的距离
          right: '5%', //右边的距离
          bottom: -2, //右边的距离
          borderColor: '#eee',
          fillerColor: '#E7E7E7',
          backgroundColor: '#eee', //两边未选中的滑动条区域的颜色
          showDataShadow: false, //是否显示数据阴影 默认auto
          showDetail: false, //即拖拽时候是否显示详细数值信息 默认true
          realtime: true, //是否实时更新
          filterMode: 'filter',
          handleStyle: {
            borderRadius: '20'
          }
        },
        //下面这个属性是里面拖到
        {
          type: 'inside',
          show: true,
          xAxisIndex: [0],
          start: 0, //默认为1
          end: 100, //默认为100
          moveOnMouseWheel: false,
          preventDefaultMouseMove: false
        }
      ];

接下来就剩下根据当前日期展示把今天的数据放在最右边这一部分我是通过一个函数解决的代码如下:

你不知道的Echarts的奇技淫巧

当获取的数据大于12条也就是上月25号到今天的时间大于12天,通过获取当天的日期因为我是展示从上月25到本月24的数据所以我通过计算当前日期是否大于25如果大于则展示6条数据不做处理,若小于25则根据具体时间截取我要展示的数据通过更改startValue和endValue属性可以达到效果。

随想

这也是我在工作中根据遇到的问题想出来的可能不是最好的解决方法,如果大家有更好的解决方法,欢迎在下方留言