Skip to content

Commit

Permalink
feat(stack-filters): fix areas z-index + seamless areas transitions
Browse files Browse the repository at this point in the history
  • Loading branch information
Raphael Benitte committed May 9, 2016
1 parent 6ef978c commit f797073
Show file tree
Hide file tree
Showing 2 changed files with 104 additions and 36 deletions.
95 changes: 83 additions & 12 deletions src/components/charts/stack/Stack.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,31 @@ import { margin as marginPropType } from '../../../PropTypes';
import decoratorsFromReactChildren from '../../../lib/decoratorsFromReactChildren';


const findPrecedingLayer = (layers, index) => {
if (layers === null) {
return null;
}

for (let i = layers.length - 1; i >= 0; i--) {
const layer = layers[i];
if (layer.index < index) {
return layer;
}
}

return null;
};


class Stack extends Component {
constructor(props) {
super(props);

this.state = {
excludeLayers: []
};

this.previousData = null;
}

renderD3(props, state) {
Expand Down Expand Up @@ -95,30 +113,70 @@ class Stack extends Component {
.domain([0, d3.max(stacked, layer => d3.max(layer.values, d => (d.y0 + d.y)))])
;

filteredData = filteredData.map(layer => {
return _.assign(layer, {
values: layer.values.map(v => ({
value: v,
interpolated: {
x: xScale(v.x),
y0: yScale(v.y0),
y: yScale(v.y0 + v.y)
}
}))
});
});

const area = d3.svg.area()
.interpolate(interpolation)
.x(d => xScale(d.x))
.y0(d => yScale(d.y0))
.y1(d => yScale(d.y0 + d.y))
.x(d => d.x)
.y0(d => d.y0)
.y1(d => d.y)
;


// —————————————————————————————————————————————————————————————————————————————————————————————————————————————
// Areas
// —————————————————————————————————————————————————————————————————————————————————————————————————————————————
let paths = wrapper.selectAll('.nivo_stack_area').data(stacked, d => d.index);
let paths = wrapper.select('.nivo_stack_areas').selectAll('.nivo_stack_area').data(stacked, d => d.index);

// ENTER
paths.enter().append('path')
.attr('class', 'nivo_stack_area')
.attr('d', d => area(d.values))
.attr('d', d => {
if (this.previousData === null) {
return area(d.values.map(p => ({
x: p.interpolated.x,
y0: yScale.range()[0],
y: yScale.range()[0],
})));
}

const precedingLayer = findPrecedingLayer(this.previousData, d.index);

if (precedingLayer !== null) {
return area(precedingLayer.values.map(p => ({
x: p.interpolated.x,
y0: p.interpolated.y,
y: p.interpolated.y,
})));
}

return area(d.values.map(p => ({
x: p.interpolated.x,
y0: p.interpolated.y0,
y: p.interpolated.y0,
})));
})
.style('fill', d => d.color)
;

// UPDATE
paths
.on('click', d => {
this.setState({ excludeLayers: excludeLayers.concat([d.index]) });
// we cannot have no layer
if (filteredData.length > 1) {
this.setState({ excludeLayers: excludeLayers.concat([d.index]) });
}
})
.on('mouseover', function (d) {
d3.select(this).style('fill', overColorFn);
Expand All @@ -132,7 +190,7 @@ class Stack extends Component {
.transition()
.duration(transitionDuration)
.ease(transitionEasing)
.attr('d', d => area(d.values))
.attr('d', d => area(d.values.map(v => v.interpolated)))
.style('fill', d => d.color)
;

Expand All @@ -142,13 +200,22 @@ class Stack extends Component {
.duration(transitionDuration)
.ease(transitionEasing)
.attr('d', d => {
const precedingLayer = findPrecedingLayer(filteredData, d.index);

if (precedingLayer !== null) {
return area(precedingLayer.values.map(p => ({
x: p.interpolated.x,
y0: p.interpolated.y,
y: p.interpolated.y,
})));
}

return area(d.values.map(p => ({
x: p.x,
y0: p.y0,
y: 0,
x: p.interpolated.x,
y0: p.interpolated.y0,
y: p.interpolated.y0,
})));
})
.style('opacity', 0)
.remove()
;

Expand Down Expand Up @@ -199,6 +266,8 @@ class Stack extends Component {
this.decorators.forEach(decorator => {
decorator(stackContext);
});

this.previousData = filteredData;
}

shouldComponentUpdate(nextProps, nextState) {
Expand All @@ -218,7 +287,9 @@ class Stack extends Component {
render() {
return (
<svg ref="svg" className="nivo_stack">
<g className="nivo_stack_wrapper" />
<g className="nivo_stack_wrapper">
<g className="nivo_stack_areas" />
</g>
</svg>
);
}
Expand Down
45 changes: 21 additions & 24 deletions src/components/charts/stack/StackDots.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,26 +26,27 @@ class StackDots extends Component {
const colorFn = getColorGenerator(props.color);
const borderColorFn = getColorGenerator(props.borderColor);
const { showOnOver } = props;
const { onMouseOver, onMouseOut } = props;

// Receive context from Parent Stack component
return ({
element, wrapper,
wrapper,
stacked,
width, height,
xScale, yScale,
transitionDuration, transitionEasing
}) => {
const slices = [];
stacked.forEach(layer => {
layer.values.forEach((datum, i) => {
if (!slices[i]) {
slices[i] = [];
slices[i] = {
x: datum.interpolated.x,
values: [],
};
}

slices[i].push(_.assign({}, datum, {
slices[i].values.push(_.assign({}, datum, {
index: layer.index,
color: layer.color
color: layer.color,
}));
});
});
Expand All @@ -54,7 +55,7 @@ class StackDots extends Component {

const newSlices = elements.enter().append('g')
.attr('class', 'nivo_stack_slices')
.attr('transform', (d, i) => `translate(${xScale(i)},0)`)
.attr('transform', d => `translate(${d.x},0)`)
.style('opacity', showOnOver ? 0 : 1)
.style('pointer-events', 'all')
;
Expand Down Expand Up @@ -96,19 +97,19 @@ class StackDots extends Component {
.transition()
.duration(transitionDuration)
.ease(transitionEasing)
.attr('transform', (d, i) => `translate(${xScale(i)},0)`)
.attr('transform', d => `translate(${d.x},0)`)
;


// —————————————————————————————————————————————————————————————————————————————————————————————————————————
// Lines
// —————————————————————————————————————————————————————————————————————————————————————————————————————————
const lines = elements.selectAll('line').data(d => d, d => d.index);
const lines = elements.selectAll('line').data(d => d.values, d => d.index);

// ENTER
lines.enter().append('line')
.attr('y1', d => yScale(d.y0 + d.y))
.attr('y2', d => yScale(d.y0))
.attr('y1', d => d.interpolated.y)
.attr('y2', d => d.interpolated.y0)
.style('stroke-width', props.borderWidth)
.style('stroke', borderColorFn)
;
Expand All @@ -118,8 +119,8 @@ class StackDots extends Component {
.transition()
.duration(transitionDuration)
.ease(transitionEasing)
.attr('y1', d => yScale(d.y0 + d.y))
.attr('y2', d => yScale(d.y0))
.attr('y1', d => d.interpolated.y)
.attr('y2', d => d.interpolated.y0)
.style('stroke-width', props.borderWidth)
.style('stroke', borderColorFn)
;
Expand All @@ -129,8 +130,8 @@ class StackDots extends Component {
.transition()
.duration(transitionDuration)
.ease(transitionEasing)
.attr('y1', d => yScale(d.y0))
.attr('y2', d => yScale(d.y0))
.attr('y1', d => d.interpolated.y0)
.attr('y2', d => d.interpolated.y0)
.style('opacity', 0)
.remove()
;
Expand All @@ -139,31 +140,29 @@ class StackDots extends Component {
// —————————————————————————————————————————————————————————————————————————————————————————————————————————
// Circles
// —————————————————————————————————————————————————————————————————————————————————————————————————————————
const circles = elements.selectAll('circle').data(d => d, d => d.index);
const circles = elements.selectAll('circle').data(d => d.values, d => d.index);

// ENTER
circles.enter().append('circle')
.attr('r', 0.1)
.style('cursor', 'pointer')
.attr('transform', d => {
return `translate(0,${yScale(d.y0 + d.y)})`;
})
.attr('transform', d => `translate(0,${d.interpolated.y})`)
.style('fill', colorFn)
.style('stroke-width', props.borderWidth)
.style('stroke', borderColorFn)
;

// UPDATE
circles
.on('mouseover', function (d) {
.on('mouseover', function () {
d3.select(this)
.transition()
.duration(overTransitionDuration)
.ease(overTransitionEasing)
.attr('r', props.radius * 2)
;
})
.on('mouseout', function (d) {
.on('mouseout', function () {
d3.select(this)
.transition()
.duration(overTransitionDuration)
Expand All @@ -175,9 +174,7 @@ class StackDots extends Component {
.duration(transitionDuration)
.ease(transitionEasing)
.attr('r', props.radius)
.attr('transform', d => {
return `translate(0,${yScale(d.y0 + d.y)})`;
})
.attr('transform', d => `translate(0,${d.interpolated.y})`)
.style('fill', colorFn)
.style('stroke-width', props.borderWidth)
.style('stroke', borderColorFn)
Expand Down

0 comments on commit f797073

Please sign in to comment.