ABTSoftware / SciChart.JS.Examples

MIT License
90 stars 37 forks source link

Calling an function after onMouseUp event on rubberBandXyZoomModifier #61

Closed hecto932 closed 3 years ago

hecto932 commented 3 years ago

Hi, again guys!

Currently, I would like to call a custom function after make zoom in using a rubberBandXyZoomModifier.

So here I have an example about how I'm using the modifierMouseUp event to call a custom function

What would I like to do?

I would like to switch between zoomPanModifier and rubberBandXyZoomModifier, and when I'm using a rubberBandXyZoomModifier I would like to call a custom function without overwriting the normal behavior of zoom.

/* eslint-disable no-console */
/* eslint-disable no-shadow */
/* eslint-disable import/no-duplicates */
/* eslint-disable import/extensions */
/* eslint-disable no-bitwise */
/* eslint-disable max-len */
/* eslint-disable no-plusplus */
/* eslint-disable @typescript-eslint/no-unused-vars */
import React, { FC, useState, useEffect } from 'react';
import { format } from 'date-fns';
import { SciChartSurface } from 'scichart/Charting/Visuals/SciChartSurface';
import { MouseWheelZoomModifier } from 'scichart/Charting/ChartModifiers/MouseWheelZoomModifier';
import { ZoomExtentsModifier } from 'scichart/Charting/ChartModifiers/ZoomExtentsModifier';
import { EAxisAlignment } from 'scichart/types/AxisAlignment';
import { NumericAxis } from 'scichart/Charting/Visuals/Axis/NumericAxis';
import { XyDataSeries } from 'scichart/Charting/Model/XyDataSeries';
import {
  ELegendOrientation,
  ELegendPlacement,
} from 'scichart/Charting/Visuals/Legend/SciChartLegendBase';
import { FastLineRenderableSeries } from 'scichart/Charting/Visuals/RenderableSeries/FastLineRenderableSeries';
import { LegendModifier } from 'scichart/Charting/ChartModifiers/LegendModifier';
import { RolloverModifier } from 'scichart/Charting/ChartModifiers/RolloverModifier';
import { TSciChart } from 'scichart/types/TSciChart';

import { randomColor } from '../../utils';
import { ZoomPanModifier } from 'scichart/Charting/ChartModifiers/ZoomPanModifier';
import { NumberRange } from 'scichart/Core/NumberRange';
import { ENumericFormat } from 'scichart/types/NumericFormat';
import { ELineDrawMode } from 'scichart/Charting/Drawing/WebGlRenderContext2D';
import { RubberBandXyZoomModifier } from 'scichart/Charting/ChartModifiers/RubberBandXyZoomModifier';
import { EXyDirection } from 'scichart/types/XyDirection';
import { easing } from 'scichart/Core/Animations/EasingFunctions';

const formatAxis = (tickItem: number) => format(tickItem, 'MM/dd HH:mm a');
const numberToFix = (tickItem: number) => tickItem.toFixed(2);

const draw = async (divElem: string, orientation: string = 'vertical', options: any) => {
  const { sciChartSurface, wasmContext } = await SciChartSurface.create(divElem);

  const xAxis = new NumericAxis(wasmContext);
  const yAxis = new NumericAxis(wasmContext, { growBy: new NumberRange(0.01, 0.01) });

  xAxis.labelProvider.numericFormat = ENumericFormat.Date_DDMMYYYY;
  xAxis.labelProvider.formatLabel = formatAxis;

  yAxis.labelProvider.formatLabel = numberToFix;

  if (orientation === 'vertical') {
    // SET VERTICAL ORIENTATION BY DEFAULT
    xAxis.axisAlignment = EAxisAlignment.Left;
    yAxis.axisAlignment = EAxisAlignment.Bottom;

    // An axis may be optionally flipped using flippedCoordinates property
    xAxis.flippedCoordinates = false;
    yAxis.flippedCoordinates = true;
  } else {
    xAxis.axisAlignment = EAxisAlignment.Bottom;
    yAxis.axisAlignment = EAxisAlignment.Left;

    // An axis may be optionally flipped using flippedCoordinates property
    xAxis.flippedCoordinates = false;
    yAxis.flippedCoordinates = false;
  }

  // ADDING AXES TO SURFACE
  sciChartSurface.xAxes.add(xAxis);
  sciChartSurface.yAxes.add(yAxis);

  return { sciChartSurface, wasmContext };
};

const divElement = 'scichart-root';

interface Props {}

const Scichart: FC<Props> = () => {
  const [chartReady, setChartReady] = useState(false);
  const [sciChartSurface, setSciChartSurface] = useState<SciChartSurface>();
  const [wasmContext, setWasmContext] = useState<TSciChart>();

  const [sciChartTooltip, setSciChartTooltip] = useState<any>();
  const [sciChartLegend, setSciChartLegend] = useState<any>();

  const [sciChartRubberModifier, setSciChartRubberModifier] = useState<any>();

  const [placementValue, setPlacementValue] = useState<ELegendPlacement>(ELegendPlacement.TopRight);
  const [orientationValue, setOrientationValue] = useState<ELegendOrientation>(
    ELegendOrientation.Vertical,
  );

  const [showLegendValue, setShowLegendValue] = useState(true);
  const [showCheckboxesValue, setShowCheckboxesValue] = useState(true);
  const [showSeriesMarkersValue, setShowSeriesMarkersValue] = useState(true);

  const [orientation, setChartOrientation] = useState('vertical');

  const [zoomByArea, setZoomByArea] = useState(false);

  const setOrientation = (orientation: string) => {
    const xAxis = sciChartSurface?.xAxes.get(0);
    const yAxis = sciChartSurface?.yAxes.get(0);

    if (xAxis && yAxis) {
      if (orientation === 'vertical') {
        // SET VERTICAL ORIENTATION BY DEFAULT
        xAxis.axisAlignment = EAxisAlignment.Left;
        yAxis.axisAlignment = EAxisAlignment.Bottom;

        // An axis may be optionally flipped using flippedCoordinates property
        xAxis.flippedCoordinates = false;
        yAxis.flippedCoordinates = true;
      } else {
        xAxis.axisAlignment = EAxisAlignment.Bottom;
        yAxis.axisAlignment = EAxisAlignment.Left;

        // An axis may be optionally flipped using flippedCoordinates property
        xAxis.flippedCoordinates = false;
        yAxis.flippedCoordinates = false;
      }
    }
  };

  const addLineDataSeries = (sciChartSurface: SciChartSurface, wasmContext: TSciChart) => {
    const data: any = {
      VALUE_A: [
        {
          VALUE_A: 167.6608,
          VALUE_B: 0,
          VALUE_C: 0,
          dts: 1612208940000,
        },
        {
          VALUE_A: 167.6608,
          VALUE_B: 0,
          VALUE_C: 0,
          dts: 1612209000000,
        },
        {
          VALUE_A: 167.6608,
          VALUE_B: 0,
          VALUE_C: 0,
          dts: 1612209060000,
        },
        {
          VALUE_A: 167.6608,
          VALUE_B: 0,
          VALUE_C: 0,
          dts: 1612209120000,
        },
        {
          VALUE_A: 167.6608,
          VALUE_B: 0,
          VALUE_C: 0,
          dts: 1612209180000,
        },
        {
          VALUE_A: 167.6608,
          VALUE_B: 0,
          VALUE_C: 0,
          dts: 1612209240000,
        },
        {
          VALUE_A: 167.6608,
          VALUE_B: 0,
          VALUE_C: 0,
          dts: 1612209300000,
        },
        {
          VALUE_A: 167.6608,
          VALUE_B: 0,
          VALUE_C: 0,
          dts: 1612209360000,
        },
        {
          VALUE_A: 167.6608,
          VALUE_B: 0,
          VALUE_C: 0,
          dts: 1612209420000,
        },
        {
          VALUE_A: 167.6927,
          VALUE_B: 0,
          VALUE_C: 0,
          dts: 1612209480000,
        },
        {
          VALUE_A: 167.747,
          VALUE_B: 0,
          VALUE_C: 0,
          dts: 1612209540000,
        },
        {
          VALUE_A: 167.8042,
          VALUE_B: 0,
          VALUE_C: 0,
          dts: 1612209600000,
        },
        {
          VALUE_A: 167.8793,
          VALUE_B: 0,
          VALUE_C: 0,
          dts: 1612209660000,
        },
        {
          VALUE_A: 167.9633,
          VALUE_B: 0,
          VALUE_C: 0,
          dts: 1612209720000,
        },
        {
          VALUE_A: 168.0597,
          VALUE_B: 0,
          VALUE_C: 0,
          dts: 1612209780000,
        },
        {
          VALUE_A: 168.1733,
          VALUE_B: 0,
          VALUE_C: 0,
          dts: 1612209840000,
        },
        {
          VALUE_A: 168.2884,
          VALUE_B: 0,
          VALUE_C: 0,
          dts: 1612209900000,
        },
        {
          VALUE_A: 168.3883,
          VALUE_B: 0,
          VALUE_C: 0,
          dts: 1612209960000,
        },
        {
          VALUE_A: 168.5152,
          VALUE_B: 0,
          VALUE_C: 0,
          dts: 1612210020000,
        },
        {
          VALUE_A: 168.6691,
          VALUE_B: 0,
          VALUE_C: 0,
          dts: 1612210080000,
        },
        {
          VALUE_A: 168.9379,
          VALUE_B: 0,
          VALUE_C: 0,
          dts: 1612210140000,
        },
        {
          VALUE_A: 169.2598,
          VALUE_B: 0,
          VALUE_C: 0,
          dts: 1612210200000,
        },
        {
          VALUE_A: 169.6442,
          VALUE_B: 0,
          VALUE_C: 0,
          dts: 1612210260000,
        },
        {
          VALUE_A: 170.0544,
          VALUE_B: 0,
          VALUE_C: 0,
          dts: 1612210320000,
        },
        {
          VALUE_A: 170.4789,
          VALUE_B: 0,
          VALUE_C: 0,
          dts: 1612210380000,
        },
        {
          VALUE_A: 170.7599,
          VALUE_B: 0,
          VALUE_C: 0,
          dts: 1612210440000,
        },
        {
          VALUE_A: 170.856,
          VALUE_B: 0,
          VALUE_C: 0,
          dts: 1612210500000,
        },
        {
          VALUE_A: 171.0672,
          VALUE_B: 0,
          VALUE_C: 0,
          dts: 1612210560000,
        },
        {
          VALUE_A: 171.2867,
          VALUE_B: 0,
          VALUE_C: 0,
          dts: 1612210620000,
        },
        {
          VALUE_A: 171.5556,
          VALUE_B: 0,
          VALUE_C: 0,
          dts: 1612210680000,
        },
        {
          VALUE_A: 171.7607,
          VALUE_B: 0,
          VALUE_C: 0,
          dts: 1612210740000,
        },
        {
          VALUE_A: 171.9235,
          VALUE_B: 0,
          VALUE_C: 0,
          dts: 1612210800000,
        },
        {
          VALUE_A: 172.0627,
          VALUE_B: 0,
          VALUE_C: 0,
          dts: 1612210860000,
        },
        {
          VALUE_A: 172.2566,
          VALUE_B: 0,
          VALUE_C: 0,
          dts: 1612210920000,
        },
        {
          VALUE_A: 172.2787,
          VALUE_B: 0,
          VALUE_C: 0,
          dts: 1612210980000,
        },
        {
          VALUE_A: 172.2787,
          VALUE_B: 0,
          VALUE_C: 0,
          dts: 1612211040000,
        },
        {
          VALUE_A: 172.2787,
          VALUE_B: 0,
          VALUE_C: 0,
          dts: 1612211100000,
        },
      ],
      VALUE_B: [],
      VALUE_C: [],
    };

    const columns = Object.keys(data);

    // This remove the previous data and allows create a new rendableSerie
    sciChartSurface.renderableSeries.clear();

    if (columns.length) {
      columns.forEach((columnName: string, index: number) => {
        const xyDataSeries = new XyDataSeries(wasmContext);
        xyDataSeries.dataSeriesName = columnName;
        const lineSeries = new FastLineRenderableSeries(wasmContext, {
          drawNaNAs: ELineDrawMode.PolyLine,
          isDigitalLine: false,
          stroke: randomColor(),
          strokeThickness: 3,
          dataSeries: xyDataSeries,
        });
        data[columnName].forEach((item: any, index: number) => {
          xyDataSeries.append(item.dts, item[columnName]);
        });

        sciChartSurface.renderableSeries.add(lineSeries);
      });
    }
    sciChartSurface.zoomExtents();
  };

  const setLegendModifier = (resScichartSurface: SciChartSurface) => {
    const legendModifier = new LegendModifier({
      placement: placementValue,
      orientation: orientationValue,
      showLegend: showLegendValue,
      showCheckboxes: showCheckboxesValue,
      showSeriesMarkers: showSeriesMarkersValue,
    });
    resScichartSurface.chartModifiers.add(legendModifier);
    setSciChartLegend(legendModifier);
  };

  const setTooltip = (resScichartSurface: SciChartSurface) => {
    if (resScichartSurface) {
      const tooltipModifier = new RolloverModifier({
        rolloverLineStroke: 'SteelBlue',
        rolloverLineStrokeThickness: 5,
        isVerticalChart: orientation === 'vertical',
      });

      tooltipModifier.onDetach = () => console.log('tooltip rolloverModifier removed!');
      tooltipModifier.onAttach = () => console.log('tooltip rolloverModifier attached');

      resScichartSurface.chartModifiers.add(tooltipModifier);
      setSciChartTooltip(tooltipModifier);
    }
  };

  const setZoomPanModifier = (resScichartSurface: SciChartSurface) => {
    const zoomPanModifier = new ZoomPanModifier();
    zoomPanModifier.onAttach = () => console.log('zoomPanModifier attached!');
    zoomPanModifier.onDetach = () => console.log('zoomPanModifier detached!');

    // Add Zoom Extents behavior
    resScichartSurface.chartModifiers.add(zoomPanModifier);
    setSciChartRubberModifier(zoomPanModifier);
  };

  const setMouseWheelZoomModifier = (resScichartSurface: SciChartSurface) => {
    const mouseWheelZoomModifier = new MouseWheelZoomModifier();
    mouseWheelZoomModifier.onDetach = () => console.log('mouseWheelZoomModifier removed!');
    mouseWheelZoomModifier.onAttach = () => console.log('mouseWheelZoomModifier attached');
    resScichartSurface.chartModifiers.add(mouseWheelZoomModifier);
  };

  const setZoomExtentsModifier = (resScichartSurface: SciChartSurface) => {
    const zoomExtentsModifier = new ZoomExtentsModifier();
    zoomExtentsModifier.onDetach = () => console.log('zoomExtentsModifier removed!');
    zoomExtentsModifier.onAttach = () => console.log('zoomExtentsModifier attached');
    resScichartSurface.chartModifiers.add(zoomExtentsModifier);
  };

  const setPanToolModifier = (resScichartSurface: SciChartSurface, active: boolean) => {
    const rubberBandXyZoomModifier = new RubberBandXyZoomModifier({
      isAnimated: true,
      animationDuration: 400,
      easingFunction: easing.outExpo,
      fill: '#FFFFFF33',
      stroke: '#FFFFFF77',
      strokeThickness: 1,
      xyDirection: EXyDirection.YDirection,
    });
    rubberBandXyZoomModifier.onAttach = () =>
      console.log(`${divElement}: rubberBandXyZoomModifier attached!`);
    rubberBandXyZoomModifier.onDetach = () =>
      console.log(`${divElement}: rubberBandXyZoomModifier detached!`);

    rubberBandXyZoomModifier.modifierMouseUp = () => {
      console.log('Calling a function after zoom in by area');
    };

    const zoomPanModifier = new ZoomPanModifier();

    zoomPanModifier.onAttach = () => console.log(`${divElement}: zoomPanModifier attached!`);
    zoomPanModifier.onDetach = () => console.log(`${divElement}: zoomPanModifier detached!`);

    if (active && sciChartRubberModifier) {
      resScichartSurface.chartModifiers.remove(sciChartRubberModifier);
      resScichartSurface.chartModifiers.add(zoomPanModifier);
      setSciChartRubberModifier(zoomPanModifier);
    } else {
      resScichartSurface.chartModifiers.remove(sciChartRubberModifier);
      resScichartSurface.chartModifiers.add(rubberBandXyZoomModifier);
      setSciChartRubberModifier(rubberBandXyZoomModifier);
    }
  };

  const onHandleScichartLegend = () => {
    if (sciChartLegend) {
      sciChartLegend.sciChartLegend.showLegend = !showLegendValue;
      setShowLegendValue(!showLegendValue);
    }
  };

  const onHandleZoomByArea = () => {
    setZoomByArea(!zoomByArea);
  };

  useEffect(() => {
    (async () => {
      const res = await draw(divElement, orientation, {});
      setSciChartSurface(res.sciChartSurface);
      setWasmContext(res.wasmContext);
      addLineDataSeries(res.sciChartSurface, res.wasmContext);
      setLegendModifier(res.sciChartSurface);
      setZoomPanModifier(res.sciChartSurface);
      setMouseWheelZoomModifier(res.sciChartSurface);
      setZoomExtentsModifier(res.sciChartSurface);
      setTooltip(res.sciChartSurface);
      setChartReady(true);
    })();

    return () => {
      sciChartSurface?.delete();
      setSciChartSurface(undefined);
    };
  }, []);

  useEffect(() => {
    if (!sciChartSurface) {
      return;
    }

    setPanToolModifier(sciChartSurface, !zoomByArea);
  }, [zoomByArea]);

  return (
    <>
      <div
        id={divElement}
        style={{
          position: 'relative',
          width: '100%',
          overflow: 'auto',
        }}
      />
      <button onClick={onHandleScichartLegend}>Show/hide Legend</button>
      <button onClick={onHandleZoomByArea}>
        {zoomByArea ? `Disabled` : `Enabled`} zoom by area
      </button>
    </>
  );
};

export default Scichart;

What would it be the best way to do this? It looks like I'm overwriting or replacing the modifierMouseUp function

klishevich commented 3 years ago

Hi @hecto932 you should create your custom modifier by extending RubberBandXyZoomModifier, here you can find an example - https://github.com/ABTSoftware/SciChart.JS.Examples/blob/master/Sandbox/CustomerExamples/CreateAnnotationsDynamically/src/CreateAnnotationModifier.ts Let me know if it helps

observerjnr commented 3 years ago

Also, you can assign different mouse buttons to the modifiers, to prevent conflicts if they are used simultaneously

    import { EExecuteOn } from "scichart/types/ExecuteOn";

    ...

    const rubberBandModifier = new RubberBandXyZoomModifier({ executeOn: EExecuteOn.MouseRightButton });
    const zoomPanModifier = new ZoomPanModifier({ executeOn: EExecuteOn.MouseMiddleButton });

    sciChartSurface.chartModifiers.add(zoomPanModifier);
    sciChartSurface.chartModifiers.add(rubberBandModifier);
hecto932 commented 3 years ago

I'll check those examples

Thanks, @klishevich @observerjnr

hecto932 commented 3 years ago

This is what I did and it solved my issue :)

import { RubberBandXyZoomModifier } from 'scichart/Charting/ChartModifiers/RubberBandXyZoomModifier';
import { ModifierMouseArgs } from 'scichart/Charting/ChartModifiers/ModifierMouseArgs';
import { easing } from 'scichart/Core/Animations/EasingFunctions';
import { EXyDirection } from 'scichart/types/XyDirection';

// Create a TypeScript class which inherits ChartModifierbase2D to insert into SciChartSurface.chartModifiers collection
export class CustomRubberXyModifier extends RubberBandXyZoomModifier {

  private options: any;

  constructor(options: any) {
    const defaultOptions = {
      isAnimated: true,
      animationDuration: 400,
      easingFunction: easing.outExpo,
      fill: '#FFFFFF33',
      stroke: '#FFFFFF77',
      strokeThickness: 1,
      xyDirection: EXyDirection.YDirection,
    }
    super(defaultOptions);

    this.options = options;
  }

  // Called when mouse-up on the chart
  public modifierMouseUp(args: ModifierMouseArgs) {
    super.modifierMouseUp(args);
    if (this.options?.modifierMouseUp) {
      this.options.modifierMouseUp();
    }
  }
}

Do you have any comments about it?

klishevich commented 3 years ago

Hi @hecto932 it looks fine :)!

andyb1979 commented 3 years ago

Hi Hector - looks good! That is precisely how to extend the RubberBandXyZoomModifier using typescript. Good job!

On Mon, Mar 15, 2021 at 10:02 AM Michael Klishevich < @.***> wrote:

Hi @hecto932 https://github.com/hecto932 it looks fine :)!

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/ABTSoftware/SciChart.JS.Examples/issues/61#issuecomment-799288281, or unsubscribe https://github.com/notifications/unsubscribe-auth/ADLEDVMCPSTWHZNGT4SPY3DTDXLKTANCNFSM4Y7LY5SQ .