1
0
Fork 0

Stacked area chart animations

This commit is contained in:
Gregory Eremin 2015-03-12 20:11:52 +07:00
parent a660f4ea3b
commit 93a88481c4
1 changed files with 101 additions and 33 deletions

View File

@ -1,17 +1,21 @@
var StackedAreaChart = React.createClass({ var StackedAreaChart = React.createClass({
mixins: [Router.Navigation, Router.State], mixins: [Router.Navigation, Router.State, Chart],
numElements: 10, numElements: 10,
maxWeeks: 20,
height: 250, height: 250,
getInitialState: function() { getInitialState: function() {
return { return {
currentApi: null,
currentParams: null,
item: this.props.items[0], item: this.props.items[0],
rawData: [], rawData: [],
top: [], top: [],
max: 1, max: 1,
weeks: [], weeks: [],
canvasWidth: 500 canvasWidth: 0,
state: 'initial'
}; };
}, },
@ -23,21 +27,29 @@ var StackedAreaChart = React.createClass({
componentWillReceiveProps: function(newProps) { componentWillReceiveProps: function(newProps) {
this.setState({ this.setState({
'item': newProps.items[0], item: newProps.items[0],
'sort': 'commits' sort: 'commits',
state: 'newProps'
}, this.fetchData); }, this.fetchData);
}, },
calculateViewBoxWidth: function() { shouldComponentUpdate: function(newProps, newState) {
this.setState({ // console.log("Should update?", newState.state);
canvasWidth: this.refs.svg.getDOMNode().offsetWidth if (newState.canvasWidth === 0) {
}); return false;
}
if (newState.state !== 'newPoints') {
return false;
}
// console.log("Updating!");
return true;
}, },
handleFilter: function(thing, i) { handleFilter: function(thing, i) {
if (this.props.items[i] !== this.state.item) { if (this.props.items[i] !== this.state.item) {
this.setState({ this.setState({
item: this.props.items[i] item: this.props.items[i],
state: 'newProps'
}, this.fetchData); }, this.fetchData);
} }
}, },
@ -49,10 +61,26 @@ var StackedAreaChart = React.createClass({
}, },
fetchData: function() { fetchData: function() {
$.get(this.props.api, this.apiParams(), function(res){ if (!this.apiParams().item) {
this.setState({ return;
rawData: res }
}, this.buildPoints); if (this.state.currentApi === this.props.api &&
this.state.currentParams === JSON.stringify(this.apiParams())) {
return;
}
// console.log('-----> fetching', this.props.api, this.state.item);
this.setState({
currentApi: this.props.api,
currentParams: JSON.stringify(this.apiParams()),
state: 'loadingData'
}, function() {
$.get(this.props.api, this.apiParams(), function(res){
this.setState({
rawData: res,
state: 'newData'
}, this.buildPoints);
}.bind(this));
}.bind(this)); }.bind(this));
}, },
@ -99,10 +127,12 @@ var StackedAreaChart = React.createClass({
// return _.sum(_.values(items)); // return _.sum(_.values(items));
// })); // }));
// console.log("New points!");
this.setState({ this.setState({
top: top, top: top,
max: max, max: max,
weeks: weeks weeks: weeks,
state: 'newPoints'
}); });
}, },
@ -117,18 +147,11 @@ var StackedAreaChart = React.createClass({
d.unshift('M0,'+ maxHeight); d.unshift('M0,'+ maxHeight);
d.push('L'+ maxWidth +','+ maxHeight +'Z'); d.push('L'+ maxWidth +','+ maxHeight +'Z');
// for (var i = 0; i < missing; i++) {
// d.push('L'+ i +','+ this.props.height/2);
// }
// for (var i = 0; i < points.length; i++) {
// d.push('L'+ missing+i +','+ points[i]);
// }
// d.push('L'+ this.props.width +','+ this.props.height/2, 'Z');
return d.join(' '); return d.join(' ');
}, },
render: function() { render: function() {
// console.log("Rendering!");
var maxWidth = this.state.canvasWidth, var maxWidth = this.state.canvasWidth,
maxHeight = this.height, maxHeight = this.height,
rtop = this.state.top.reverse(), rtop = this.state.top.reverse(),
@ -151,27 +174,49 @@ var StackedAreaChart = React.createClass({
return [week, points]; return [week, points];
}) })
.sort(0) .sort(0)
.reverse()
.take(this.maxWeeks)
.reverse()
.value(); .value();
var paths = _.reduce(rtop, function(res, item, i) { var paths = _.reduce(rtop, function(res, item, i) {
res[item] = _.map(points, function(pair) { res[item] = _.map(points, function(pair) {
return pair[1][i]; return pair[1][i];
}).slice(-15); });
return res; return res;
}, {}); }, {});
var paths = _.map(rtop, function(item, i) {
var itemPoints = _.map(points, function(pair) {
return pair[1][i];
});
return[item, itemPoints];
});
var i = -1;
var colors = {} var colors = {}
var areas = _.map(paths, function(path, item) { // console.log('----- Areas!');
i++; var areas = _.map(paths, function(pair, i) {
var item = pair[0], path = pair[1];
colors[item] = Colors2[i]; colors[item] = Colors2[i];
// console.log("Building path for", item, path);
// console.log('Area', i);
return ( return (
<StackedArea key={'sa-item-'+ item} <StackedArea key={'area-'+ i}
item={item} item={item}
path={roundPathCorners(this.buildPathD(path), 5)} d={roundPathCorners(this.buildPathD(path), 5)}
color={Colors2[i]} /> color={colors[item]} />
); );
}.bind(this)); }.bind(this));
areas = areas.reverse();
for (var i = areas.length; i < this.numElements; i++) {
// console.log('Area (empty)', i);
var d = 'M0,'+ this.height +' L'+ maxWidth +','+ maxHeight +'Z';
areas.push(
<StackedArea key={'area-'+ i}
item={''}
d={d}
color="rgba(0, 0, 0, 0)" />
);
};
return ( return (
<div className="sachart-container"> <div className="sachart-container">
@ -184,10 +229,10 @@ var StackedAreaChart = React.createClass({
items={['commits']} items={['commits']}
value={'commits'} /> value={'commits'} />
</div> </div>
<svg ref="svg" className="sachart" <svg ref="svg" className="sachart" key="sachart-svg"
width="100%" height={maxHeight} width="100%" height={maxHeight}
viewBox={"0 0 "+ this.state.canvasWidth + " "+ maxHeight}> viewBox={"0 0 "+ this.state.canvasWidth + " "+ maxHeight}>
{areas.reverse()} {areas}
</svg> </svg>
<ul className="legend"> <ul className="legend">
{_.pairs(colors).map(function(pair){ {_.pairs(colors).map(function(pair){
@ -205,10 +250,33 @@ var StackedAreaChart = React.createClass({
}); });
var StackedArea = React.createClass({ var StackedArea = React.createClass({
mixins: [Chart],
easing: '0.55, 0.055, 0.675, 0.19',
getInitialState: function() {
return {lastd: ''};
},
componentDidMount: function() {
// console.log("-- mounted area");
},
componentWillReceiveProps: function(newProps) {
// console.log("New area props!", newProps.item);
this.setState({
lastd: this.props.d,
}, this.state.lastd === '' ? null : this.animateAll);
},
animateAll: function() {
// console.log("Animating area", this.props.item);
this.animate(this.refs.path, 'd', this.state.lastd, this.props.d);
},
render: function() { render: function() {
return ( return (
<path key={'sac-area-'+ this.props.item} <path ref="path"
d={this.props.path} d={this.props.d}
fill={this.props.color} fill={this.props.color}
shapeRendering="optimizeQuality" /> shapeRendering="optimizeQuality" />
); );