| OLD | NEW |
| 1 // | |
| 2 // Copyright 2014 Google Inc. All rights reserved. | 1 // Copyright 2014 Google Inc. All rights reserved. |
| 3 // | 2 // |
| 4 // Use of this source code is governed by a BSD-style | 3 // Use of this source code is governed by a BSD-style |
| 5 // license that can be found in the LICENSE file or at | 4 // license that can be found in the LICENSE file or at |
| 6 // https://developers.google.com/open-source/licenses/bsd | 5 // https://developers.google.com/open-source/licenses/bsd |
| 7 // | 6 // |
| 8 | 7 |
| 9 part of charted.charts; | 8 part of charted.charts; |
| 10 | 9 |
| 11 class LineChartRenderer extends CartesianRendererBase { | 10 class LineChartRenderer extends CartesianRendererBase { |
| 12 final Iterable<int> dimensionsUsingBand = const []; | 11 final Iterable<int> dimensionsUsingBand = const []; |
| 13 | 12 |
| 14 final bool alwaysAnimate; | 13 final bool alwaysAnimate; |
| 14 final bool showHoverCardOnTrackedDataPoints; |
| 15 final bool trackDataPoints; | 15 final bool trackDataPoints; |
| 16 final bool trackOnDimensionAxis; | 16 final bool trackOnDimensionAxis; |
| 17 final int quantitativeScaleProximity; | 17 final int quantitativeScaleProximity; |
| 18 | 18 |
| 19 bool _trackingPointsCreated = false; | 19 bool _trackingPointsCreated = false; |
| 20 List _xPositions = []; | 20 List _xPositions = []; |
| 21 | 21 |
| 22 // Currently hovered row/column | 22 // Currently hovered row/column |
| 23 int _savedOverRow = 0; | 23 int _savedOverRow = 0; |
| 24 int _savedOverColumn = 0; | 24 int _savedOverColumn = 0; |
| 25 | 25 |
| 26 int currentDataIndex = -1; | 26 int currentDataIndex = -1; |
| 27 | 27 |
| 28 @override | 28 @override |
| 29 final String name = "line-rdr"; | 29 final String name = "line-rdr"; |
| 30 | 30 |
| 31 LineChartRenderer( | 31 LineChartRenderer( |
| 32 {this.alwaysAnimate: false, | 32 {this.alwaysAnimate: false, |
| 33 this.showHoverCardOnTrackedDataPoints: false, |
| 33 this.trackDataPoints: true, | 34 this.trackDataPoints: true, |
| 34 this.trackOnDimensionAxis: false, | 35 this.trackOnDimensionAxis: false, |
| 35 this.quantitativeScaleProximity: 5}); | 36 this.quantitativeScaleProximity: 5}); |
| 36 | 37 |
| 37 // Returns false if the number of dimension axes on the area is 0. | 38 // Returns false if the number of dimension axes on the area is 0. |
| 38 // Otherwise, the first dimension scale is used to render the chart. | 39 // Otherwise, the first dimension scale is used to render the chart. |
| 39 @override | 40 @override |
| 40 bool prepare(ChartArea area, ChartSeries series) { | 41 bool prepare(ChartArea area, ChartSeries series) { |
| 41 _ensureAreaAndSeries(area, series); | 42 _ensureAreaAndSeries(area, series); |
| 42 if (trackDataPoints != false) { | 43 if (trackDataPoints != false) { |
| (...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 107 ..on('click', (d, i, e) => _mouseClickHandler(d, i, e)) | 108 ..on('click', (d, i, e) => _mouseClickHandler(d, i, e)) |
| 108 ..on('mouseover', (d, i, e) => _mouseOverHandler(d, i, e)) | 109 ..on('mouseover', (d, i, e) => _mouseOverHandler(d, i, e)) |
| 109 ..on('mouseout', (d, i, e) => _mouseOutHandler(d, i, e)); | 110 ..on('mouseout', (d, i, e) => _mouseOutHandler(d, i, e)); |
| 110 } | 111 } |
| 111 | 112 |
| 112 svgLines.exit.remove(); | 113 svgLines.exit.remove(); |
| 113 } | 114 } |
| 114 | 115 |
| 115 @override | 116 @override |
| 116 void dispose() { | 117 void dispose() { |
| 118 _disposer.dispose(); |
| 117 if (root == null) return; | 119 if (root == null) return; |
| 118 root.selectAll('.line-rdr-line').remove(); | 120 root.selectAll('.line-rdr-line').remove(); |
| 119 root.selectAll('.line-rdr-point').remove(); | 121 root.selectAll('.line-rdr-point').remove(); |
| 120 _disposer.dispose(); | |
| 121 } | 122 } |
| 122 | 123 |
| 123 @override | 124 @override |
| 124 void handleStateChanges(List<ChangeRecord> changes) { | 125 void handleStateChanges(List<ChangeRecord> changes) { |
| 125 var lines = host.querySelectorAll('.line-rdr-line'); | 126 var lines = host.querySelectorAll('.line-rdr-line'); |
| 126 if (lines == null || lines.isEmpty) return; | 127 if (lines == null || lines.isEmpty) return; |
| 127 | 128 |
| 128 for (int i = 0, len = lines.length; i < len; ++i) { | 129 for (int i = 0, len = lines.length; i < len; ++i) { |
| 129 var line = lines.elementAt(i), | 130 var line = lines.elementAt(i), |
| 130 column = int.parse(line.dataset['column']), | 131 column = int.parse(line.dataset['column']), |
| (...skipping 28 matching lines...) Expand all Loading... |
| 159 }) | 160 }) |
| 160 ..on('click', _mouseClickHandler) | 161 ..on('click', _mouseClickHandler) |
| 161 ..on('mousemove', _mouseOverHandler) // Ensure that we update values | 162 ..on('mousemove', _mouseOverHandler) // Ensure that we update values |
| 162 ..on('mouseover', _mouseOverHandler) | 163 ..on('mouseover', _mouseOverHandler) |
| 163 ..on('mouseout', _mouseOutHandler); | 164 ..on('mouseout', _mouseOutHandler); |
| 164 | 165 |
| 165 linePoints.exit.remove(); | 166 linePoints.exit.remove(); |
| 166 _trackingPointsCreated = true; | 167 _trackingPointsCreated = true; |
| 167 } | 168 } |
| 168 | 169 |
| 169 void _showTrackingCircles(int row) { | 170 void _showTrackingCircles(ChartEvent event, int row) { |
| 170 if (_trackingPointsCreated == false) { | 171 if (_trackingPointsCreated == false) { |
| 171 _createTrackingCircles(); | 172 _createTrackingCircles(); |
| 172 } | 173 } |
| 173 | 174 |
| 174 var yScale = area.measureScales(series).first; | 175 var yScale = area.measureScales(series).first; |
| 175 root.selectAll('.line-rdr-point').each((d, i, e) { | 176 root.selectAll('.line-rdr-point').each((d, i, e) { |
| 176 var x = _xPositions[row], | 177 var x = _xPositions[row], |
| 177 measureVal = area.data.rows.elementAt(row).elementAt(d); | 178 measureVal = area.data.rows.elementAt(row).elementAt(d); |
| 178 if (measureVal != null && measureVal.isFinite) { | 179 if (measureVal != null && measureVal.isFinite) { |
| 179 var color = colorForColumn(d), filter = filterForColumn(d); | 180 var color = colorForColumn(d), filter = filterForColumn(d); |
| (...skipping 10 matching lines...) Expand all Loading... |
| 190 e.attributes.remove('filter'); | 191 e.attributes.remove('filter'); |
| 191 } else { | 192 } else { |
| 192 e.attributes['filter'] = filter; | 193 e.attributes['filter'] = filter; |
| 193 } | 194 } |
| 194 } else { | 195 } else { |
| 195 e.style | 196 e.style |
| 196 ..setProperty('opacity', '$EPSILON') | 197 ..setProperty('opacity', '$EPSILON') |
| 197 ..setProperty('visibility', 'hidden'); | 198 ..setProperty('visibility', 'hidden'); |
| 198 } | 199 } |
| 199 }); | 200 }); |
| 201 |
| 202 if (showHoverCardOnTrackedDataPoints) { |
| 203 var firstMeasureColumn = series.measures.first; |
| 204 mouseOverController.add(new DefaultChartEventImpl( |
| 205 event.source, area, series, row, firstMeasureColumn, 0)); |
| 206 _savedOverRow = row; |
| 207 _savedOverColumn = firstMeasureColumn; |
| 208 } |
| 200 } | 209 } |
| 201 | 210 |
| 202 void _hideTrackingCircles() { | 211 void _hideTrackingCircles(ChartEvent event) { |
| 203 root.selectAll('.line-rdr-point') | 212 root.selectAll('.line-rdr-point') |
| 204 ..style('opacity', '0.0') | 213 ..style('opacity', '0.0') |
| 205 ..style('visibility', 'hidden'); | 214 ..style('visibility', 'hidden'); |
| 215 if (showHoverCardOnTrackedDataPoints) { |
| 216 mouseOutController.add(new DefaultChartEventImpl( |
| 217 event.source, area, series, _savedOverRow, _savedOverColumn, 0)); |
| 218 } |
| 206 } | 219 } |
| 207 | 220 |
| 208 int _getNearestRowIndex(num x) { | 221 int _getNearestRowIndex(num x) { |
| 209 var lastSmallerValue = 0; | 222 double lastSmallerValue = 0.0; |
| 210 var chartX = x - area.layout.renderArea.x; | 223 var chartX = x - area.layout.renderArea.x; |
| 211 for (var i = 0; i < _xPositions.length; i++) { | 224 for (var i = 0; i < _xPositions.length; i++) { |
| 212 var pos = _xPositions[i]; | 225 double pos = _xPositions[i].toDouble(); |
| 213 if (pos < chartX) { | 226 if (pos < chartX) { |
| 214 lastSmallerValue = pos; | 227 lastSmallerValue = pos; |
| 215 } else { | 228 } else { |
| 216 return i == 0 | 229 return i == 0 |
| 217 ? 0 | 230 ? 0 |
| 218 : (chartX - lastSmallerValue <= pos - chartX) ? i - 1 : i; | 231 : (chartX - lastSmallerValue <= pos - chartX) ? i - 1 : i; |
| 219 } | 232 } |
| 220 } | 233 } |
| 221 return _xPositions.length - 1; | 234 return _xPositions.length - 1; |
| 222 } | 235 } |
| 223 | 236 |
| 224 void _trackPointerInArea() { | 237 void _trackPointerInArea() { |
| 225 _trackingPointsCreated = false; | 238 _trackingPointsCreated = false; |
| 226 _disposer.add(area.onMouseMove.listen((ChartEvent event) { | 239 _disposer.add(area.onMouseMove.listen((ChartEvent event) { |
| 227 if (area.layout.renderArea.contains(event.chartX, event.chartY)) { | 240 if (area.layout.renderArea.contains(event.chartX, event.chartY)) { |
| 228 var row = _getNearestRowIndex(event.chartX); | 241 var row = _getNearestRowIndex(event.chartX); |
| 229 window.animationFrame.then((_) => _showTrackingCircles(row)); | 242 window.animationFrame.then((_) { |
| 243 _showTrackingCircles(event, row); |
| 244 }); |
| 230 } else { | 245 } else { |
| 231 _hideTrackingCircles(); | 246 _hideTrackingCircles(event); |
| 232 } | 247 } |
| 233 })); | 248 })); |
| 234 _disposer.add(area.onMouseOut.listen((ChartEvent event) { | 249 _disposer.add(area.onMouseOut.listen((ChartEvent event) { |
| 235 _hideTrackingCircles(); | 250 _hideTrackingCircles(event); |
| 236 })); | 251 })); |
| 237 } | 252 } |
| 238 | 253 |
| 239 void _mouseClickHandler(d, int i, Element e) { | 254 void _mouseClickHandler(d, int i, Element e) { |
| 240 if (area.state != null) { | 255 if (area.state != null) { |
| 241 area.state.select(int.parse(e.dataset['column'])); | 256 var selectedColumn = int.parse(e.dataset['column']); |
| 257 area.state.isSelected(selectedColumn) |
| 258 ? area.state.unselect(selectedColumn) |
| 259 : area.state.select(selectedColumn); |
| 242 } | 260 } |
| 243 if (mouseClickController != null && e.tagName == 'circle') { | 261 if (mouseClickController != null && e.tagName == 'circle') { |
| 244 var row = int.parse(e.dataset['row']), | 262 var row = int.parse(e.dataset['row']), |
| 245 column = int.parse(e.dataset['column']); | 263 column = int.parse(e.dataset['column']); |
| 246 mouseClickController.add( | 264 mouseClickController.add( |
| 247 new DefaultChartEventImpl(scope.event, area, series, row, column, d)); | 265 new DefaultChartEventImpl(scope.event, area, series, row, column, d)); |
| 248 } | 266 } |
| 249 } | 267 } |
| 250 | 268 |
| 251 void _mouseOverHandler(d, int i, Element e) { | 269 void _mouseOverHandler(d, int i, Element e) { |
| (...skipping 12 matching lines...) Expand all Loading... |
| 264 if (area.state != null && | 282 if (area.state != null && |
| 265 area.state.preview == int.parse(e.dataset['column'])) { | 283 area.state.preview == int.parse(e.dataset['column'])) { |
| 266 area.state.preview = null; | 284 area.state.preview = null; |
| 267 } | 285 } |
| 268 if (mouseOutController != null && e.tagName == 'circle') { | 286 if (mouseOutController != null && e.tagName == 'circle') { |
| 269 mouseOutController.add(new DefaultChartEventImpl( | 287 mouseOutController.add(new DefaultChartEventImpl( |
| 270 scope.event, area, series, _savedOverRow, _savedOverColumn, d)); | 288 scope.event, area, series, _savedOverRow, _savedOverColumn, d)); |
| 271 } | 289 } |
| 272 } | 290 } |
| 273 } | 291 } |
| OLD | NEW |