Echarts柱状极坐标图实现风向玫瑰图

Echarts柱状极坐标图实现风向玫瑰图,第1张

Echarts柱状极坐标风向玫瑰图的实现 echarts代码

本来看到玫瑰图,就看到了echarts示例中饼图的南丁格尔玫瑰图。

发现不太像。后来发现是柱状图极坐标系堆叠图。
一开始用了多个series,图例是能实现,但是显示的图有问题。柱太细了数据一多根本看不见。
使用一个series,图例又没办法绑定到数据。

还好图例比较简单,决定直接自己写一个。

eharts的配置项真的太难了。找的很头疼,实现的也很头疼。

一辈子不想写图表了。

图例的实现

首先根据数据生成一个title数组,因为要判断当前点击和未点击,设置一个flag参数,默认值为1。表示彩色的是未点击。
接下来就是颜色绑定。echarts配置项colors是一个颜色数组。数据会依次从中取色。超过就从头再来。
但是经过测试发现,colors数组的长度最多是16个。超过16个后就继续重头开始了。

然后就是点击图例icon,先判断flag,来修改icon颜色,然后获取到点击的index,来设置点击的那个值为0。再次点击从原始的数据根据index中获取到对应的值在赋上去就好了。然后就是图例颜色的判断。超过16个从头取。
然后就是图例的分页,我这一页显示12个,根据长度判断一下页数,点击下一页加一,上一页减一,正常的分页。没什么难的。

主要就是配置项比较烦。一定要到echarts配置项中一个一个找。

数据中的方位和极坐标的方位对准。series中data,是个二维数组,第一个表示数据,第二个就表示,对应的angleAxis的下标。
这边根据值,通过findIndex从数组中取一下就好了。

**这边注意:**我一开始用的filter和splice。这样虽然可以实现,但是会改变数组长度,图表的tooltip取得数据的下标,改变了会有问题。

效果如下:



/*
 * @Author: shimmer
 * @Date: 2022-05-11 17:09:11
 * @LastEditors: shimmer
 * @LastEditTime: 2022-05-19 15:31:09
 * @Description:
 *
 */
import React, {FC, useEffect, useState} from 'react';
import ReactECharts from 'echarts-for-react';
import styles from '../styles.module.scss';
import {color, pathArr} from '../constant';

interface Title {
  title: string;
  flag: number;
}

const EchartsPageB: FC<any> = ({picData}) => {
  const [titleArr, setTitleArr] = useState<Title[]>([]);
  const [echartsData, setEchartsData] = useState<any[]>([]);
  const [page, setPage] = useState<number>(1);
  const [currentPage, setCurrentPage] = useState<number>(1);

  useEffect(() => {
    setEchartsData(picData.series);
    const count = picData.series.length % 12;
    if (count === 0) {
      setPage(picData.series.length / 12);
    } else {
      const str = String(picData.series.length / 12);
      setPage(Number(parseInt(str, 10)) + 1);
    }
    setTitleArr(picData.series.map((item: Title) => ({title: item.title, flag: 1})));
  }, [picData]);

  const option = {
    grid: {
      left: 50,
      right: 50,
      bottom: 0,
    },
    angleAxis: {
      type: 'category',
      axisTick: false,
      boundaryGap: false,
      splitLine: {
        show: true,
        lineStyle: {
          color: '#8d979d',
        },
      },
      axisLabel: {color: '#8d979d'},
      data: pathArr,
    },
    tooltip: {
      trigger: 'item',
      position(pos: number[], params: any, dom: any, rect: any, size: any) {
        // 鼠标在左侧时 tooltip 显示到右侧,鼠标在右侧时 tooltip 显示到左侧。
        interface Obj {
          left: string | number;
          top: number | string;
          bottom: number | string;
        }
        const obj: Obj = {left: '', top: '', bottom: ''};
        const count: boolean = pos[1] < size.viewSize[1] / 2;
        if (count) {
          obj.bottom = 10;
        } else {
          obj.top = 10;
        }
        obj.left = pos[0] < size.viewSize[0] / 2 ? pos[0] : pos[0] - size.contentSize[0];
        return obj;
      },
      backgroundColor: 'rgba(1,9,13,0.7)',
      borderWidth: 1,
      borderColor: '#2E3C44',
      formatter: (params: any) => {
        const data = picData.series[params.dataIndex].tips[0];
        const infoHtml = `
            风向玫瑰图
            ${styles.tooltipHr}>
            ${styles.styleA}>
              ${styles.styleE}>风向:
              ${styles.styleB}>
                ${styles.styleC}>${data[0].path}
                ${styles.styleD}>
              
            
            ${styles.styleA}>
              ${styles.styleE}>风速区间:
              ${styles.styleB}>
                ${styles.styleC}>${data[2].value}
                ${styles.styleD}>m/s
              
              
            ${styles.styleA}>
              ${styles.styleE}>频次:
              ${styles.styleB}>
               ${styles.styleC}>${data[0].value}
               ${styles.styleD}>次
                
            
            ${styles.styleA}>
              ${styles.styleE}>频次占比:
              ${styles.styleB}>
               ${styles.styleC}>${data[3].value}
               ${styles.styleD}>%
               
            
           
          `;

        return infoHtml;
      },
    },
    polar: {
      radius: '80%',
      center: ['42%', '50%'],
    },
    radiusAxis: {},
    series: [
      {
        type: 'bar',
        data: echartsData.map((item: any) => {
          const pathIndex = pathArr.findIndex((k) => k === item.tips[0][0].path);
          const newValue = item.values[0];
          const dataArr = [newValue, pathIndex];
          return dataArr;
        }),
        coordinateSystem: 'polar',
        colorBy: 'data',
        stack: 'path',
      },
    ],
    color,
  };

  const changeLegend = (item: Title, index: number) => {
    if (item.flag === 1) {
      const newTitleArr = titleArr;
      newTitleArr[index].flag = 0;
      setTitleArr(newTitleArr);

      const newArr = echartsData.map((k: any, j: number) => {
        const obj = {...k};
        if (index === j) {
          obj.values = [0];
        }
        return obj;
      });
      setEchartsData(Object.assign([...newArr]));
    } else if (item.flag === 0) {
      const newTitleArr = titleArr;
      newTitleArr[index].flag = 1;
      setTitleArr(newTitleArr);
      const arr = picData.series.filter((k: any) => k.title === item.title);
      const newArr = echartsData.map((x: any, j: number) => {
        const obj = {...x};
        if (index === j) {
          obj.values = arr[0].values;
        }
        return obj;
      });
      setEchartsData(Object.assign([...newArr]));
    }
  };
  let count = -1;
  return (
    <div className={styles.windRose}>
      <div className={styles.title}>
        <span className={styles.dot} />
        <span className={styles.stationName}>{picData.title}</span>
      </div>
      <ReactECharts option={option} style={{height: '400px'}} />
      <div className={styles.legend}>
        {titleArr.map((item: Title, index: number) => {
          if (count === 15) {
            count = -1;
          }
          count += 1;
          if (index + 1 > (currentPage - 1) * 12 && index + 1 <= currentPage * 12) {
            return (
              <div className={styles.legendItem} key={item.title}>
                <span
                  className={styles.legendIcon}
                  style={{background: item.flag ? color[count] : '#6c7379'}}
                  onClick={() => {
                    changeLegend(item, index);
                  }}
                />
                <span className={styles.legendName}>{item.title}</span>
              </div>
            );
          }
          return '';
        })}
        <div className={styles.changePage}>
          <span
            className={styles.changePageA}
            onClick={() => {
              if (currentPage === 1) {
                return;
              }
              setCurrentPage(currentPage - 1);
            }}>
            {'<'}
          </span>
          <span className={styles.changePageB}>{currentPage}</span>
          <span className={styles.changePageC}>{`/ ${page}`}</span>
          <span
            className={styles.changePageD}
            onClick={() => {
              if (currentPage === page) {
                return;
              }
              setCurrentPage(currentPage + 1);
            }}>
            {'>'}
          </span>
        </div>
      </div>
    </div>
  );
};

export default EchartsPageB;


constant代码
export const pathArr = ['N', 'NNE', 'NE', 'ENE', 'E', 'ESE', 'SE', 'SSE', 'S', 'SSW', 'SW', 'WSW', 'W', 'WNW', 'NW', 'NNW'];
export const color = [
  '#5470c6',
  '#91aa75',
  '#fac858',
  '#ee6666',
  '#73c0de',
  '#3bd272',
  '#fc8452',
  '#9a60b4',
  '#ea7ccc',
  '#f00a5d',
  '#9a60b4',
  '#e07ccc',
  '#df0a1a',
  "#c14cac",
  '#f08300',
  '#91ee75',
];
样式代码
  .windRose {
    display: flex;
    position: relative;
    flex-direction: column;
    width: 32%;
    margin-bottom: 10px;
    .title {
      background-color: #0a445a;
      display: flex;
      align-items: center;
      border-radius: 7px;
      height: 30px;
      margin-bottom: 10px;
      .dot {
        background-color: #097488;
        margin-left: 10px;
        margin-right: 10px;
        border-radius: 2px;
        width: 5px;
        height: 17px;
      }
      .stationName {
        font-size: 17px;
      }
    }
    .legend {
      position: absolute;
      right: 0px;
      top: 60px;
      display: flex;
      flex-direction: column;
      width: 110px;
      height: 350px;
      overflow: hidden;
      .legendItem {
        display: flex;
        width: 110px;
        align-items: center;
        justify-content: space-between;
        margin-bottom: 7px;
        .legendIcon {
          width: 12px;
          height: 12px;
          border-radius: 2px;
          cursor: pointer;
        }
        .legendName {
          color: #fff;
          width: 80px;
        }
      }
      .changePage {
        display: flex;
        align-items: center;
        justify-content: space-between;
        height: 30px;
        width: 100%;
        position: absolute;
        bottom: 0;
        right: 0;
        background-color: #0a445a;
        .changePageA {
          width: 25px;
          height: 25px;
          text-align: center;
          border: 1px solid #00e4ff;
          border-radius: 4px;
          color: #00e4ff;
          cursor: pointer;
        }
        .changePageB {
          width: 25px;
          height: 25px;
          text-align: center;
          border: 1px solid #00e4ff;
          border-radius: 4px;
          color: #fff;
        }
        .changePageC {
          color: #fff;
        }
        .changePageD {
          width: 25px;
          height: 25px;
          text-align: center;
          border: 1px solid #00e4ff;
          border-radius: 4px;
          color: #00e4ff;
          cursor: pointer;
        }
      }
    }
  }

  .echartsBox {
    overflow-y: scroll;
    height: calc(100% - 48px);
    .windFrqBox {
      display: flex;
      flex-wrap: wrap;
      justify-content: space-between;
    }
    .windRoseBox {
      display: flex;
      flex-wrap: wrap;
      justify-content: space-between;
    }
  }



.tooltipHr {
  background: #4c555a;
  width: 250px;
  height: 2px;
  margin-bottom: 15px;
}

.styleA {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 0 9px 0 9px;
  margin-bottom: 10px;
}
.styleE {
  color: #fff;
  font-size: 14px;
  font-weight: bold;
}

.styleB {
  display: flex;
  align-items: center;
}
.styleC {
  color: #00e4ff;
  font-size: 14px;
  margin-right: 8px;
}

.styleD {
  color: #6c7379;
  width: 30px;
}

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/1321311.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-12
下一篇 2022-06-12

发表评论

登录后才能评论

评论列表(0条)

保存