带有 react-chartjs-2 库的自定义工具提示

var*_*run 7 chart.js

我对 chartjs 提供的默认工具提示有问题,因为我无法在工具提示中添加 html。我一直在研究如何在工具提示中添加 html/jsx。我在这里看到一个使用自定义工具提示的示例Chart JS Show HTML in Tooltip。有人可以给我举个例子,如何使用 react-chartjs-2 库实现相同的目标?

kum*_*rsh 7

您必须使用customtooltip 属性中的回调来定义自己的定位并将悬停数据集设置为组件状态

state = {
  top: 0,
  left: 0,
  date: '',
  value: 0,
};

_chartRef = React.createRef();

setPositionAndData = (top, left, date, value) => {
  this.setState({top, left, date, value});
};

render() {
  chartOptions = {
    "tooltips": {
      "enabled": false,
      "mode": "x",
      "intersect": false,
      "custom": (tooltipModel) => {
        // if chart is not defined, return early
        chart = this._chartRef.current;
        if (!chart) {
          return;
        }

        // hide the tooltip when chartjs determines you've hovered out
        if (tooltipModel.opacity === 0) {
          this.hide();
          return;
        }

        const position = chart.chartInstance.canvas.getBoundingClientRect();

        // assuming your tooltip is `position: fixed`
        // set position of tooltip
        const left = position.left + tooltipModel.caretX;
        const top = position.top + tooltipModel.caretY;

        // set values for display of data in the tooltip
        const date = tooltipModel.dataPoints[0].xLabel;
        const value = tooltipModel.dataPoints[0].yLabel;

        this.setPositionAndData({top, left, date, value});
      },
    }
  }

  return (
    <div>
      <Line data={data} options={chartOptions} ref={this._chartRef} />
      { this.state.showTooltip
        ? <Tooltip style={{top: this.state.top, left: this.state.left}}>
            <div>Date: {this.state.date}</div>
            <div>Value: {this.state.value}</div>
          </Tooltip>
        : null
      }
    </div>
  );
}
Run Code Online (Sandbox Code Playgroud)

您可以使用React Popper Tooltip提供的工具提示或滚动您自己的工具提示- 将top和传递left给工具提示以进行定位,并且datevalue(在我的示例中)应该用于在工具提示中显示数据。

  • 很好的答案,但请更新它以包含左侧的“pageXOffset”和顶部的“pageYOffset”,否则它不会考虑滚动。react-chartjs 上的文档也没有考虑到这一点。`const left = position.left + pageXOffset + tooltipModel.caretX;` `const top = position.top + pageYOffset + tooltipModel.caretY;` (4认同)

小智 5

如果有人在寻找工具提示和渐变图自定义的答案,这里是我的代码:

我的包裹:

"react": "^17.0.2"
"chart.js": "^3.7.1"
"react-chartjs-2": "^4.1.0"
"tailwindcss": "^3.0.23"
Run Code Online (Sandbox Code Playgroud)

顶部提示组件:

import React, { memo } from "react";
import { monetarySuffix } from "@src/helpers/util";
// tooltip.js
const GraphTooltip = ({ data, position, visibility }) => {
  return (
    <div
      className={`absolute px-4 py-3.5 rounded-lg shadow-lg bg-chart-label-gradient text-white overflow-hidden transition-all duration-300 hover:!visible
      ${visibility ? "visible" : "invisible"}
        `}
      style={{
        top: position?.top,
        left: position?.left,
      }}
    >
      {data && (
        <>
          <h5 className="w-full mb-1.5 block text-[12px] uppercase">
            {data.title}
          </h5>

          <ul className="divide-y divide-gray-100/60">
            {data.dataPoints.map((val, index) => {
              return (
                <li
                  key={index}
                  className="m-0 py-1.5 text-base font-rubik font-medium text-left capitalize last:pb-0"
                >
                  {val?.dataset.label}
                  {":"} {monetarySuffix(val?.raw)}
                </li>
              );
            })}
          </ul>
        </>
      )}
    </div>
  );
};

export default memo(GraphTooltip);
Run Code Online (Sandbox Code Playgroud)

图表组件

import React, { useMemo, useState, useRef, useCallback } from 'react';
import {
  Chart as ChartJS,
  CategoryScale,
  LinearScale,
  PointElement,
  LineElement,
  Title,
  Tooltip,
  Legend,
  Filler,
} from 'chart.js';
import { Line } from 'react-chartjs-2';
import GraphTooltip from './chart-tooltip';

ChartJS.register(
  CategoryScale,
  LinearScale,
  PointElement,
  LineElement,
  Title,
  Tooltip,
  Legend,
  Filler
);

const GradientChart = () => {
  const [tooltipVisible, setTooltipVisible] = useState(false);
  const [tooltipData, setTooltipData] = useState(null);
  const [tooltipPos, setTooltipPos] = useState(null);

  const chartRef = useRef(null);

  const data = {
    labels: [
      'January',
      'February',
      'March',
      'April',
      'May',
      'June',
      'July',
      'Agust',
      'September',
      'October',
      'November',
      'December',
    ],
    datasets: [
      {
        fill: true,
        backgroundColor: (context) => {
          const chart = context.chart;
          const { ctx, chartArea } = chart;

          if (!chartArea) {
            return;
          }
          return createGradient(
            ctx,
            chartArea,
            '#F46079',
            '#F46079',
            'rgba(255,255,255,0)'
          );
        },
        borderColor: '#F46079',
        lineTension: 0.4,
        pointRadius: 5,
        pointHoverRadius: 10,
        pointBackgroundColor: '#FE5670',
        pointBorderColor: '#ffffff',
        pointBorderWidth: 1.5,
        label: 'Sales',
        data: [
          4500, 2800, 4400, 2800, 3000, 2500, 3500, 2800, 3000, 4000, 2600,
          3000,
        ],
      },
      {
        fill: true,
        backgroundColor: (context) => {
          const chart = context.chart;
          const { ctx, chartArea } = chart;

          if (!chartArea) {
            return;
          }
          return createGradient(
            ctx,
            chartArea,
            '#2f4b7c',
            '#2f4b7c',
            'rgba(255,255,255,0)'
          );
        },
        borderColor: '#2f4b7c',
        lineTension: 0.4,
        pointRadius: 5,
        pointHoverRadius: 10,
        pointBackgroundColor: '#FE5670',
        pointBorderColor: '#ffffff',
        pointBorderWidth: 1.5,
        label: 'Commision',
        data: [
          5000, 3500, 3000, 5500, 5000, 3500, 6000, 1500, 2000, 1800, 1500,
          2800,
        ],
      },
      {
        fill: true,
        backgroundColor: (context) => {
          const chart = context.chart;
          const { ctx, chartArea } = chart;

          if (!chartArea) {
            return;
          }
          return createGradient(
            ctx,
            chartArea,
            '#665191',
            '#665191',
            'rgba(255,255,255,0)'
          );
        },
        borderColor: '#665191',
        lineTension: 0.4,
        pointRadius: 5,
        pointHoverRadius: 10,
        pointBackgroundColor: '#FE5670',
        pointBorderColor: '#ffffff',
        pointBorderWidth: 1.5,
        label: 'Transaction',
        data: [
          1000, 2000, 1500, 2000, 1800, 1500, 2800, 2800, 3000, 2500, 3500,
          2800,
        ],
      },
    ],
  };

  const createGradient = (ctx, chartArea, c1, c2, c3) => {
    const chartWidth = chartArea.right - chartArea.left;
    const chartHeight = chartArea.bottom - chartArea.top;
    const gradient = '';
    const width = '';
    const height = '';
    if (!gradient || width !== chartWidth || height !== chartHeight) {
      width = chartWidth;
      height = chartHeight;
      gradient = ctx.createLinearGradient(
        0,
        chartArea.bottom,
        0,
        chartArea.top
      );
      gradient.addColorStop(0, c3);
      gradient.addColorStop(0.5, c2);
      gradient.addColorStop(1, c1);
    }
    return gradient;
  };

  const customTooltip = useCallback((context) => {
    if (context.tooltip.opacity == 0) {
      // hide tooltip visibilty
      setTooltipVisible(false);
      return;
    }

    const chart = chartRef.current;
    const canvas = chart.canvas;
    if (canvas) {
      // enable tooltip visibilty
      setTooltipVisible(true);

      // set position of tooltip
      const left = context.tooltip.x;
      const top = context.tooltip.y;

      // handle tooltip multiple rerender
      if (tooltipPos?.top != top) {
        setTooltipPos({ top: top, left: left });
        setTooltipData(context.tooltip);
      }
    }
  });

  const options = useMemo(() => ({
    responsive: true,
    scales: {
      y: {
        grid: {
          display: false,
        },
      },
    },
    interaction: {
      mode: 'index',
      intersect: false,
    },
    plugins: {
      legend: {
        display: false,
      },
      title: {
        display: false,
      },
      tooltip: {
        enabled: false,
        position: 'nearest',
        external: customTooltip,
      },
    },
  }));

  return (
    <div className="grad-chart-wrapper w-full relative">
      <Line options={{ ...options }} data={data} ref={chartRef} />

      {tooltipPos && (
        <GraphTooltip
          data={tooltipData}
          position={tooltipPos}
          visibility={tooltipVisible}
        />
      )}
    </div>
  );
};

export default GradientChart;
Run Code Online (Sandbox Code Playgroud)