Skip to content

Instantly share code, notes, and snippets.

@oosby
Created November 2, 2015 04:58
Show Gist options
  • Save oosby/6a87ad0d39cdab040e05 to your computer and use it in GitHub Desktop.
Save oosby/6a87ad0d39cdab040e05 to your computer and use it in GitHub Desktop.
React canvas widget for plotting simple points
var Graph = React.createClass({
displayName: 'graphWidget',
getDefaultProps() {
return {
backgroundColor: 'aliceblue',
canvasHeight: 200,
canvasWidth: 200,
clickThreshold: 10,
colorMatrix: {
2: 'lime',
4: 'gold',
6: 'orange',
8: 'red'
},
lineWidth: 2,
pointRadius: 4,
points: [2,6,12,4,9]
};
},
getInitialState() {
return {
coordList: this.getCoordList(),
ctx: null,
currentCoords: null
};
},
componentDidMount() {
this.setState({
ctx: this.refs.canvas.getContext("2d")
}, function() {
this.drawBgShape();
this.drawGraphLines();
this.drawPoints();
});
},
canvasStyle: {},
clearCanvas() {
const {canvasHeight, canvasWidth} = this.props;
const {ctx} = this.state;
if (ctx) {
ctx.clearRect(0,0, canvasWidth, canvasHeight);
}
},
getColor(val) {
const {colorMatrix} = this.props;
const keys = Object.keys(colorMatrix);
var output = null;
keys.some( (key, idx, arr) => {
if (+val === +key) {
output = colorMatrix[key];
return true;
}
if (+val < +key && +val > arr[idx + 1]) {
output = colorMatrix[key];
return true;
}
if (+val > key) {
output = colorMatrix[keys[keys.length-1]];
}
});
return output;
},
getCoordList() {
const {points, canvasHeight, canvasWidth, pointRadius} = this.props;
const innerWidth = canvasWidth - (pointRadius * 4);
const innerHeight = canvasHeight - (pointRadius * 8);
const max = Math.max(...points);
const yRatio = innerHeight / max;
const xIncrement = innerWidth / (points.length-1)
var output = [];
points.map( (req, idx) => {
output.push({
x: (xIncrement * idx) + (pointRadius * 2),
y: canvasHeight - (req * yRatio),
point: req
});
});
return output;
},
getGradient(startCoords, endCoords, startColor, endColor) {
const {ctx} = this.state;
var output = null;
if (ctx) {
if (startColor && endColor) {
output = ctx.createLinearGradient(startCoords.x, startCoords.y, endCoords.x, endCoords.y);
output.addColorStop(0, startColor);
output.addColorStop(.4, startColor);
output.addColorStop(1, endColor);
}
}
return output;
},
drawBgShape() {
const {ctx, coordList} = this.state;
const {backgroundColor, canvasHeight, pointRadius} = this.props;
const innerHeight = canvasHeight - (pointRadius * 8);
var currentCoords = {};
if (!ctx) {return false;}
this.clearCanvas(ctx);
ctx.fillStyle = backgroundColor;
ctx.strokeStyle = 'transparent';
ctx.beginPath();
ctx.moveTo(coordList[0].x, innerHeight);
coordList.map(list => {
ctx.lineTo(list.x, list.y);
ctx.stroke();
currentCoords = list;
});
ctx.lineTo(currentCoords.x, innerHeight);
ctx.fill();
},
drawGraphLines() {
const {coordList, ctx} = this.state;
const {canvasWidth} = this.props;
if (!ctx) {return false;}
coordList.map((list, idx, arr) => {
if (idx === 0) {
this.drawLine(list,list);
} else if (idx === (arr.length-1)) {
this.drawLine(arr[idx-1],list);
} else {
const endColor = this.getColor(list.point);
const startColor = this.getColor(arr[idx-1].point);
this.drawLine(arr[idx-1],list, startColor, endColor);
}
});
},
drawLine(startCoords, endCoords, startColor, endColor) {
const {ctx} = this.state;
const {lineWidth} = this.props;
var grad;
if (!ctx) {return false;}
if (startColor && endColor) {
grad = this.getGradient(startCoords, endCoords, startColor, endColor);
}
ctx.beginPath();
ctx.strokeStyle = grad;
ctx.lineWidth = lineWidth;
ctx.moveTo(startCoords.x, startCoords.y);
ctx.lineTo(endCoords.x, endCoords.y);
ctx.stroke();
ctx.closePath();
},
drawPoints() {
const {pointRadius} = this.props;
const {currentCoords, coordList, ctx} = this.state;
if (!ctx) {return false;}
coordList.map((list, idx) =>{
const color = this.getColor(list.point);
ctx.beginPath();
ctx.arc(list.x, list.y, pointRadius, 0, Math.PI * 2);
ctx.fillStyle = color;
ctx.fill();
ctx.closePath();
ctx.font = "18px Helvetica";
ctx.fillStyle = '#333';
ctx.textAlign = 'center';
ctx.fillText(list.point, list.x, (list.y - (pointRadius*2)));
});
},
handleClick(e) {
const {clickThreshold} = this.props;
const {coordList} = this.state;
coordList.some(coord => {
if (e.clientX <= (coord.x + clickThreshold) &&
e.clientX >= (coord.x - clickThreshold) &&
e.clientY <= (coord.y + clickThreshold) &&
e.clientY >= (coord.y - clickThreshold)) {
alert(coord);
return true;
}
})
},
render() {
const {canvasWidth, canvasHeight} = this.props;
return (
<canvas onClick={this.handleClick} ref="canvas" style={this.canvasStyle} id="graph" width={canvasWidth} height={canvasHeight}></canvas>
);
}
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment