当前位置: 首页 > 工具软件 > BizCharts > 使用案例 >

bizcharts之完全自定义tooltip(插件bx-tooltip)

刘博雅
2023-12-01

一、需求描述

1、横坐标固定写死

            横坐标固定8个,哪怕数据只有一个,但是鼠标悬浮时,没有数据的横坐标应该也没有tootip

     实现思路:

           没有数据的横坐标对应的值置为0=====》产品不同意=====》  没有数据的横坐标对应的值置为null====》解决问题

   方法:

            没有数据的横坐标对应值设置为null

2、默认的tooltip不符合需求,需要自定义

        null在自定义tooltip时,模板无法过滤掉null值

     实现思路:

            能否找带有参数的自定义tooltip模板

    方法:官网刚刚出了一个插件bx-tooltip

二、采坑小结

  1、ticks(<Chart scale/>)

     scale type的值为linear和cat是不一样的,当值为linear时,横坐标如果设置ticks时,会按照预期展示,但是如果是cat,则ticks最终的值只能是数据里面有的值,且顺序和数据中出现的顺序一致

2、显示横坐标,但是不显示tooltip

   在不需要自定义tootip(提供模板)情况下,如果希望横坐标出现,但是在tooltip中不要出现,可将对应的值置为null

3、完全自定义tootip(一个表格,多种不同的tootip)

   使用bx-tooltip插件实现完全自定义

三、完整代码

ts:代码

import React from 'react';
import { Chart, Geom, Axis, Tooltip, Legend, G2 } from 'bizcharts';
import DataSet from '@antv/data-set';
// @ts-ignore
import useCustTooltip from 'bx-tooltip';
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
// @ts-ignore
import Slider from 'bizcharts-plugin-slider';
import styles from './GroupedBoxChart.less';

interface IGropuedBoxChartProps {
  height?: number;
  data: any[];
  xAxis: string; // x轴坐标
  // yAxis: string; // y轴坐标
  boxValue: string; // 代表值的字段
  xNickName?: string;
  yNickName?: string;
  xUnitName?: string;
  yUnitName?: string;
  legendName: string; // 图例名称
  xUnitPosition?: string[];
  yUnitPosition?: string[];
  colors?: string[];
  sliderMaxShowNum?: number;
}

/**
 * 分组箱形图
 * @param props
 * @constructor
 */
const GropuedBoxChart: React.FC<IGropuedBoxChartProps> = props => {
  const {
    height = 400,
    data = [],
    xAxis,
    xNickName,
    yNickName,
    legendName = [],
    boxValue = '',
    colors,
    sliderMaxShowNum = 8,
  } = props;
  const xAxisData = [...new Set(data.map((item: any) => item[xAxis]))];
  let ds: any;
  let dv: any;
  if (xAxisData && xAxisData.length > sliderMaxShowNum) {
    const startLength = 0;
    const endLength = sliderMaxShowNum - 1;
    ds = new DataSet({
      state: {
        start: xAxisData[startLength],
        end: xAxisData[endLength],
      },
    });
    dv = ds.createView(`${xAxis}`).source(data);
    dv.transform({
      type: 'filter',
      // eslint-disable-next-line consistent-return
      callback: (obj: any): void => filterSilderData(obj, data),
    });
  } else {
    ds = new DataSet({
      state: {
        start: '',
        end: '',
      },
    });
    dv = ds.createView(`${xAxis}`);
    dv.source(data);
  }
  // eslint-disable-next-line consistent-return
  function filterSilderData(obj: any, sliderData: any[]) {
    if (xAxisData.length > sliderMaxShowNum) {
      // 获取开始节点的下标值
      const { start } = ds.state;
      const startIndex = sliderData.findIndex((value: any) => value[xAxis] === start);
      // 获取末尾节点下标值
      const { end } = ds.state;
      const endIndex = sliderData.findIndex((value: any) => value[xAxis] === end);
      if (startIndex > -1 && endIndex > -1) {
        const filterData = sliderData.slice(startIndex, endIndex + 1);
        const filterIndex = filterData.findIndex((value: any) => value[xAxis] === obj[xAxis]);
        if (filterIndex > -1) {
          return obj;
        }
      }
    } else {
      return obj;
    }
  }

  const cols: {
    [key: string]: { [key: string]: string | number | string[] | number[] | any };
  } = {};
  cols[xAxis] = {
    alias: xNickName || '',
  };
  cols[boxValue] = {
    alias: yNickName || '',
    max: 5, // GPA最大值为5
    // maxLimit: 5,
  };

  function onSlideChange(obj: { startText: any; endText: any }) {
    const { startText, endText } = obj;
    ds.setState('start', startText);
    ds.setState('end', endText);
  }

  function getCurrentColor(key: string): string {
    // 获取所有的图例信息
    const lend: string[] = [];
    data.forEach((item: any) => {
      lend.push(item[`${legendName}`]);
    });
    const lendData = [...new Set(lend)];
    const resultMap = [];
    let result: string = '';
    if (colors && colors.length > 0) {
      // 传入颜色
      for (let i = 0; i < colors.length && i < lendData.length; i += 1) {
        const obj: any = {};
        obj[`${lendData[i]}`] = colors[i % 8];
        resultMap.push(obj);
      }
    } else {
      // 未传入颜色
      for (let i = 0; i < lendData.length; i += 1) {
        const obj = {};
        obj[`${lendData[i]}`] = G2.Global.colors[i];
        resultMap.push(obj);
      }
    }

    if (resultMap && resultMap.length > 0) {
      const rr = resultMap.filter((item: any) => item[`${key}`]);
      if (rr && rr.length > 0) {
        result = rr[0][`${key}`];
      }
    }
    return result;
  }
  const [BxChart, CustTooltip] = useCustTooltip(Chart, Tooltip);
  return (
    <>
      <BxChart
        height={height}
        data={dv}
        padding="auto"
        forceFit
        scale={cols}
        className={styles.groupBoxChart}
      >
        <Axis
          name={xAxis}
          label={{
            textStyle: {
              fill: '#FFFFFF', // 文本的颜色
            },
          }}
          title={{
            offset: 20,
            textStyle: {
              textAlign: 'right',
              fill: '#FFF',
            },
            position: 'end',
          }}
        />
        <Axis
          name={boxValue}
          label={{
            textStyle: {
              fill: '#FFFFFF', // 文本的颜色
            },
          }}
          title={{
            offset: 40,
            textStyle: {
              textAlign: 'right',
              fill: '#FFF',
            },
            position: 'end',
          }}
        />
        <CustTooltip enterable triggerOn="mousemove">
          {(title: string, items: any[]) => {
            let isShow = true; // 是否显示tooltip
            const realItem = items.filter(item => item.name !== undefined);
            if (!(realItem && realItem.length > 0)) {
              isShow = false;
            }
            if (isShow) {
              return (
                <div className={styles.custTooltip}>
                  <span className={styles.title}>{title}</span> <br />
                  <div className={styles.content}>
                    <div className={styles.itemTitle}>
                      <div>最高分:</div>
                      <div>上四位分:</div>
                      <div>平均分:</div>
                      <div>下四位分:</div>
                      <div>最低分:</div>
                    </div>
                    <div className={styles.items}>
                      {items.map((it: any) => {
                        const { name, low, q1, median, q3, high } = it;
                        if (name) {
                          return (
                            <div className={styles.item}>
                              <span className={styles.name}>{name}</span>
                              <span>{high}</span>
                              <span>{q3}</span>
                              <span>{median}</span>
                              <span>{q1}</span>
                              <span>{low}</span>
                            </div>
                          );
                        }
                        return null;
                      })}
                    </div>
                  </div>
                </div>
              );
            }
            return null;
          }}
        </CustTooltip>
        <Legend
          position="top-right"
          textStyle={{
            textAlign: 'start', // 文本对齐方向,可取值为: start middle end
            fill: '#FFF', // 文本的颜色
          }}
        />
        <Geom
          type="schema"
          position={`${xAxis}*${boxValue}`}
          shape="box"
          color={[`${legendName}`, val => getCurrentColor(val)]}
          size={[...new Set(data.map(item => item[xAxis]))].length < 8 ? 38 : undefined}
          style={[
            `${legendName}`,
            {
              stroke: 'rgba(0, 0, 0, 0.45)',
              fill: (val: any) => getCurrentColor(val),
              fillOpacity: 0.8,
            },
          ]}
          adjust="dodge"
          tooltip={
            [
              `${xAxis}*${legendName}*${boxValue}`,
              (x: string, legend: string, values: string[]) => {
                if (
                  !(
                    values[0] === null &&
                    values[1] === null &&
                    values[2] === null &&
                    values[3] === null &&
                    values[4] === null
                  )
                ) {
                  return {
                    name: `${legend}`,
                    low: `${values[0]}分`,
                    q1: `${values[1]}分`,
                    median: `${values[2]}分`,
                    q3: `${values[3]}分`,
                    high: `${values[4]}分`,
                  };
                }
                return null;
              },
            ] as any
          }
        />
      </BxChart>
      {xAxisData.length > sliderMaxShowNum && (
        <Slider
          height={20}
          width="auto"
          xAxis={xAxis}
          yAxis={boxValue}
          data={data}
          onChange={onSlideChange}
          start={ds.state.start}
          end={ds.state.end}
          textStyle={{
            fontSize: '0',
          }}
          backgroundChart={{
            type: 'heatmap',
          }}
        />
      )}
    </>
  );
};

export default GropuedBoxChart;

less文件:

.groupBoxChart {
  :global {
    .bx-tooltip {
      padding: 0;
    }
  }
  .custTooltip {
    padding: 10px;
    .title {
      color: #242424;
      font-size: 14px;
    }
    .content {
      display: flex;
      flex-direction: row;
    }
    .itemTitle {
      margin-top: 28px;
    }
    .items {
      display: flex;
      flex-direction: row;
      .item {
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        padding: 8px;
        .name {
          color: #242424;
          font-size: 14px;
        }
      }
    }
  }
}

四、资源地址:

1、npm 路径:https://www.npmjs.com/package/bx-tooltip

2、在线demo:https://codesandbox.io/s/stoic-lovelace-hv5z2?file=/src/App.js:488-493

     

 类似资料: