Skip to content

Instantly share code, notes, and snippets.

@lourd
Created May 26, 2015 14:21
Show Gist options
  • Save lourd/c2fd4af98f7292ce6a57 to your computer and use it in GitHub Desktop.
Save lourd/c2fd4af98f7292ce6a57 to your computer and use it in GitHub Desktop.
D3 and React Integration Example
function createChart(dom, props){
var width = props.width;
var height = props.height;
width = width + 200;
var data = props.data;
var sum = data.reduce(function(memo, num){ return memo + num.count; }, 0);
var chart = d3.select(dom).append('svg').attr('class', 'd3').attr('width', width).attr('height', height)
.append("g")
.attr("transform", "translate(" + (props.width/2) + "," + (height/2) + ")");
var outerRadius = props.width/2.2;
var innerRadius = props.width/8;
var arc = d3.svg.arc()
.outerRadius(outerRadius)
.innerRadius(innerRadius);
var colors = ['#FD9827', '#DA3B21', '#3669C9', '#1D9524', '#971497'];
var pie = d3.layout.pie()
.value(function (d) { return d.count; });
var g = chart.selectAll(".arc")
.data(pie(data))
.enter().append("g")
.attr("class", "arc")
.on("click", function(d) {
alert('you clicked ' + d.data.name)
})
.on('mouseover', function (d, i) {
d3.select(this)
.transition()
.duration(500)
.ease('bounce')
.attr('transform', function (d) {
var dist = 10;
d.midAngle = ((d.endAngle - d.startAngle) / 2) + d.startAngle;
var x = Math.sin(d.midAngle) * dist;
var y = -Math.cos(d.midAngle) * dist;
return 'translate(' + x + ',' + y + ')';
});
d3.select(this).append("text").style("fill", function(d) { return colors[i]; }).attr("id", "percent")
.attr('transform', "translate(0,-5)")
.attr("text-anchor", "middle").attr("dy", ".35em").style("font", "bold 15px Arial")
.text(function(d) { return (((d.value/sum)*100).toFixed(1) + " %"); });
g.filter(function(e) { return e.value != d.value; }).style('opacity',0.5);
}).on('mouseout', function (d, i) {
d3.select(this)
.transition()
.duration(500)
.ease('bounce')
.attr('transform', 'translate(0,0)');
d3.select("#percent").remove();
g.filter(function(e) { return e.value != d.value; }).style('opacity',1)
});
g.append("path")
.style("fill", function(d, i) { return colors[i]; })
.transition().delay(function(d, i) { return i * 400; }).duration(500)
.attrTween('d', function(d) {
var i = d3.interpolate(d.startAngle, d.endAngle);
return function(t) {
d.endAngle = i(t);
return arc(d);
}
});
var center =
g.filter(function(d) { return d.endAngle - d.startAngle > .1; }).append("text").style("fill", "white")
.attr('transform', function(d){
return "translate(" + arc.centroid(d) + ")";
})
.attr("text-anchor", "middle").attr("dy", ".35em")
.text(function(d) { return d.value; });
var legend = chart.selectAll(".legend")
.data(data)
.enter().append("g")
.attr("class", "legend")
.attr("transform", function (d, i) {
return "translate(150," + (-i * 20) + ")";
});
var rect = legend.append("rect")
.attr("width", 18)
.attr("height", 18)
.style("fill", function(d, i) { return colors[i]; }).style('opacity', 0);
var name = legend.append("text")
.attr("x", 24)
.attr("y", 12)
.text(function (d) {
var text = d.name;
if(text.length >30){
text = text.substring(0,26);
text = text + '...';
}
return text;
}).style('opacity', 0);
rect.transition().delay(function(d, i) { return i * 400; }).duration(1000).style('opacity',1);
name.transition().delay(function(d, i) { return i * 400; }).duration(1000).style('opacity',1);
};
var PieChart = React.createClass({
propTypes: {
width: React.PropTypes.number,
height: React.PropTypes.number,
title: React.PropTypes.string,
data: React.PropTypes.array.isRequired,
},
getDefaultProps: function() {
return {
width: 300,
height: 350,
title: '',
Legend: true,
};
},
render: function() {
return (
<div>
<h4> {this.props.title} </h4>
</div>
);
},
componentDidMount: function() {
var dom = this.getDOMNode();
createChart(dom, this.props);
},
shouldComponentUpdate: function() {
var dom = this.getDOMNode();
createChart(dom, this.props);
return false;
}
});
var data = [
{name: "Apples", count: 10},
{name: "Oranges", count: 20},
{name: "Bananas", count: 5},
{name: "Blueberries", count: 42},
{name: "mangoes ", count: 29}
];
React.render(<PieChart data={data} title="Sample Fruits"/>, document.body);
var D3Legend = React.createClass({
propTypes: {
width: React.PropTypes.number,
height: React.PropTypes.number,
colors: React.PropTypes.array.isRequired,
data: React.PropTypes.array.isRequired,
},
render: function() {
var color = this.props.colors;
var data = this.props.data;
var elements = data.map(function(item, i){
return (
<LegendElement color={color} xpos="0" ypos={100+i*20} data={item.name} key={i} ikey={i}/>
)
})
return(
<svg className="legend" width={this.props.width} height={this.props.height}>{elements}</svg>
);
}
});
var LegendElement = React.createClass({
render: function() {
var position = "translate(" + this.props.xpos + "," + this.props.ypos + ")";
return (
<g transform={position}>
<rect width="18" height="18" fill={this.props.color[this.props.ikey]}></rect>
<text x="24" y="9" dy=".35em">{this.props.data}</text>
</g>
);
}
});
var Sector = React.createClass({
getInitialState: function() {
return {text: '', opacity:'arc'};
},
render: function() {
var outerRadius = this.props.width/2.2;
var innerRadius = this.props.width/8;
var arc = d3.svg.arc()
.outerRadius(outerRadius)
.innerRadius(innerRadius);
var data = this.props.data;
var center = "translate(" + arc.centroid(data) + ")";
var percentCenter = "translate(0,3)";
var color = this.props.colors;
return (
<g onMouseOver={this.onMouseOver} onMouseOut={this.onMouseOut} onClick={this.onClick}>
<path className={this.state.opacity} fill={color[this.props.ikey]} d={arc(this.props.data)}></path>
<text fill="white" transform={center} textAnchor="middle" fontSize="15px">{data.value}</text>
<text fill={color[this.props.ikey]} stroke={color} fontSize="15px" transform={percentCenter} textAnchor="middle">{this.state.text}</text>
</g>
);
},
onMouseOver: function() {
this.setState({text: '', opacity:'arc-hover'});
var percent = (this.props.data.value/this.props.total)*100;
percent = percent.toFixed(1);
this.setState({text: percent + " %"});
},
onMouseOut: function() {
this.setState({text: '', opacity:'arc'});
},
onClick: function() {
alert("You clicked "+this.props.name);
}
});
var DataSeries = React.createClass({
propTypes: {
width: React.PropTypes.number.isRequired,
height: React.PropTypes.number.isRequired,
color: React.PropTypes.array,
data: React.PropTypes.array.isRequired,
},
render: function() {
var color = this.props.colors;
var data = this.props.data;
var width = this.props.width;
var height = this.props.height;
var pie = d3.layout.pie();
var result = data.map(function(item){
return item.count;
})
var names = data.map(function(item){
return item.name;
})
var sum = result.reduce(function(memo, num){ return memo + num; }, 0);
var position = "translate(" + (width)/2 + "," + (height)/2 + ")";
var bars = (pie(result)).map(function(point, i) {
return (
<Sector data={point} ikey={i} key={i} name={names[i]} colors={color} total=
{sum} width={width} height={height}/>
)
});
return (
<g transform={position}>{bars}</g>
);
}
});
var D3Chart = React.createClass({
propTypes: {
width: React.PropTypes.number.isRequired,
height: React.PropTypes.number.isRequired,
children: React.PropTypes.node,
},
render: function() {
return (
<svg width={this.props.width} height={this.props.height}>
{this.props.children}</svg>
);
}
});
var D3PieChart = React.createClass({
propTypes: {
width: React.PropTypes.number,
height: React.PropTypes.number,
title: React.PropTypes.string,
data: React.PropTypes.array.isRequired,
},
getDefaultProps: function() {
return {
width: 300,
height: 350,
title: '',
Legend: true,
};
},
render: function() {
var colors = ['#FD9827', '#DA3B21', '#3669C9', '#1D9524', '#971497'];
return (
<div>
<h4> {this.props.title} </h4>
<D3Chart width={this.props.width} height={this.props.height}>
<DataSeries data={this.props.data} colors={colors} width=
{this.props.width} height={this.props.height}/>
</D3Chart>
<D3Legend data={this.props.data} colors={colors} width={this.props.width - 100} height={this.props.height} />
</div>
);
}
});
var data = [
{name: "Apples", count: 10},
{name: "Oranges", count: 20},
{name: "Bananas", count: 5},
{name: "Blueberries", count: 42},
{name: "mangoes ", count: 29}
];
React.render(<D3PieChart data={data} title="Sample Fruits"/>, document.body);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment