score:1

Here's an example of using a linearGradient, with a function that finds the y coordinates given an x coordinate. A rect with 0 opacity captures mouse events to show and update the linearGradients's stops and lines coordinates

https://bl.ocks.org/tomshanley/01a87c81b5ed86b6d55e566403c175ba

An area shape is created where the y0 and y1 functions are using the y values from the two lines.

``````let curve = d3.curveCatmullRom.alpha(0.5)

let area = d3.area()
.x(function (d, i) { return xScale(i) })
.y0(function (d) { return yScale(d.data1) })
.y1(function (d) { return yScale(d.data2) })
.curve(curve);

let line1 = d3.line()
.x(function (d, i) { return xScale(i) })
.y(function (d) { return yScale(d.data1) })
.curve(curve);

let line2 = d3.line()
.x(function (d, i) { return xScale(i) })
.y(function (d) { return yScale(d.data2) })
.curve(curve);
``````

The area's fill is set to a linearGradient, which has 'stops' that are updated with mousemove event. The stops' percentages are at set so that the white and grey change abruptly to give the impression of a solid fill, either side of the mouse's position.

``````let x = d3.mouse(this)
let middle = x / width

offset1 = (middle - 0.1) < 0 ? 0 : (middle - 0.1)
offset2 = (middle + 0.1) > 1 ? 1 : (middle + 0.1)

stopsData = [
{ "offset": 0, "stopColour": "#FFFFFF" },
{ "offset": offset1, "stopColour": "#FFFFFF" },
{ "offset": offset1, "stopColour": "#777777" },
{ "offset": offset2, "stopColour": "#777777" },
{ "offset": offset2, "stopColour": "#FFFFFF" },
{ "offset": 1, "stopColour": "#FFFFFF" }
]

.data(stopsData)
.attr("offset", function (d) { return d.offset })
.attr("stop-color", function (d) { return d.stopColour })
``````

Your sketch included lines each side of the area's fill, which I created using two lines, whose y1 and y2 attributes by set by traversing along each path until you get to the point where the x coordinate is same (or just greater than) the mouse's x position.

``````o1 = width * offset1
o2 = width * offset2

line1Ys = findYs(path1Node, o1, o2)
line2Ys = findYs(path2Node, o1, o2)

areaLine1.attr("x1", width*offset1)
.attr("x2", width*offset1)
.attr("y1", line1Ys)
.attr("y2", line2Ys)

areaLine2.attr("x1", width*offset2)
.attr("x2", width*offset2)
.attr("y1", line1Ys)
.attr("y2", line2Ys)

function findYs(p, x1, x2) {
const accuracy = 1 //increase for quicker, but less accurate lines
let ys = [];
let i = x1;
const l = p.getTotalLength()
for (i; i < l; i+=accuracy) {
let pos = p.getPointAtLength(i)
if (pos.x > x1) {
ys.push(pos.y)
break
}
}
for (i; i < l; i+=accuracy) {
let pos = p.getPointAtLength(i)
if (pos.x > x2) {
ys.push(pos.y)
break
}
}
return ys;
}
``````