I am making an indexed chart and I would like to be able to calculate the rate change between the previous data value, and the one that is currently being hovered over. What do you suggest is the best way to do this in a multiline chart? I saw some of the previous posts, but they are 4-6 years old, and furthermore didn't work for me.
const dateParser = d3.timeParse('%Y');
const formatYear = d3.timeFormat('%Y');
const xAccessor = d => dateParser(d.time.substring(0, 4));
const y0Accessor = d => d.vr;
const y1Accessor = d => d.unemp;
const colorValue = d => d.className;
const colorScale = d3.scaleOrdinal()
.range(d3.schemeSet1);
const xScale = d3.scaleTime()
.domain(d3.extent(data, xAccessor))
.range([0, boundedWidth]);
const yScale = d3.scaleLinear()
.domain([50, 200])
.range([boundedHeight, 0]);
const previousData = d3.local();
function drawData(g, accessor, color, id){
// draw circles
g.attr('class', 'line');
g.selectAll('circle').data(data).enter() .append('circle')
.attr('r', 4)
.attr('cx', d => xScale(xAccessor(d)))
.attr('cy', accessor)
.attr('class', `data-point ${id.replace(/\s/g, '')}`)
.style('fill', colorScale(id))
.on('mouseover', function() {
d3.select(this)
.transition()
.attr('r', 6);
})
.on('mouseout', function() {
d3.select(this)
.transition()
.attr('r', 4);
})
.style('cursor', 'pointer')
.attr('fill', color);
// draw lines
const lineGenerator = d3.line()
.curve(d3.curveMonotoneX)
.x(d=>xScale(xAccessor(d)))
.y(accessor);
g.append('path')
.attr('fill', 'none')
.attr('d', lineGenerator(data))
.attr('stroke', color)
.attr('class', `data-point ${id.replace(/\s/g, '')}`)
.style('stroke-width', '3px');
}
const g1 = bounds.append('g');
const g2 = bounds.append('g');
drawData(g1, d=> yScale(y0Accessor(d)), '#e41a1c', 'Vacancy Rate');
drawData(g2, d => yScale(y1Accessor(d)), '#377eb8', 'Unemployment Rate');
//Create the axis
const y0AxisGenerator = d3.axisLeft()
.scale(yScale)
.ticks(5);
const yAxisLabel = yAxis.append('text')
.attr('class', 'y-axis-label')
.attr('x', -boundedHeight / 2)
.attr('y', -dimensions.margin.left + 50)
.html('Difference Rate');
const xAxisGenerator = d3.axisBottom(xScale);
const xAxis = bounds.append('g')
.attr('class', 'x-axis')
.style('transform', `translateY(${boundedHeight}px)`)
.call(xAxisGenerator);
//interactions
const tooltip = d3.select('#tooltip');
//gets all the key names
// color.domain(d3.keys(data[0]).filter(function(key) { return key !== 'label'; }));
g1.selectAll('circle')
.on('mouseenter', function(event) {
const value = d3.select(this).data()[0];
tooltip.call(onMouseEnter, yScale, y0Accessor, value);
})
.on('mouseleave', onMouseLeave);
g2.selectAll('circle')
.on('mouseenter', function(event, i) {
const value = d3.select(this).data()[0];
tooltip.call(onMouseEnter, yScale, y1Accessor, value, i);
})
.on('mouseleave', onMouseLeave);
function onMouseEnter(g, yScale, accessor, data, i) {
g.select('#value')
.text(
`${accessor(data)}%`
);
const x = xScale(xAccessor(data)) + dimensions.margin.left;
const y = yScale(accessor(data)) + dimensions.margin.top;
g.style('transform', `translate(`
+ `calc(-50% + ${x}px),`
+ `calc(-100% + ${y}px)`
+ `)`);
g.style('opacity', .8);
}
function onMouseLeave() {
tooltip.style('opacity', 0);
}