Skip to content
This repository has been archived by the owner on Dec 13, 2020. It is now read-only.

Commit

Permalink
Merge pull request #634 from metasfresh/dev-608
Browse files Browse the repository at this point in the history
Chart animations #608
  • Loading branch information
damianprzygodzki authored Apr 12, 2017
2 parents 388d95b + ebbbef8 commit 056ba6f
Show file tree
Hide file tree
Showing 5 changed files with 167 additions and 56 deletions.
83 changes: 75 additions & 8 deletions src/components/charts/BarChartComponent/data.js
Original file line number Diff line number Diff line change
@@ -1,30 +1,97 @@
const mapDataset = (dataset, labelField) => Object.keys(dataset)
import * as d3 from 'd3';

const mapDataset = (dataset, prevData, labelField) => Object.keys(dataset)
.filter(key => key[0] !== '_' && key !== labelField)
.map(key => ({
key,
value: dataset[key]
value: dataset[key],
valuePrev: prevData && prevData[key] ? prevData[key] : 0
}));

export const drawData = (svg, dimensions, ranges, data, labelField) => {
export const isYRangeChanged = (data, prevData, fields) => {
const keys = fields.map(field => field.fieldName);

const yprev = prevData && d3.max(prevData, d => {
return d3.max(keys, key => {
return d[key];
});
});

const ynext = d3.max(data, d => {
return d3.max(keys, key => {
return d[key];
});
});

return yprev !== ynext;
}

export const isXRangeChanged = (data, prevData, svg) => {

if(data.length!==(prevData && prevData.length)){
svg.select('g.datasets').selectAll('g').remove();
} else {
data.map((item, index) => {
if (prevData && JSON.stringify(Object.keys(data[index])) !==
JSON.stringify(Object.keys(prevData[index])) ) {
svg.select('g.datasets').selectAll('g').remove();
}
});
}
}

export const drawData = (
svg, dimensions, ranges, data, labelField, prev, fields, reRender) => {

if(reRender){
svg.select('g.datasets').selectAll('g').remove();
}

let chartData = [];
data.map((item, index) => {
chartData.push({data: item, prevData: prev ? prev[index] : 0});
});

const yChanged = isYRangeChanged(data, prev, fields);
isXRangeChanged(data, prev, svg);

const groups = svg.select('g.datasets')
.selectAll('g')
.data(data);
.data(chartData);

const bars = groups.enter().append('g')
.classed('bar-group', true)
.merge(groups)
.attr('transform', d => 'translate(' +
ranges.x0(d[labelField]) + ', 0)')
ranges.x0(d.data[labelField]) + ', 0)')
.selectAll('rect')
.data(d => mapDataset(d, labelField))
.data(d => mapDataset(d.data, d.prevData, labelField))

bars.enter().append('rect')
.classed('bar', true)
.merge(bars)
.attr('x', d => ranges.x1(d.key))
.attr('width', ranges.x1.bandwidth())

.attr('y', d => {

if(yChanged || reRender){
return dimensions.height;
} else {
return ranges.y(d.valuePrev);
}
})
.attr('height', d => {

if(yChanged || reRender){
return 0;
} else {
return dimensions.height - ranges.y(d.valuePrev);
}
})
.transition()
.duration(1000)
.attr('y', d => ranges.y(d.value))
.attr('height', d => dimensions.height - ranges.y(d.value))
.attr('fill', d => ranges.z(d.key))
.transition()
};
};
14 changes: 9 additions & 5 deletions src/components/charts/BarChartComponent/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,14 @@ class BarChartComponent extends Component {
}
}

draw(){
const { data, groupBy } = this.props;
draw(prev, reRender){
const { data, groupBy, fields } = this.props;
const { dimensions, ranges } = this.prepare();

drawData(this.svg, dimensions, ranges, data, groupBy.fieldName);
drawData(
this.svg, dimensions, ranges, data, groupBy.fieldName,
prev, fields, reRender
);
}

addResponsive = () => {
Expand All @@ -91,8 +94,9 @@ class BarChartComponent extends Component {
return !(this.props.reRender && !nextProps.reRender)
}

componentDidUpdate(){
this.draw();
componentDidUpdate(prevProps){
const { reRender } = this.props;
this.draw(prevProps.data, reRender);
}

render() {
Expand Down
1 change: 1 addition & 0 deletions src/components/charts/BarChartComponent/ranges.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as d3 from 'd3';

export const getX0Range = (width, data, groupBy) => {

return d3.scaleBand()
.range([0, width])
.padding(0.2)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import React, { Component } from 'react';
import * as d3 from 'd3';
import { getSvg } from './svg';

class PieChartComponent extends Component {
constructor(props){
super(props);
}
svg;

componentDidMount() {
const {data, responsive, colors} = this.props;
Expand All @@ -14,9 +13,15 @@ class PieChartComponent extends Component {

const dimensions = this.setDimensions();

this.drawChart(
this.setSvg(
dimensions.width,
dimensions.height,
dimensions.wrapperWidth
);

dimensions && this.drawChart(
dimensions.wrapperWidth, dimensions.width, dimensions.height,
dimensions.pie, dimensions.arc, data, color, dimensions.radius
dimensions.pie, dimensions.arc, data, color
);

if(responsive){
Expand All @@ -36,31 +41,40 @@ class PieChartComponent extends Component {
const color = d3.scaleOrdinal()
.range(colors);
this.clearChart();
const dimensions = this.setDimensions(chartWrapp.offsetWidth);
this.drawChart(
const dimensions =
chartWrapp && this.setDimensions(chartWrapp.offsetWidth);
dimensions && this.updateChart(
dimensions.wrapperWidth, dimensions.width, dimensions.height,
dimensions.pie, dimensions.arc, data, color
);
}

setSvg(width, height, wrapperWidth){
const {chartClass} = this.props;
this.svg = getSvg(chartClass, width, height, wrapperWidth);
}

setDimensions = (width = 400) => {
const {chartClass, responsive, fields, height} = this.props;
let wrapperWidth = 0;
let chartWidth = width;
let chartHeight = height;

const chartWrapper =
document.getElementsByClassName(chartClass + '-wrapper')[0];

if(responsive) {
wrapperWidth =
document.getElementsByClassName(chartClass + '-wrapper')[0]
.offsetWidth;
wrapperWidth = chartWrapper && chartWrapper.offsetWidth;
chartWidth = wrapperWidth;
}
const radius = Math.min(chartWidth, 0.66*chartHeight) / 2;
const arc =
d3.arc().outerRadius(radius * 0.8).innerRadius(radius * 0.4);
const pie = d3.pie()
.sort(null)
.value( d => d[fields[0].fieldName]);
.value( d => {
return d[fields[0].fieldName];
});

return {
wrapperWidth: wrapperWidth,
Expand All @@ -72,41 +86,44 @@ class PieChartComponent extends Component {
};
}
drawChart = (wrapperWidth, width, height, pie, arc, data, color) => {
const {chartClass, fields} = this.props;
const {fields} = this.props;

const svg = d3.select('.' + chartClass)
.attr('width', width)
.attr('height', height)
.append('g');
const slice = this.svg.select('.slices').selectAll('.pie-path')
.data(pie(data), function(d) {
return d;
})

const chart = svg
.attr('width', width)
.attr('height', height)
.append('g')
.attr('class', 'chart')
.attr('transform',
'translate(' + wrapperWidth / 2 + ',' + 0.66*height + ')'
);
slice.enter().append('path')
.attr('class', 'pie-path')
.style('fill', d => color(d.data[fields[0].fieldName]))
.transition().duration(1500)
.attrTween('d', d=>{
var i = d3.interpolate({startAngle: 0, endAngle: 0}, d);
return function(t) {
return arc(i(t));
}
})
slice.exit().remove();

this.drawLegend(this.svg, width, height, color);
};

chart.append('g')
.attr('class', 'slices');
chart.append('g')
.attr('class', 'labels');
chart.append('g')
.attr('class', 'lines');
updateChart = (wrapperWidth, width, height, pie, arc, data, color) => {
const {fields} = this.props;

const g = d3.select('.slices').selectAll('.arc')
.data(pie(data))
.enter().append('g')
.attr('class', 'arc');
const slice = this.svg.select('.slices').selectAll('.pie-path')
.data(pie(data), function(d) {
return d;
});

g.append('path')
.attr('d', arc)
slice.enter().append('path')
.attr('class', 'pie-path')
.style('fill', d => color(d.data[fields[0].fieldName]));
.style('fill', d => color(d.data[fields[0].fieldName]))
.attr('d', arc);

this.drawLegend(svg, width, height, color);
};
slice.exit().remove();

}

drawLegend = (svg, width, height, color) => {
const {groupBy, data} = this.props;
Expand Down Expand Up @@ -157,8 +174,9 @@ class PieChartComponent extends Component {
d3.select(window)
.on('resize.'+chartClass, () => {
this.clearChart();
const dimensions = this.setDimensions(chartWrap.offsetWidth);
this.drawChart(
const dimensions =
chartWrap && this.setDimensions(chartWrap.offsetWidth);
dimensions && this.drawChart(
dimensions.wrapperWidth, dimensions.width,
dimensions.height, dimensions.pie, dimensions.arc, data,
color
Expand All @@ -167,8 +185,7 @@ class PieChartComponent extends Component {
};

clearChart = () => {
const {chartClass} = this.props;
document.getElementsByClassName(chartClass)[0].childNodes[0].remove();
this.svg.select('.slices').selectAll('path').remove()
};

render() {
Expand Down
22 changes: 22 additions & 0 deletions src/components/charts/PieChartComponent/svg.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import * as d3 from 'd3';

export const getSvg = (chartClass, width, height, wrapperWidth) => {
const svg = d3.select('.' + chartClass)
.attr('width', width)
.attr('height', height)
.append('g');

const chart = svg
.attr('width', width)
.attr('height', height)
.append('g')
.attr('class', 'chart')
.attr('transform',
'translate(' + wrapperWidth / 2 + ',' + 0.66*height + ')'
);

chart.append('g')
.attr('class', 'slices');

return svg;
};

0 comments on commit 056ba6f

Please sign in to comment.