Skip to content

Instantly share code, notes, and snippets.

@AlexDaGr8
Last active December 17, 2018 16:42
Show Gist options
  • Save AlexDaGr8/8c24741b8f5a8335661e12815efaa149 to your computer and use it in GitHub Desktop.
Save AlexDaGr8/8c24741b8f5a8335661e12815efaa149 to your computer and use it in GitHub Desktop.
Animated area chart with linear gradients

Tried to emulate something seen by Graphiq.

<!DOCTYPE html>
<body>
<script src="https://d3js.org/d3.v5.min.js"></script>
<script>
var data = [
{
key: 'set3',
values: d3.range(30).map((d,i) => 500 + i*2)
},
{
key: 'set1',
values: d3.range(30).map((d,i) => Math.pow(i+1,2))
},
{
key: 'set2',
values: d3.range(30).map((d,i) => Math.pow(i+1,2)).reverse()
}
];
var margin = {left: 60, top: 40, right: 20, bottom: 40}
var width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom,
svg = d3.select('body').append('svg')
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom)
.style('box-shadow', '0px 0px 25px')
.style('margin', 'auto')
.append('g')
.attr('transform', 'translate(' + [margin.left, margin.top] + ')')
var x = d3.scaleLinear()
.range([0,width])
.domain([0,data[0].values.length-1])
var y = d3.scaleLinear()
.range([height, 0])
.domain([0, d3.max(data, d => d3.max(d.values, v => v))])
var area = d3.area()
.x((d,i) => x(i))
.y0(y(0))
.y1(d => y(d))
.curve(d3.curveMonotoneX)
var line = d3.line()
.x((d,i) => x(i))
.y(d => y(d))
.curve(d3.curveMonotoneX)
var color = d3.scaleOrdinal(d3.schemeDark2)
color.domain(data.map(d => d.key))
var title = svg.append('text')
.text('Generic Title')
.attr('x', width/2)
.attr('y', -10)
.attr('font-size', 25)
.attr('text-anchor', 'middle')
var legend = svg.selectAll('g.legendItems')
.data(color.domain())
.enter().append('g')
.attr('class', d => 'legendItems ' + d)
.style('cursor', 'pointer')
.on('mouseover', (a,b,c) => mouseOver(a,b,c))
.on('mouseout', (a,b,c) => mouseOut(a,b,c))
.on('click', (a,b,c) => click(a,b,c))
legend.append('rect')
.attr('width', 10)
.attr('height', 10)
.attr('stroke', function (d) { return color(d); })
.attr('stroke-width', 2)
.attr('fill', function (d) { return color(d); })
.attr('fill-opacity', .7);
legend.append('text')
.text(function (d) { return d.replace(/^\w/, c => c.toUpperCase()); })
.attr('x', 15)
.attr('y', 10);
var moveLegend = 0;
legend.each(function (g) {
var gDiv = svg.select('g.' + g.replace(/\s|\//, ''));
var gWidth = gDiv.node().getBoundingClientRect().width + 10;
moveLegend += gWidth;
gDiv.attr('transform', 'translate(' + [(moveLegend - gWidth) + 40, -20] + ')');
});
var defs = svg.append('defs')
var lineGrads = defs.selectAll("linearGradient")
.data(color.domain())
.enter().append('linearGradient')
.attr("id", d => d)
.attr("x2", "0%")
.attr("y2", "100%");
lineGrads.append("stop")
.attr('class', 'start')
.attr("offset", "0%")
.attr("stop-color", d => color(d))
.attr('stop-opacity', .15)
lineGrads.append("stop")
.attr('class', 'end')
.attr("offset", "100%")
.attr("stop-color", '#fff')
.attr('stop-opacity', 0)
svg.append('g')
.attr('transform', 'translate(0,' + height + ')')
.call(d3.axisBottom(x))
.call(g => {
g.select('.tick:last-of-type text').clone()
.attr('y', 20)
.attr('text-anchor', 'middle')
.attr('font-weight', 'bold')
.attr('font-size', 12)
.text('X Axis')
})
svg.append('g')
.attr('transform', 'translate(' + width + ',0)')
.call(d3.axisLeft(y).tickSize(width))
.call(g => {
g.select('.domain').remove()
g.selectAll('line')
.attr('stroke', '#aaa')
.attr("stroke-dasharray", "2,2");
g.select('.tick:last-of-type text').clone()
.attr('y', -40)
.attr('text-anchor', 'middle')
.attr('font-weight', 'bold')
.attr('font-size', 12)
.attr('transform', 'rotate(-90)')
.text('Y Axis')
g.select('.tick:first-of-type line').remove()
});
// .call(d3.axisLeft(y))
// .call(g => {
// g.select('.tick:last-of-type text').clone()
// .attr('y', -40)
// .attr('text-anchor', 'middle')
// .attr('font-weight', 'bold')
// .attr('font-size', 12)
// .attr('transform', 'rotate(-90)')
// .text('Y Axis')
// })
var areas = svg.selectAll('g.area')
.data(data)
.enter().append('g')
.attr('class', d => 'area ' + d.key)
var ease = d3.easeQuad
var lines = areas.append('path')
.attr('class', 'linePath')
.datum(d => d)
.attr('d', d => line(new Array(d.values.length).fill(0)))
.transition().delay((d,i) => 150 * i)
.ease(ease)
.attr('d', d => line(d.values))
.attr('fill', 'none')
.attr('stroke', d => color(d.key))
.attr('stroke-width', 2)
areas.append('path')
.attr('class', 'areaPath')
.datum(d => d)
.attr('d', d => area(new Array(d.values.length).fill(0)))
.transition().delay((d,i) => 150 * i)
.ease(ease)
.attr('d', d => area(d.values))
.attr('fill', d => 'url(#' + d.key + ')')
function mouseOver(a,b,c) {
color.domain().filter(d => d != a)
.forEach(d => {
svg.select('g.area.' + d)
.transition()
.style('opacity', .2)
})
}
function mouseOut(a,b,c) {
svg.selectAll('g.area')
.transition()
.style('opacity', 1)
}
function click(a,b,c) {
if (d3.select(c[b]).classed('clicked')) {
d3.select(c[b]).classed('clicked', false)
.select('rect').attr('fill', '#bbb')
c.filter(d => d != c[b] && d3.select(d).classed('clicked'))
.forEach(d => {
d3.select(d).select('rect')
.attr('fill', v => color(v))
})
var filter = c.filter(d => !d3.select(d).classed('clicked'))
if (filter.length === c.length) {
filter.forEach(d => {
d3.select(d).select('rect')
.attr('fill', d => color(d))
var singleArea = d3.select('g.area.' + d3.select(d).data())
singleArea.select('.areaPath').transition()
.ease(ease)
.attr('d', d => area(d.values))
singleArea.select('.linePath').transition()
.ease(ease)
.attr('d', d => line(d.values))
})
} else {
filter.forEach(d => {
d3.select(d).select('rect')
.attr('fill', '#bbb')
var singleArea = d3.select('g.area.' + d3.select(d).data())
singleArea.select('.areaPath').transition()
.ease(ease)
.attr('d', d => area(new Array(d.values.length).fill(0)))
singleArea.select('.linePath').transition()
.ease(ease)
.attr('d', d => line(new Array(d.values.length).fill(0)))
})
}
} else {
d3.select(c[b]).classed('clicked', true)
d3.select(c[b]).select('rect').attr('fill', v => color(v))
c.filter(d => d != c[b] && !d3.select(d).classed('clicked'))
.forEach(d => {
d3.select(d).select('rect')
.attr('fill', '#bbb')
var singleArea = d3.select('g.area.' + d3.select(d).data())
singleArea.select('.areaPath').transition()
.ease(ease)
.attr('d', d => area(new Array(d.values.length).fill(0)))
singleArea.select('.linePath').transition()
.ease(ease)
.attr('d', d => line(new Array(d.values.length).fill(0)))
})
c.filter(d => d3.select(d).classed('clicked'))
.forEach(d => {
var singleArea = d3.select('g.area.' + d3.select(d).data())
singleArea.select('.areaPath').transition()
.ease(ease)
.attr('d', d => area(d.values))
singleArea.select('.linePath').transition()
.ease(ease)
.attr('d', d => line(d.values))
})
}
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment