Search code examples
javafxlinechartzooming

JavaFx LineChart Mouse Location Zoom With ScrollEvent


I am currently trying to implement LineChart mouse location zoom with ScrollEvent. I managed to do something but my problem is zooming into wrong direction. When my mouse stands on left side of lineChart, my zoom goes to right side of lineChart (symmetrical). Here is my code;

import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.scene.input.ScrollEvent;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
import javafx.util.StringConverter;

public class ChartZoom extends Application {

    @Override
    public void start(Stage stage) {

        LineChart<Number, Number> chart = chart();
        final NumberAxis axis = (NumberAxis) chart.getXAxis();
        final double lowerX = axis.getLowerBound();
        final double upperX = axis.getUpperBound();

        chart.setOnScroll(new EventHandler<ScrollEvent>() {

            @Override
            public void handle(ScrollEvent event) {
                final double minX = axis.getLowerBound();
                final double maxX = axis.getUpperBound();
                double threshold = minX + (maxX - minX) / 2d;
                double x = event.getX();
                double value = axis.getValueForDisplay(x).doubleValue();
                double direction = event.getDeltaY();
                if (direction > 0) {
                    if (maxX - minX <= 1) {
                        return;
                    }
                    if (value < threshold) {
                        axis.setLowerBound(minX + 1);
                    } else {
                        axis.setUpperBound(maxX - 1);
                    }
                } else {
                    if (value < threshold) {
                        double nextBound = Math.max(lowerX, minX - 1);
                        axis.setLowerBound(nextBound);
                    } else {
                        double nextBound = Math.min(upperX, maxX + 1);
                        axis.setUpperBound(nextBound);
                    }
                }

            }
        });

        BorderPane borderPane = new BorderPane();
        borderPane.setCenter(chart);
        Scene scene = new Scene(borderPane, 800, 600);
        stage.setScene(scene);
        stage.show();
    }

    private LineChart<Number, Number> chart() {
        XYChart.Series series = new LineChart.Series<>();
        final String[] days = { "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" };
        NumberAxis xAxis = new NumberAxis(0, days.length - 1, 1);
        xAxis.setAutoRanging(false);
        xAxis.setTickLabelFormatter(new StringConverter<Number>() {

            @Override
            public String toString(Number t) {
                int index = t.intValue();
                return (index >= 0 && index < days.length) ? days[index] : null;
            }

            @Override
            public Number fromString(String string) {
                throw new UnsupportedOperationException("Not supported yet.");
            }
        });
        NumberAxis yAxis = new NumberAxis();
        final LineChart<Number, Number> sc = new LineChart<>(xAxis, yAxis);
        sc.setCreateSymbols(false);
        sc.setPrefSize(1200, 210);
        series.setName("Line Chart Series");
        sc.getData().add(series);
        series.getData().add(new LineChart.Data(0, 23));
        series.getData().add(new LineChart.Data(1, 57));
        series.getData().add(new LineChart.Data(2, 54));
        series.getData().add(new LineChart.Data(3, 44));
        series.getData().add(new LineChart.Data(4, 14));
        series.getData().add(new LineChart.Data(5, 35));
        series.getData().add(new LineChart.Data(6, 65));
        return sc;
    }

    public static void main(String[] args) {
        launch(args);
    }
}

Solution

  • Ok i just fixed it by changing 3rd if below

    if (direction > 0) {
                        if (maxX - minX <= 1) {
                            return;
                        }
                        if (value < threshold) { // if(value > threshold)
                            axis.setLowerBound(minX + 1);
                        } else {
                            axis.setUpperBound(maxX - 1);
                        }