React + ECharts 数据可视化实战 第二章:基础图表开发

276 阅读4分钟

第二章:基础图表开发


​2.1 柱状图开发全流程​

​1. 基础实现​

// 柱状图组件
const BarChart = () => {
  const chartRef = useRef<HTMLDivElement>(null);
  const [data] = useState<number[]>([120, 200, 150, 80, 70]);

  useEffect(() => {
    const chart = echarts.init(chartRef.current!);
    const option: EChartsOption = {
      grid: { left: '15%', right: '10%' },
      xAxis: {
        type: 'category',
        data: ['周一', '周二', '周三', '周四', '周五'],
        axisLabel: {
          color: '#666',
          rotate: 45 // 标签倾斜防重叠
        }
      },
      yAxis: {
        type: 'value',
        splitLine: { show: true, lineStyle: { type: 'dashed' } }
      },
      series: [{
        name: '销量',
        type: 'bar',
        data,
        itemStyle: {
          color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
            { offset: 0, color: '#83bff6' },
            { offset: 1, color: '#188df0' }
          ])
        },
        emphasis: {
          itemStyle: { shadowBlur: 10, shadowColor: 'rgba(0,0,0,0.5)' }
        }
      }]
    };
    chart.setOption(option);
    return () => chart.dispose();
  }, [data]);

  return <div ref={chartRef} style={{ width: '100%', height: 400 }} />;
};

​2. 进阶功能​

  • ​动态数据更新​​:结合WebSocket实现实时刷新

    const [realTimeData, setRealTimeData] = useState<number[]>([]);
    
    useEffect(() => {
      const ws = new WebSocket('wss://api.example.com/sales');
      ws.onmessage = (e) => {
        setRealTimeData(prev => [JSON.parse(e.data), ...prev].slice(0, 5));
      };
      return () => ws.close();
    }, []);
    
  • ​多系列对比​

    series: [
      { name: '线上', type: 'bar', data: [80, 150, 120, 90, 110] },
      { name: '线下', type: 'bar', data: [40, 80, 60, 30, 50] },
      {
        name: '目标线',
        type: 'line',
        markLine: {
          data: [{ yAxis: 100 }],
          lineStyle: { color: 'red', type: 'dotted' }
        }
      }
    ]
    

​3. 性能优化​

  • ​虚拟滚动​​:处理万级数据时启用

    series: [{
      type: 'bar',
      large: true,
      largeThreshold: 2000,
      progressive: 1000,
      progressiveThreshold: 5000
    }]
    
  • ​防抖更新​​:防止频繁数据变更导致的重绘

    const debouncedSetOption = useCallback(debounce((chart, option) => {
      chart.setOption(option);
    }, 300), []);
    

​2.2 折线图高阶应用​

​1. 多维度展示​

series: [
  {
    name: '温度',
    type: 'line',
    yAxisIndex: 0,
    data: [22, 24, 26, 23, 25],
    markArea: {
      data: [[{name: '高温', xAxis: '周一'}, {xAxis: '周三'}]] // 区域标注
    }
  },
  {
    name: '湿度',
    type: 'line',
    yAxisIndex: 1,
    data: [55, 60, 65, 58, 62],
    yAxis: {
      type: 'value',
      min: 0,
      max: 100,
      axisLabel: { formatter: '{value}%' }
    }
  }
]

​2. 动画配置​

animation: {
  duration: 1000, // 动画时长
  easing: 'cubicInOut' // 缓动效果
},
animationDelay: (idx) => idx * 100, // 逐个动画延迟
animationEasingUpdate: 'quinticInOut' // 更新动画效果

​3. 交互增强​

  • ​数据区域缩放​

    dataZoom: [{
      type: 'slider',
      start: 0,
      end: 60,
      handleSize: '80%'
    }, {
      type: 'inside',
      start: 0,
      end: 60
    }]
    
  • ​自定义提示框​

    tooltip: {
      trigger: 'axis',
      formatter: (params) => {
        const date = new Date(params[0].axisValue);
        return `
          <div>${date.toLocaleDateString()}</div>
          ${params.map(p => `
            <div style="color:${p.color}">${p.marker}${p.seriesName}: ${p.value}</div>
          `).join('')}
        `;
      }
    }
    

​2.3 饼图与地图专项​

​1. 饼图优化技巧​

  • ​环形图+图例联动​

    series: [{
      type: 'pie',
      radius: ['55%', '70%'], // 环形效果
      label: {
        position: 'outside',
        formatter: '{b}\n{c} ({d}%)'
      },
      labelLine: {
        length: 20,
        length2: 30
      },
      data: [
        {value: 335, name: '直接访问'},
        {value: 310, name: '邮件营销'},
        {value: 234, name: '联盟广告'},
        {value: 135, name: '视频广告'},
        {value: 1548, name: '搜索引擎'}
      ]
    }]
    
  • ​动态占比动画​

    animationEasing: 'cubicInOut',
    animationDelay: (idx) => Math.random() * 200
    

​2. 地图开发全流程​

  • ​中国地图基础配置​

    import 'echarts/map/js/china'; // 引入中国地图数据
    
    series: [{
      type: 'map',
      map: 'china',
      roam: true, // 允许缩放平移
      label: {
        show: true,
        color: '#fff'
      },
      itemStyle: {
        areaColor: '#323c48',
        borderColor: '#111'
      },
      emphasis: {
        label: { show: false },
        itemStyle: { areaColor: '#2a333d' }
      },
      data: [
        {name: '北京', value: 100},
        {name: '上海', value: 200},
        // 其他省份数据
      ]
    }]
    
  • ​自定义地理坐标​

    geo: {
      map: 'custom',
      roam: true,
      itemStyle: { areaColor: '#eee' },
      regions: [{
        name: '重点区域',
        itemStyle: { areaColor: 'rgba(255,0,0,0.3)' },
        label: { show: false }
      }]
    }
    

​2.4 图表联动与组合​

​1. 多图表联动​

// 主图表点击触发副图表更新
const handleMainChartClick = (params: any) => {
  setFilterData(params.name);
};

// 在副图表配置中绑定过滤条件
series: [{
  data: filteredData.map(item => item.value)
}]

​2. 组合图表布局​

grid: [
  { left: '5%', top: '10%', height: '30%' }, // 柱状图区域
  { left: '5%', top: '50%', height: '30%' }  // 折线图区域
],

​3. 响应式适配​

useEffect(() => {
  const resizeObserver = new ResizeObserver(() => {
    chart.resize();
  });
  resizeObserver.observe(chartRef.current!);
  return () => resizeObserver.disconnect();
}, []);

​2.5 企业级开发规范​

​1. 主题定制方案​

// 自定义主题文件 theme.js
export default {
  color: ['#5470C6', '#91CC75', '#EE6666', '#FAC858', '#73C0DE'],
  backgroundColor: 'transparent',
  textStyle: { fontFamily: 'PingFang SC, Microsoft YaHei' },
  title: { textStyle: { fontWeight: 'bold' } }
};

// 初始化时应用主题
const chart = echarts.init(dom, theme);

​2. 异常处理机制​

useEffect(() => {
  try {
    const chart = echarts.init(chartRef.current!);
    // ...初始化逻辑
  } catch (error) {
    console.error('[ECharts Error]', error);
    Sentry.captureException(error); // 上报错误监控
  }
}, []);

​3. 代码规范示例​

// 类型定义
interface ChartConfig {
  title?: string;
  xAxisData: string[];
  seriesData: number[];
}

// 组件Props
type BarChartProps = {
  config: ChartConfig;
  height?: number;
  style?: React.CSSProperties;
};

​2.6 性能调优实战​

​1. 大数据量处理方案​

数据量级优化方案效果
1k-1wCanvas渲染保持流畅
1w-10w数据采样 + 分段加载首屏加载优化
10w+WebGL渲染 + 虚拟滚动高性能展示

​2. 内存泄漏预防​

// 使用useRef保存实例
const chartRef = useRef<echarts.ECharts | null>(null);

useEffect(() => {
  chartRef.current = echarts.init(container);
  return () => {
    if (chartRef.current) {
      chartRef.current.dispose();
      chartRef.current = null;
    }
  };
}, []);

​3. Web Worker应用​

// worker.ts
self.onmessage = (e) => {
  const processedData = heavyDataProcessing(e.data);
  self.postMessage(processedData);
};

// 主线程
const worker = new Worker('./worker.ts');
worker.onmessage = (e) => {
  setChartData(e.data);
};

​2.7 常见问题解决方案​

​1. 图表不显示排查清单​

  1. 检查容器尺寸是否为0
  2. 确认数据格式是否正确(数组/对象)
  3. 验证ECharts初始化时机(DOM加载完成)
  4. 检查控制台错误信息

​2. 中文字体缺失处理​

// 在public/index.html添加
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@400;700&display=swap">

// 在CSS中应用
.echarts {
  font-family: 'Noto Sans SC', sans-serif !important;
}

​3. 移动端适配方案​

// 启用响应式布局
const resizeHandler = () => {
  if (chart) {
    chart.resize();
    chart.dispatchAction({ type: 'resize' });
  }
};

// 监听页面尺寸变化
useEffect(() => {
  window.addEventListener('resize', resizeHandler);
  return () => window.removeEventListener('resize', resizeHandler);
}, []);