zhyp
zhyp

Reputation: 349

Using zoom/pan with ChartJS

So I've been using react ChartJS to build some graphs, but eventually I ran into scroll problems that I couldn't solve with chartjs, so I found Apex that has Zoom built-in and I could make it scrollable. But now since they are two completely different libs, so I can't copy it 100% the way it was. charts
I did not find anything on the docs talking about multiple labels in one value, so if anyone could help me with this one. And if you see close enough there are some line running on the chart below, are those possible to do with Apex? (I'm using a customPlugin in chartJS to do it)

Upvotes: 2

Views: 2624

Answers (1)

zhyp
zhyp

Reputation: 349

Ok, in the end I changed back to ChartJS and used the chartjs-plugin-zoom plugin to do the same thing I was doing with ApexChart, I'm placing my code in this answer in case that anyone faces the same problem. React Bar Component:

import React from "react";
import { Bar, Chart } from "react-chartjs-2";
import ChartDataLabels from "chartjs-plugin-datalabels";
import Zoom from "chartjs-plugin-zoom";

const ChartBar = ({
  data,
  text,
  noLabel,
  stacked,
  newPlugin,
  labelPosition,
  // test,
}) => {
  Chart.register(Zoom);
  return (
    <div
      className="graphics"
      style={{
        display: "flex",
        alignItems: "center",
        flexDirection: "column",
      }}
    >
      {/* <h1>Gráficos</h1> */}
      <div style={{ width: "65%", height: "350px" }}>
        <Bar
          data={data}
          plugins={[
            ChartDataLabels,
            newPlugin ? newPlugin : "",
            // test ? test : "",
          ]}
          options={{
            categoryPercentage: stacked ? 1.0 : 0.9,
            barPercentage: 1.0,
            // layout: {
            //   padding: {
            //   left: 0,
            //   right: 0,
            // top: 60,
            //   bottom: 0,
            //   },
            // },
            responsive: true,
            maintainAspectRatio: false,
            plugins: {
              zoom: {
                // limits: { y: { min: "original", max: "original" } },
                // pan: { enabled: true, mode: "xy", threshold: 10 },
                // zoom: {
                //   wheel: {
                //     enabled: true,
                //     mode: "xy",
                //   },
                // },
                limits: { y: { min: "original", max: "original" } },
                pan: { enabled: true, mode: "x", threshold: 10 },
                zoom: {
                  mode: "x",
                  drag: {
                    enabled: true,
                    backgroundColor: "rgba(225,0,225,0.3)",
                  },
                  wheel: {
                    enabled: true,
                    modifierKey: "alt",
                  },
                },
              },
              tooltip: { enabled: false },
              legend: {
                display: noLabel ? false : true,
                position: labelPosition ? labelPosition : "bottom",
                title: { padding: 40 },
              },
              title: { text: text, display: true, padding: 30 },
            },
            scales: {
              // scaleLabel: { display: true },
              x: {
                stacked: stacked ? true : false,
                // ticks: {
                // display: false,
                // autoSkip: true,
                // maxTicksLimit: 10,
                // beginAtZero: true,
                // },
                // gridLines: {
                //   display: false,
                // },
              },

              y: { stacked: stacked ? true : false, ticks: { display: false } },
              // xAxes: [{ scaleLabel: { display: true } }],
            },
          }}
        />
      </div>
    </div>
  );
};

export default ChartBar;

One data object that is being used with this component(eg. the one in the main question):

{
          customPlugin: {
            id: "customValue",
            afterDraw: (chart, args, opts) => {
              const {
                ctx,
                data: { datasets },
                _metasets,
              } = chart;

              datasets[1].data.forEach((dp, i) => {
                let increasePercent =
                  (dp * 100) / datasets[0].data[i] >= 100
                    ? Math.round(
                        ((dp * 100) / datasets[0].data[i] - 100) * 100
                      ) / 100
                    : (Math.round(
                        (100 - (dp * 100) / datasets[0].data[i]) * 100
                      ) /
                        100) *
                      -1;
                let barValue = `${increasePercent}%`;
                const lineHeight = ctx.measureText("M").width;
                const offset = opts.offset || 0;
                const dash = opts.dash || [];

                ctx.textAlign = "center";

                ctx.fillText(
                  barValue,
                  _metasets[1].data[i].x,
                  _metasets[1].data[i].y - lineHeight * 1.5,
                  _metasets[1].data[i].width
                );

                if (_metasets[0].data[i].y >= _metasets[1].data[i].y) {
                  ctx.beginPath();
                  ctx.setLineDash(dash);

                  ctx.moveTo(_metasets[0].data[i].x, _metasets[0].data[i].y);
                  ctx.lineTo(
                    _metasets[0].data[i].x,
                    _metasets[1].data[i].y - offset
                  );
                  ctx.lineTo(
                    _metasets[1].data[i].x,
                    _metasets[1].data[i].y - offset
                  );
                  ctx.stroke();
                } else {
                  ctx.beginPath();
                  ctx.setLineDash(dash);

                  ctx.moveTo(
                    _metasets[0].data[i].x,
                    _metasets[0].data[i].y - offset
                  );
                  ctx.lineTo(
                    _metasets[1].data[i].x,
                    _metasets[0].data[i].y - offset
                  );
                  ctx.lineTo(
                    _metasets[1].data[i].x,
                    _metasets[1].data[i].y - offset - lineHeight * 2
                  );
                  ctx.stroke();
                }
              });
            },
          },
          text: "Evolução da Receita Líquida por Produto",
          type: "bar",
          // labels: values?.estimatedProducts?.map((v, i) => {
          //   return `Rec Líq - Prod ${++i}`;
          // }),
          labels: addNewArrayValue(values?.estimatedProducts, true),
          datasets: [
            {
              type: "bar",
              label: values?.monthsLabels?.mesBaseLabel,
              data: values?.productsValues?.receitaLiquidaBase,
              backgroundColor: ["rgba(42,62,176, 1)"],

              datalabels: {
                font: {
                  size: 10,
                },
                rotation: -90,
                color: "white",
                formatter: (value, context) => {
                  if (value !== 0) {
                    return value
                      ?.toFixed()
                      .replace(/\B(?=(\d{3})+(?!\d))/g, ",");
                    // ?.toFixed(0)
                    // .replace(/\d(?=(\d{3})+\.)/g, "$&,");
                  } else {
                    return 0;
                  }
                },
              },
            },
            {
              type: "bar",
              label: values?.monthsLabels?.mesOrcadoLabel,
              data: values?.productsValues?.receitaLiquidaOrcado,
              backgroundColor: "orange",

              datalabels: {
                font: {
                  size: 10,
                },
                rotation: -90,
                color: "black",
                formatter: (value, context) => {
                  if (value !== 0) {
                    return value
                      ?.toFixed()
                      .replace(/\B(?=(\d{3})+(?!\d))/g, ",");
                  } else {
                    return 0;
                  }
                },
              },
            },
          ],
        },

Yeah, it's not the best code, but it's a starter, I've been struggling for 1h30m just because there's little to no actual examples/doc on this case.

Upvotes: 2

Related Questions