简体   繁体   中英

Unable to calculate vertical coordinates for tooltip on Y-axis legend in D3 v4

I have created a graph which has a mouseover/tooltop for each legend item in the y-axis. The mouseover creates a tooltip which spans the width of the graph at the height of the y legend item. I have successfully positioned the height and width as well as x-axis location of the tooltip but cannot come up with the right formula to locate the top of the tooltip.

鼠标悬停时垂直偏移不正确

var countryBox = d3.select("body").append("div")
    .attr("class", "countryTip")
    .style("opacity", 0);

...

/*  Build mouseover infobox for each country in y axis legend in 1800 */
h.selectAll(".axis--y .tick text")
    .on("mouseover", function() {
        totSpec = 0;
        var myElement = d3.select(this);
        var countryName = myElement.text();
        /*  determine absolute coordinates for left edge of SVG */
        var matrix = this.getScreenCTM()
            .translate(+this.getAttribute("cx"), +this.getAttribute("cy"));
        h.selectAll("." + countryName.replace(/\s+/g, '_')).each(function(d) {
            totSpec += d.freqEnd;
            totSpec -= d.freqStart;
            sumSpec = sumSpec + d.freqEnd - d.freqStart;
        });
        var availPercent = totSpec / availSpec;

        countryBox.transition()
            .duration(200)
            .style("opacity", .9);
        var yText = getTranslation(d3.select(this.parentNode).attr("transform"));
        countryBox.html(countryName + '</br>' + r(totSpec) + ' MHz assigned out of ' + r(availSpec) + ' MHz available. <br><b>Band occupancy ' + p(availPercent) + '</b>')
            .style("left", (window.pageXOffset + matrix.e) + "px")
            .style("top", (yText[1] - y.bandwidth()/2 - window.pageYOffset) + "px")
            .style("height", y.bandwidth() + "px")
            .style("width", width + "px");
        console.log("yText[1]: "  + yText[1] + " halfBand: " + halfBand + " margin.top: " + margin.top + " window.pageYOffset: " + window.pageYOffset );

    })
    .on("mouseout", function() {
        countryBox.transition()
            .duration(500)
            .style("opacity", 0);
    });

where

getTranslation is a function to compensate for the absence of d3.transform in v4 to

function getTranslation(transform) {
    var g = document.createElementNS("http://www.w3.org/2000/svg", "g");
    g.setAttributeNS(null, "transform", transform);
    var matrix = g.transform.baseVal.consolidate().matrix;
    return [matrix.e, matrix.f];
}

For

.style("top", (yText[1] - y.bandwidth()/2 - window.pageYOffset) + "px")

  • yText[1] returns the y coordinates of the y legend item the mouse is passing over, essentially the vertical midpoint of the country band in question.
  • y.bandwidth()/2 represents half the vertical height of the band and
  • window.pageYOffset compensates for scroll

As far as I can see, that should put the top of the tooltip at the top of the relevant x band. Obviously not though. Is bootstrap a factor? If it comes to pure svg, I find I can draw a circle perfect laid out on each band use yText[1] as the centre point of the circle. Am I mixing up svg and html?

CSS

div.countryTip {
    position: fixed;
    z-index: 99;
    text-align: left;
    font-family: arial;
    font-size: 1em;
    line-height: 1.5;
    padding: 1.5em;
    background: #144667;
    border: 0px;
    color: white;
    border-radius: 3px;
    pointer-events: none;
    -webkit-transform: translate3d(0, -0.5em, 0);
    -moz-transform: translate3d(0, -0.5em, 0);
    transform: translate3d(0, -0.5em, 0);
    -webkit-transition: opacity 0.1s, -webkit-transform 0.1s;
    -moz-transition: opacity 0.1s, -moz-transform 0.1s;
    transition: opacity 0.1s, transform 0.1s;
}

Working site and full source

Update: Added a Plunker

Hat tip to @ Gerald Furtado who pointed out that I needed to add the top offset from the container div for the SVG element.

.style("top", (document.getElementById(divID).offsetTop + yText[1] - window.pageYOffset) + "px")

did the job although I am still getting a little bit of vertical variance across the different tabs on the tooltips which I can't explain.

Plunker updated

Finally solved this. I was confusing the SVG and HTML coordinate systems. The answer was to use the y element of the matrix coordinate defined by getScreenCTM()

let matrix = this.getScreenCTM()
   .translate(+this.getAttribute('cx') +this.getAttribute('cy'))

with the result being

.style('top', matrix.f - y.bandwidth() / 2 + 8 + 'px')

This post on understanding SVG coordinates and this practical example on SVG tooltips helped me get there in the end.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM