Skip to content

Commit

Permalink
Add curves to LineSeriesCanvas (#880)
Browse files Browse the repository at this point in the history
* Add curves to LineSeriesCanvas

* my visual linter got turned off, extreme atom eyeroll

* address comment
  • Loading branch information
mcnuttandrew authored Aug 13, 2018
1 parent aab5d4f commit 5ebe092
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 55 deletions.
10 changes: 5 additions & 5 deletions docs/line-series.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ Top-to-bottom position of the top edge of the series.
#### color (optional)
Type: `string|number`
Default: see [colors](colors.md)
Color of the line series.
Color of the line series.
By default, you can pass a literal color to the series (i.e. "red" or "#f70"). You can also define a color scale at the top level, and pass a number which will be interpolated by the scale. If nothing is provided, lineSeries will be colored according to react-vis default scale.

#### curve (optional)
Expand Down Expand Up @@ -65,7 +65,7 @@ A color for the series. Will override color if both are provided.

##### strokeDasharray (optional)
Type: `string`
Specify a custom `stroke-dasharray` attribute which controls the pattern of dashes and gaps used to stroke paths.
Specify a custom `stroke-dasharray` attribute which controls the pattern of dashes and gaps used to stroke paths. For the canvas version of this series, this should be an array of values, ala [7, 5].

##### strokeStyle (optional)
Type: `string`
Expand All @@ -91,11 +91,11 @@ An object which holds CSS properties that will be applied to the SVG element(s)
Note - interacting with a line may be difficult especially with the standard width. To address that, consider:
- the proximity handlers - onNearestX, onNearestXY;
- increasing the width of your line to make it easier to reach with the mouse,
- creating a near-transparent line series with extra width to catch mouse events.
- creating a near-transparent line series with extra width to catch mouse events.

#### onNearestX (optional)
Type: `function(value, {event, innerX, index})`
A callback function which is triggered each time the mouse pointer moves. It can access the datapoint of the mark whose x position is the closest to that of the cursor.
A callback function which is triggered each time the mouse pointer moves. It can access the datapoint of the mark whose x position is the closest to that of the cursor.
Callback is triggered with two arguments. `value` is the data point, `info` object has following properties:
- `innerX` is the left position of the mark;
- `index` is the index of the data point in the array of data;
Expand All @@ -104,7 +104,7 @@ See [interaction](interaction.md)

#### onNearestXY (optional)
Type: `function(value, {event, innerX, innerY, index})`
A callback function which is triggered each time the mouse pointer moves. It can access the datapoint of the mark whose position is the closest to that of the cursor.
A callback function which is triggered each time the mouse pointer moves. It can access the datapoint of the mark whose position is the closest to that of the cursor.
Callback is triggered with two arguments. `value` is the data point, `info` object has following properties:
- `innerX` is the left position of the mark;
- `innerY` is the top position of the mark;
Expand Down
101 changes: 58 additions & 43 deletions showcase/plot/line-chart.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,61 +20,76 @@

import React from 'react';
import {curveCatmullRom} from 'd3-shape';
import ShowcaseButton from '../showcase-components/showcase-button';

import {
XYPlot,
XAxis,
YAxis,
HorizontalGridLines,
VerticalGridLines,
LineSeries
LineSeries,
LineSeriesCanvas
} from 'index';

export default class Example extends React.Component {
state = {
useCanvas: false
}
render() {
const {useCanvas} = this.state;
const content = useCanvas ? 'TOGGLE TO SVG' : 'TOGGLE TO CANVAS';
const Line = useCanvas ? LineSeriesCanvas : LineSeries;

return (
<XYPlot
width={300}
height={300}>
<HorizontalGridLines />
<VerticalGridLines />
<XAxis title="X Axis" position="start"/>
<YAxis title="Y Axis"/>
<LineSeries
className="first-series"
data={[
{x: 1, y: 3},
{x: 2, y: 5},
{x: 3, y: 15},
{x: 4, y: 12}
]}/>
<LineSeries
className="second-series"
data={null}/>
<LineSeries
className="third-series"
curve={'curveMonotoneX'}
style={{
strokeDasharray: '2 2'
}}
data={[
{x: 1, y: 10},
{x: 2, y: 4},
{x: 3, y: 2},
{x: 4, y: 15}
]}
strokeDasharray="7, 3"
/>
<LineSeries
className="fourth-series"
curve={curveCatmullRom.alpha(0.5)}
data={[
{x: 1, y: 7},
{x: 2, y: 11},
{x: 3, y: 9},
{x: 4, y: 2}
]}/>
</XYPlot>
<div>
<ShowcaseButton
onClick={() => this.setState({useCanvas: !useCanvas})}
buttonContent={content}/>
<XYPlot
width={300}
height={300}>
<HorizontalGridLines />
<VerticalGridLines />
<XAxis title="X Axis" position="start"/>
<YAxis title="Y Axis"/>
<Line
className="first-series"
data={[
{x: 1, y: 3},
{x: 2, y: 5},
{x: 3, y: 15},
{x: 4, y: 12}
]}/>
<Line
className="second-series"
data={null}/>
<Line
className="third-series"
curve={'curveMonotoneX'}
data={[
{x: 1, y: 10},
{x: 2, y: 4},
{x: 3, y: 2},
{x: 4, y: 15}
]}
strokeDasharray={useCanvas ? [7, 3] : '7, 3'}
/>
<Line
className="fourth-series"
curve={curveCatmullRom.alpha(0.5)}
style={{
// note that this can not be translated to the canvas version
strokeDasharray: '2 2'
}}
data={[
{x: 1, y: 7},
{x: 2, y: 11},
{x: 3, y: 9},
{x: 4, y: 2}
]}/>
</XYPlot>
</div>
);
}
}
4 changes: 3 additions & 1 deletion showcase/plot/linemark-chart.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,10 @@ export default class Example extends React.Component {
<LineMarkSeries
className="linemark-series-example"
style={{
stroke: 'white'
strokeWidth: '3px'
}}
lineStyle={{stroke: 'red'}}
markStyle={{stroke: 'blue'}}
data={[
{x: 1, y: 10},
{x: 2, y: 5},
Expand Down
21 changes: 16 additions & 5 deletions src/plot/series/line-series-canvas.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
// THE SOFTWARE.
import PropTypes from 'prop-types';
import {rgb} from 'd3-color';
import * as d3Shape from 'd3-shape';

import {DEFAULT_OPACITY} from 'theme';
import {getAttributeFunctor, getAttributeValue} from 'utils/scales-utils';
Expand All @@ -36,6 +37,7 @@ class LineSeriesCanvas extends AbstractSeries {

static renderLayer(props, ctx) {
const {
curve,
data,
marginLeft,
marginTop,
Expand All @@ -52,19 +54,29 @@ class LineSeriesCanvas extends AbstractSeries {
const strokeColor = rgb(stroke);
const newOpacity = getAttributeValue(props, 'opacity');
const opacity = Number.isFinite(newOpacity) ? newOpacity : DEFAULT_OPACITY;
let line = d3Shape.line()
.x(row => x(row) + marginLeft)
.y(row => y(row) + marginTop);
if (typeof curve === 'string' && d3Shape[curve]) {
line = line.curve(d3Shape[curve]);
} else if (typeof curve === 'function') {
line = line.curve(curve);
}

ctx.beginPath();
ctx.moveTo(x(data[0]) + marginLeft, y(data[0]) + marginTop);
data.forEach(row => ctx.lineTo(x(row) + marginLeft, y(row) + marginTop));

ctx.strokeStyle = `rgba(${strokeColor.r}, ${strokeColor.g}, ${strokeColor.b}, ${opacity})`;
ctx.lineWidth = strokeWidth;

if (strokeDasharray) {
ctx.setLineDash(strokeDasharray);
}

line.context(ctx)(data);
ctx.stroke();
ctx.closePath();
// set back to default
ctx.lineWidth = 1;
ctx.setLineDash([]);
}

render() {
Expand All @@ -75,8 +87,7 @@ class LineSeriesCanvas extends AbstractSeries {
LineSeriesCanvas.displayName = 'LineSeriesCanvas';
LineSeriesCanvas.defaultProps = {
...AbstractSeries.defaultProps,
strokeWidth: 1,
strokeDasharray: ''
strokeWidth: 2
};

LineSeriesCanvas.propTypes = {
Expand Down
8 changes: 7 additions & 1 deletion tests/components/line-series-tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ test('LineSeries: basic rendering', t => {

test('LineSeries: Showcase Example - LineChart', t => {
const $ = mount(<LineChart />);
t.equal($.text(), '1.01.52.02.53.03.54.0X Axis2468101214Y Axis', 'should find the right text content');
t.equal($.text(), 'TOGGLE TO CANVAS1.01.52.02.53.03.54.0X Axis2468101214Y Axis', 'should find the right text content');
t.equal($.find('.rv-xy-plot__series--line').length, 3, 'should find the right number of series');

['first-series', 'third-series', 'fourth-series'].forEach(customClassName => {
Expand All @@ -59,6 +59,12 @@ test('LineSeries: Showcase Example - LineChart', t => {
});
t.equal($.find('.second-series').length, 0,
'there should be no line with the class second series bc it has null data and should be filtered out');

$.find('button').simulate('click');
t.equal($.find('.rv-xy-plot__series--line').length, 0, 'should find no more line series');

$.find('button').simulate('click');
t.equal($.find('.rv-xy-plot__series--line').length, 3, 'should find no more line series');
t.end();
});

Expand Down

0 comments on commit 5ebe092

Please sign in to comment.