1

I have a simple Line chart using HighCharts & trying to add draggable plotlines in it.

$(function () {

function draggablePlotLine(axis, plotLineId) {
    var clickX, clickY;

    var getPlotLine = function () {
        for (var i = 0; i < axis.plotLinesAndBands.length; i++) {
            if (axis.plotLinesAndBands[i].id === plotLineId) {
                return axis.plotLinesAndBands[i];
            }
        }
    };

    var getValue = function() {
        var plotLine = getPlotLine();
        var translation = axis.horiz ? plotLine.svgElem.translateX : plotLine.svgElem.translateY;
        var new_value = axis.toValue(translation) - axis.toValue(0) + plotLine.options.value;
        new_value = Math.max(axis.min, Math.min(axis.max, new_value));
        return new_value;
    };

    var drag_start = function (e) {
        $(document).bind({
            'mousemove.line': drag_step,
                'mouseup.line': drag_stop
        });

        var plotLine = getPlotLine();
        clickX = e.pageX - plotLine.svgElem.translateX;
        clickY = e.pageY - plotLine.svgElem.translateY;
        if (plotLine.options.onDragStart) {
            plotLine.options.onDragStart(getValue());
        }
    };

    var drag_step = function (e) {
        var plotLine = getPlotLine();
        var new_translation = axis.horiz ? e.pageX - clickX : e.pageY - clickY;
        var new_value = axis.toValue(new_translation) - axis.toValue(0) + plotLine.options.value;
        new_value = Math.max(axis.min, Math.min(axis.max, new_value));
        new_translation = axis.toPixels(new_value + axis.toValue(0) - plotLine.options.value);
        plotLine.svgElem.translate(
            axis.horiz ? new_translation : 0,
            axis.horiz ? 0 : new_translation);

        if (plotLine.options.onDragChange) {
            plotLine.options.onDragChange(new_value);
        }
    };

    var drag_stop = function () {
        $(document).unbind('.line');

        var plotLine = getPlotLine();
        var plotLineOptions = plotLine.options;
        //Remove + Re-insert plot line
        //Otherwise it gets messed up when chart is resized
        if (plotLine.svgElem.hasOwnProperty('translateX')) {
            plotLineOptions.value = getValue()
            axis.removePlotLine(plotLineOptions.id);
            axis.addPlotLine(plotLineOptions);

            if (plotLineOptions.onDragFinish) {
                plotLineOptions.onDragFinish(plotLineOptions.value);
            }
        }

        getPlotLine().svgElem
            .css({'cursor': 'pointer'})
            .translate(0, 0)
            .on('mousedown', drag_start);
    };
    drag_stop();
};

$('#container').highcharts({
    xAxis: {

        min: -10,
        max: 10,
        plotLines: [{
            id: 'foo',
            color: '#00F',
            width: 4,
            value: 5,
            onDragStart: function (new_value) {
                $("#x_value").text(new_value + ' (Not changed yet)');
            },
            onDragChange: function (new_value) {
                $("#x_value").text(new_value + ' (Dragging)');
            },
            onDragFinish: function (new_value) {
                $("#x_value").text(new_value);
            }
        }]
    },

    yAxis: {
           type: 'logarithmic',  
        plotLines: [{
            label: {
                text: 'Not draggable'
            },
            id: 'y1',
            color: '#CCC',
            width: 4,
            value: 150
        }, {
            id: 'y2',
            color: '#00F',
            width: 4,
            value: 200,
            onDragStart: function (new_value) {
                $("#y_value").text(new_value + ' (Not changed yet)');
            },
            onDragChange: function (new_value) {
                $("#y_value").text(new_value + ' (Dragging)');
            },
            onDragFinish: function (new_value) {
                $("#y_value").text(new_value);
            }
        }, {
            label: {
                text: 'Not draggable'
            },
            id: 'y3',
            color: '#CCC',
            width: 4,
            value: 250
        }]
    },

    series: [{
        data: [29.9, 71.5, 106.4, 129.2, 144.0, 176.0, 135.6, 148.5, 216.4, 194.1, 95.6, 54.4]
    }]
}, function (chart) {
    draggablePlotLine(chart.xAxis[0], 'foo');
    draggablePlotLine(chart.yAxis[0], 'y2');
    console.log('ready');
});
});

JSFiddle Link: http://jsfiddle.net/48awM/30/ (actual) My Forked Update: http://jsfiddle.net/kk8322/z62nnmwe/1/

Actual JSFiddle works fine - which has normal axis scale, but forked version fails while dragging plotline. Only difference between actual & forked version is that my Y-Axis will be of type "Logarithmic".

   type: 'logarithmic'

Adding Logarithmic scale for axis is not handling drag/drop properly. Please suggest me how to handle Draggable Plotlines with Logarithmic x/y axis scale.

Karthikeyan
  • 506
  • 2
  • 8
  • 20

2 Answers2

4

There is a simpler way to move line while drag and drop. It works fine on logarithmic type axis.

Example: http://jsfiddle.net/tret53sv/

$(function () {

    var line,
    clickX,
    clickY;

    var start = function (e) {

        $(document).bind({
            'mousemove.line': step,
                'mouseup.line': stop
        });

        clickY = e.pageY - line.translateY;
        //clickY = e.pageY - line.translateY; //uncomment if plotline should be also moved vertically
    }

    var step = function (e) {
        line.translate(e.pageX - clickX, e.pageY - clickY)
    }

    var stop = function () {
        $(document).unbind('.line');
    }

    $('#container').highcharts({
        xAxis: {
            categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
        },
        yAxis:{
        type:'logarithmic',
             plotLines: [{
                color: '#FF0000',
                width: 5,
                value: 100
            }]
        },

        series: [{
            data: [29.9, 71.5, 106.4, 129.2, 144.0, 176.0, 135.6, 148.5, 216.4, 194.1, 95.6, 54.4]
        }]
    }, function (chart) {

        line = chart.yAxis[0].plotLinesAndBands[0].svgElem.attr({
            stroke: 'yellow'
        })
            .css({
            'cursor': 'pointer'
        })
            .translate(0, 0)
            .on('mousedown', start);


    });





});
Kacper Madej
  • 7,846
  • 22
  • 36
  • Thanks for sharing. I will try this locally & check. – Karthikeyan Apr 27 '16 at 10:04
  • For me this works, however, unlike the example code in the question, this does not compute the new "value" of the line. Any suggestions on how to recreate the getValue function (like in the question), that works for Logarithmic axis? – Basser Jul 24 '17 at 10:36
  • @Basser This would require changes in the code that would detect if an axis is linear and use linear values. You could check in the Highcharts code functions like lin2log or log2lin. – Kacper Madej Jul 24 '17 at 14:32
0

Thanks for sharing. Works pretty well. I also need to make an draggable or adjustable Plotband. For those who need an example here you can find an quick & dirty example based on Karthikeyan's solution from me.

I also used plotlines to adjust the plotband.

$(function () {
    function draggablePlotLineBand(axis, plotLineId, plotBandId, upperLine) {
        var clickX, clickY;
        var getPlotLine = function () {
            for (var i = 0; i < axis.plotLinesAndBands.length; i++) {
                if (axis.plotLinesAndBands[i].id === plotLineId) {
                    return axis.plotLinesAndBands[i];
                }
            }
        };

        var getPlotBand = function () {
            for (var i = 0; i < axis.plotLinesAndBands.length; i++) {
                if (axis.plotLinesAndBands[i].id === plotBandId) {
                    return axis.plotLinesAndBands[i];
                }
            }
        };

        var getFrom = function() {
            var plotBand = getPlotBand();
            var translation = axis.horiz ? plotBand.svgElem.translateX : plotBand.svgElem.translateY;
            var new_value = axis.toValue(translation) - axis.toValue(0) + plotBand.options.from;
            new_value = Math.max(axis.min, Math.min(axis.max, new_value));
            return new_value;
        };

        var getTo = function() {
            var plotBand = getPlotBand();
            var translation = axis.horiz ? plotBand.svgElem.translateX : plotBand.svgElem.translateY;
            var new_value = axis.toValue(translation) - axis.toValue(0) + plotBand.options.to;
            new_value = Math.max(axis.min, Math.min(axis.max, new_value));
            return new_value;
        };

        var getValue = function() {
            var plotLine = getPlotLine();
            var translation = axis.horiz ? plotLine.svgElem.translateX : plotLine.svgElem.translateY;
            var new_value = axis.toValue(translation) - axis.toValue(0) + plotLine.options.value;
            new_value = Math.max(axis.min, Math.min(axis.max, new_value));
            return new_value;
        };

        var drag_start = function (e) {
            $(document).bind({
                'mousemove.line': drag_step,
                'mouseup.line': drag_stop
            });

            var plotLine = getPlotLine();
            var plotBand = getPlotBand();
            clickX = e.pageX - plotLine.svgElem.translateX;
            clickY = e.pageY - plotLine.svgElem.translateY;
        };

        var drag_step = function (e) {
            var plotLine = getPlotLine();
            var plotBand = getPlotBand();
            var new_translation = axis.horiz ? e.pageX - clickX : e.pageY - clickY;
            var new_value = axis.toValue(new_translation) - axis.toValue(0) + plotLine.options.value;
            new_value = Math.max(axis.min, Math.min(axis.max, new_value));
            new_translation = axis.toPixels(new_value + axis.toValue(0) - plotLine.options.value);
            plotLine.svgElem.translate(
                axis.horiz ? new_translation : 0,
                axis.horiz ? 0 : new_translation);

            var new_value_to = axis.toValue(new_translation) - axis.toValue(0) + plotBand.options.to;
            new_value_to = Math.max(axis.min, Math.min(axis.max, new_value_to));

            var new_value_from = axis.toValue(new_translation) - axis.toValue(0) + plotBand.options.from;
            new_value_from = Math.max(axis.min, Math.min(axis.max, new_value_from));

            var newPath = plotBand.svgElem.d.split(' ');

            if(upperLine) {
                new_translation = axis.toPixels(new_value_to + axis.toValue(0) - plotBand.options.to);
                newPath[2] = axis.toPixels(plotBand.options.from) - new_translation;
                newPath[5] = axis.toPixels(plotBand.options.from) - new_translation;
            } else {
                new_translation = axis.toPixels(new_value_from + axis.toValue(0) - plotBand.options.from);
                newPath[7] = axis.toPixels(plotBand.options.to) - new_translation;
                newPath[9] = axis.toPixels(plotBand.options.to) - new_translation;
            }

            newPath = newPath.join(" ");
            plotBand.svgElem.attr('d', newPath);

            plotBand.svgElem.translate(
                axis.horiz ? new_translation : 0,
                axis.horiz ? 0 : new_translation);
        };

        var drag_stop = function () {
            $(document).unbind('.line');

            var plotLine = getPlotLine();
            var plotLineOptions = plotLine.options;

            var plotBand = getPlotBand();
            var plotBandOptions = plotBand.options;

            if (plotLine.svgElem.hasOwnProperty('translateX')) {
                plotLineOptions.value = getValue();
                axis.removePlotLine(plotLineOptions.id);
                axis.addPlotLine(plotLineOptions);
            }

            if (plotBand.svgElem.hasOwnProperty('translateX')) {
                if(upperLine) {
                    plotBandOptions.to = getTo();
                    axis.removePlotBand(plotBandOptions.id);
                    axis.addPlotBand(plotBandOptions);                
                } else {
                    plotBandOptions.from = getFrom();
                    axis.removePlotBand(plotBandOptions.id);
                    axis.addPlotBand(plotBandOptions);
                }
            }

            getPlotLine().svgElem
                .css({'cursor': 'pointer'})
                .translate(0, 0)
                .on('mousedown', drag_start);
        };
        drag_stop();
    };

    $('#container').highcharts({
        xAxis: {
            min: -10,
            max: 10
        },
        yAxis: {
            tickInterval: 1,
            plotLines: [{
                id: 'pl1',
                color: "#ffcc00",
                width: 4,
                value: 3,
                zIndex: 3
            },{
                id: 'pl2',
                color: "#ffcc00",
                width: 4,
                value: 5,
                zIndex: 3
            }],
            plotBands: [{
                id: 'pb1',
                color: "#ffe682",
                width: 1,
                from: 3,
                to: 5,
                zIndex: 2
            }]
        },
        series: [{
            data: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
        }]
    }, function (chart) {
        // Need to use the function for both plotlines wih reference to plotband id
        // last parameter defines which is the lower(0) and upper(1) plotline
        draggablePlotLineBand(chart.yAxis[0], 'pl1', 'pb1', 0);
        draggablePlotLineBand(chart.yAxis[0], 'pl2', 'pb1', 1);
    });
});

JsFiddle Example Draggable Plotband with Plotlines

CruelMaze
  • 13
  • 6