How can I implement an invert function for a point scale?

后端 未结 1 1825
挽巷
挽巷 2021-01-18 02:14

I am trying to add a tooltip for my dual line chart graph.

However, instead of using timeScale or scaleLinear, I used scalePoint to graph my chart.

I am tr

相关标签:
1条回答
  • 2021-01-18 02:20

    You are right, there is no invert for a point scale. But you can create your own function to get the corresponding domain of a given x position:

    function scalePointPosition() {
        var xPos = d3.mouse(this)[0];
        var domain = xScale.domain(); 
        var range = xScale.range();
        var rangePoints = d3.range(range[0], range[1], xScale.step())
        var yPos = domain[d3.bisect(rangePoints, xPos) -1];
        console.log(yPos);
    }
    

    Step by step explanation

    First, we get the x mouse position.

    var xPos = d3.mouse(this)[0];
    

    Then, based on your scale's range and domain...

    var domain = xScale.domain(); 
    var range = xScale.range();
    

    ...we create an array with all the steps in the point scale using d3.range:

    var rangePoints = d3.range(range[0], range[1], xScale.step())
    

    Finally, we get the corresponding domain using bisect:

    var yPos = domain[d3.bisect(rangePoints, xPos) -1];
    

    Check the console.log in this demo:

    var data = [{
        A: "groupA",
        B: 10
    }, {
        A: "groupB",
        B: 20
    }, {
        A: "groupC",
        B: 30
    }, {
        A: "groupD",
        B: 10
    }, {
        A: "groupE",
        B: 17
    }]
    
    var width = 500,
        height = 200;
    
    var svg = d3.selectAll("body")
        .append("svg")
        .attr("width", width)
        .attr("height", height);
    
    var color = d3.scaleOrdinal(d3.schemeCategory10)
        .domain(data.map(function(d) {
            return d.A
        }));
    
    var xScale = d3.scalePoint()
        .domain(data.map(function(d) {
            return d.A
        }))
        .range([50, width - 50])
        .padding(0.5);
    
    var yScale = d3.scaleLinear()
        .domain([0, d3.max(data, function(d) {
            return d.B
        }) * 1.1])
        .range([height - 50, 10]);
    
    var circles = svg.selectAll(".circles")
        .data(data)
        .enter()
        .append("circle")
        .attr("r", 8)
        .attr("cx", function(d) {
            return xScale(d.A)
        })
        .attr("cy", function(d) {
            return yScale(d.B)
        })
        .attr("fill", function(d) {
            return color(d.A)
        });
    
    var xAxis = d3.axisBottom(xScale);
    var yAxis = d3.axisLeft(yScale);
    
    svg.append("g").attr("transform", "translate(0,150)")
        .attr("class", "xAxis")
        .call(xAxis);
    
    svg.append("g")
        .attr("transform", "translate(50,0)")
        .attr("class", "yAxis")
        .call(yAxis);
    
    svg.append("rect")
        .attr("opacity", 0)
        .attr("x", 50)
        .attr("width", width - 50)
        .attr("height", height)
        .on("mousemove", scalePointPosition);
    
    function scalePointPosition() {
        var xPos = d3.mouse(this)[0];
        var domain = xScale.domain();
        var range = xScale.range();
        var rangePoints = d3.range(range[0], range[1], xScale.step())
        var yPos = domain[d3.bisect(rangePoints, xPos) - 1];
        console.log(yPos);
    }
    .as-console-wrapper { max-height: 20% !important;}
    <script src="https://d3js.org/d3.v4.min.js"></script>

    0 讨论(0)
提交回复
热议问题