最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Google Line Chart: drag to adjust value - Stack Overflow

programmeradmin5浏览0评论

I've recently dropped use of Graphael and and extensions package Ico. I think it's still a great library, it just needs better documentation and a proper API before it can be widely adopted.

I've converted the areas with charting to use Google Charts and can't seem to find a way to do one particular feature I used graphael for: drag support. One of my line charts needs to be editable, meaning that individual dots on the line can be dragged up and down to adjust value.

I've been trying to find an event or a way to attach an event in the API without much success. Has anyone managed to do something like that?

It's supposedly possible to do custom charts - would it be possible to do it this way?

EDIT: Seems like it's not really possible or is incredibly hard to hook into Google API or outputted SVG. It being in an iframe and no source code available just makes it less and less attractive option.

I've since started experimenting with Highcharts. I was able to do everything that Google engine does and can fiddle with chart after it's been rendered. Since source code is provided it makes it easier to figure certain things out. It also seems to work a bit faster than Google solution since it uses path when doing a Line Chart instead of individual SVG circle elements.

The issue I'm facing now has to do with the SVG Path element - there is no single point to set up drag on. So in order to use jQuery.draggable one has to inject a SVG Circle or another element at the same position as the SVG Path fragment tying to drag. So the following has to be resolved: - how to place the created element to correct position? - how to move element when chart is scaled (min or max value change on either axis)? - how to convert new position back to a value, scaled for series data supplied?

Has anyone got enough in depth experience with Highcharts to fiddle with these? I'll try to provide JSfiddle example when possible.

I've recently dropped use of Graphael and and extensions package Ico. I think it's still a great library, it just needs better documentation and a proper API before it can be widely adopted.

I've converted the areas with charting to use Google Charts and can't seem to find a way to do one particular feature I used graphael for: drag support. One of my line charts needs to be editable, meaning that individual dots on the line can be dragged up and down to adjust value.

I've been trying to find an event or a way to attach an event in the API without much success. Has anyone managed to do something like that?

It's supposedly possible to do custom charts - would it be possible to do it this way?

EDIT: Seems like it's not really possible or is incredibly hard to hook into Google API or outputted SVG. It being in an iframe and no source code available just makes it less and less attractive option.

I've since started experimenting with Highcharts. I was able to do everything that Google engine does and can fiddle with chart after it's been rendered. Since source code is provided it makes it easier to figure certain things out. It also seems to work a bit faster than Google solution since it uses path when doing a Line Chart instead of individual SVG circle elements.

The issue I'm facing now has to do with the SVG Path element - there is no single point to set up drag on. So in order to use jQuery.draggable one has to inject a SVG Circle or another element at the same position as the SVG Path fragment tying to drag. So the following has to be resolved: - how to place the created element to correct position? - how to move element when chart is scaled (min or max value change on either axis)? - how to convert new position back to a value, scaled for series data supplied?

Has anyone got enough in depth experience with Highcharts to fiddle with these? I'll try to provide JSfiddle example when possible.

Share Improve this question edited Jan 20, 2012 at 16:46 Ales Potocnik asked Jan 13, 2012 at 8:35 Ales PotocnikAles Potocnik 3,0872 gold badges28 silver badges32 bronze badges 3
  • I've tried using jQuery and listen for mousedown / mouseup on the container (div) holding the chart but contained Google Chart suppresses event bubbling ... Perhaps there is a way to allow event bubbling on the chart? – Ales Potocnik Commented Jan 13, 2012 at 9:42
  • This might work if only one was able to tell Google Charts to render <object> instead of <iframe> chart – Ales Potocnik Commented Jan 19, 2012 at 17:58
  • Can you provide a link to the API or demos for the draggable charts behaviour in Raphael? Also do you happen to know any library that enables draggable/resizable pie charts? Relates to stackoverflow.com/q/8027340/339872 – antitoxic Commented Mar 25, 2012 at 18:08
Add a comment  | 

3 Answers 3

Reset to default 16 +50

Draggable points is not built-in into Highcharts but using the events and methods in the API it is possible to implement an editable line/column/scatter/area-chart (will only handle moving the point in y-direction so far). The following example uses mousemove, mousedown and mouseup events to handle interaction.

  • At mousedown the start y value is recorded if a point is focused. The y-axis.translate method is the key here, it will translate mouse position to the data domain.
  • At mousemove the focused point and tooltip is updated with the new value.
  • At mouseup the point is updated and a new event drop is fired that updates the text in a status message.

Full example on jsfiddle. See also the feature request draggable points

This is now an available plugin on Highcharts: http://www.highcharts.com/plugin-registry/single/3/Draggable%20Points

Realize this is a rather old question, yet its highly referenced in relation to Google Charts, so perhaps this will help someone.

Spent a while working on this exact same issue, and with the help of several answers from @WhiteHat finally managed to get this working.

Basically, you need to addEventListener to the container for the chart for "mousemove", "mouseup", and "mousedown" and then track whether you're currently selected on a point or dragging a point.

The rest mostly ends up being a lot of edge and distance and scaling calculations to try and figure out where the point actually is at in the chart.

The main parts that end up being really relevant are:

// Get layout object for the chart (mostly for bounding boxes)
var chartLayout     = myLineChart.getChart().getChartLayoutInterface();
// Get the actual chart container (has all the chart properties as sub-variables)
var chartContainer  = document.getElementById( myLineChart.getContainerId() );
// Find the outer (beyond title and axes labels) limits of chart
// Varies with screen scrolling
var chartBounds     = chartContainer.getBoundingClientRect();
// Find the actual drawn region where your lines are (offset relative to chart)
var chartAreaBounds = chartLayout.getChartAreaBoundingBox();

Once you've actually figured out where the point is at with your mouse movement, then the location can be adjusted with:

var dataTable = myLineChart.getDataTable();
var spX = ( x - chartBounds.left - chartAreaBounds.left ) / chartAreaBounds.width  * ( chartMaxX - chartMinX );
var spY = ( chartAreaBounds.height - ( y - chartBounds.top  - chartAreaBounds.top ) ) / chartAreaBounds.height * ( chartMaxY - chartMinY );
dataTable.setValue( selectedPoint.row, 0, spX );
dataTable.setValue( selectedPoint.row, selectedPoint.column, spY );

A working example is included below that has two different line datasets with separate X values.

        google.charts.load('current', {'packages':['corechart']});
        google.charts.setOnLoadCallback( initChart );
        
        var myLineChart;
        var selectedPoint = null;
        
        var arrData = [
            ['Age', 'Weight', 'Weight2'],
            [ 3,      3.5,      null   ],
            [ 4,      5,        null   ],
            [ 4,      5.5,      null   ],
            [ 6.5,    7,        null   ],
            [ 8,      12,       null   ],
            [ 11,     14,       null   ],
            [ 1,      null,      3.551 ],
            [ 2,      null,     12.753 ],
            [ 3,      null,      5.058 ],
            [ 4,      null,      6.620 ],
            [ 5,      null,     12.371 ],
            [ 6,      null,      1.342 ],
            [ 7,      null,      5.202 ],
            [ 8,      null,      7.008 ]
        ];
        
        var data;
        
        var options = {
            title: 'Age vs. Weight comparison',
            hAxis: {title: 'Age', minValue: 0, maxValue: 15},
            vAxis: {title: 'Weight', minValue: 0, maxValue: 15},
            legend: 'none'
        };
        
        function initChart(){
            data = google.visualization.arrayToDataTable( arrData );
            
            myLineChart = new google.visualization.ChartWrapper({
                chartType:   'LineChart',
                containerId: 'exampleChart',
                dataTable:   data,
                options:     options
            });
            
            document.getElementById("exampleChart").addEventListener( "mousemove", mouseMoveScript );
            document.getElementById("exampleChart").addEventListener( "mousedown", mouseDownScript );
            document.getElementById("exampleChart").addEventListener( "mouseup", mouseUpScript );
            
            drawChart();
        }

        function drawChart() {
            myLineChart.draw();
        }
        
        function selectPoints( mx, my ) {
            var chartLayout    = myLineChart.getChart().getChartLayoutInterface();
            var chartContainer = document.getElementById( myLineChart.getContainerId() );
            var chartBounds    = chartContainer.getBoundingClientRect();
            if ( ( ( (chartBounds.left + window.pageXOffset) <= mx ) && ( (chartBounds.left + chartBounds.width  + window.pageXOffset) >= mx ) ) &&
                 ( ( (chartBounds.top  + window.pageYOffset) <= my ) && ( (chartBounds.top  + chartBounds.height + window.pageYOffset) >= my ) ) ){
                var selection = [];
                var dataTable = myLineChart.getDataTable();
                for (var row = 0; row < dataTable.getNumberOfRows(); row++) {
                    for (var col = 1; col < dataTable.getNumberOfColumns(); col++) {
                        var point = chartLayout.getBoundingBox('point#' + (col - 1) + '#' + row);
                        if( point != null ){
                            if ((((chartBounds.left + point.left) >= (mx - point.width)) &&
                                 ((chartBounds.left + point.left + point.width) <= (mx + point.width))) &&
                                (((chartBounds.top + point.top) >= (my - point.height)) &&
                                 ((chartBounds.top + point.top + point.height) <= (my + point.height)))) {
                              selection.push({row: row, column: col});
                            }
                        }
                    }
                }
            
                if( selection.length > 0 ){
                    var item = selection[0];
                    selectedPoint = selection[0];
                } else {
                    selectedPoint = null;
                }
                
                myLineChart.getChart().setSelection( selection );
            }
        }
        
        function mouseMoveScript( e ){
            var x = e.clientX;
            var y = e.clientY;
            var coor = "Coordinates: (" + x + "," + y + ")";
            document.getElementById("output").innerHTML = coor;
            if( selectedPoint != null ){
                var chartContainer  = document.getElementById( myLineChart.getContainerId() );
                var chartBounds    = chartContainer.getBoundingClientRect();
                var chartLayout     = myLineChart.getChart().getChartLayoutInterface()
                var chartAreaBounds = chartLayout.getChartAreaBoundingBox();
                var chartMinX       = chartLayout.getHAxisValue( chartAreaBounds.left );
                var chartMaxX       = chartLayout.getHAxisValue( chartAreaBounds.left + chartAreaBounds.width );
                var chartMinY       = chartLayout.getVAxisValue( chartAreaBounds.top + chartAreaBounds.height );
                var chartMaxY       = chartLayout.getVAxisValue( chartAreaBounds.top );
                
                var dataTable = myLineChart.getDataTable();
                var spX = ( x - chartBounds.left - chartAreaBounds.left ) / chartAreaBounds.width  * ( chartMaxX - chartMinX );
                var spY = ( chartAreaBounds.height - ( y - chartBounds.top  - chartAreaBounds.top ) ) / chartAreaBounds.height * ( chartMaxY - chartMinY );
                dataTable.setValue( selectedPoint.row, 0, spX );
                dataTable.setValue( selectedPoint.row, selectedPoint.column, spY );
                drawChart();
            }
        }
        
        function mouseDownScript( e ){
            var mx = e.clientX;
            var my = e.clientY;
            
            if( e.target ){
                targ = e.target;
                selectPoints( mx, my );
            } else if (e.srcElement) {
                targ = e.srcElement;
            }
            var tname;
            tname = targ.tagName;
        }
        
        function mouseUpScript( e ){
            if( selectedPoint != null ){
                selectedPoint = null;
            }
        }
html, body {
  height: 100%;
  margin: 0px 0px 0px 0px;
  padding: 0px 0px 0px 0px;
}

#select_div {
  border: 1px dashed #3366cc;
  position: absolute;
  z-index: 1000;
}

.exampleChart{
  height: 100%;
}

.hidden {
  display: none;
  visibility: hidden;
}
<script src="https://www.gstatic.com/charts/loader.js"></script>
<div id="output"></div>
<div>
    <div id="exampleChart"  style="width: 900px; height: 500px;"></div>
</div>

发布评论

评论列表(0)

  1. 暂无评论