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)[0]
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" }
 ]

gradient.selectAll("stop")
      .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[0])
    .attr("y2", line2Ys[0])

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

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;
        }

Related Query

More Query from same tag