D3:在mousemove上显示文本。旧文本不会消失

时间:2018-03-28 00:53:06

标签: d3.js text mousemove

我想显示位于鼠标位置的数据。我知道网上有很多例子,但没有一个真正适用于我拥有的数据,因为我有三条线没有相同的x间隔。

以下是我用来尝试mousemove功能的代码。

mb = focus.append("rect")
.attr("width", width)
.attr("height", height)
.attr("opacity", 0)
.on("mousemove", function(){
    mouse_dt = x.invert(d3.mouse(mb.node())[0]);

    tooltip.style("display", "block")
    .style("left", (d3.mouse(mb.node())[0] + margin.left + 20) + "px")
    .style("top", (d3.mouse(mb.node())[1] + margin.top) + "px")
    .selectAll()
    .data(grouped_data)
    .enter().append("div")
    .html(d => d.key);
})
.on("mouseout", function(){if (tooltip) tooltip.style("display", "none");})

我的问题是当我将鼠标悬停在焦点rect周围时,该函数允许我在第一个鼠标点获取数据,但是当我指向第二个位置时,它不会删除旧数据。 (如下图所示)

text display bug

grouped_data如下所示;

half-hourly_interval_data hourly-interval_data

感谢任何帮助。

PS。如果您对完整代码感兴趣

<style> /* set the CSS */

.svg-container {
    display: inline-block;
    position: relative;
    width: 100%;
    padding-bottom: 100%;
    vertical-align: top;
    overflow: hidden;
}

.svg-content-responsive {
    display: inline-block;
    position: absolute;
    top: 0;
    left: 0;
}

body { font: 12px Arial;}

path {
    stroke: #ccc;
    stroke-width: 2;
    fill: none;
}

.axis path,
.axis line {
    fill: none;
    stroke: grey;
    stroke-width: 1;
    shape-rendering: crispEdges;
}

#showAll{
    position:absolute;
    top:750px;
    left:880px;
}

#clearAll{
    position:absolute;
    top:750px;
    left:950px;
}

input{
    border-radius:5px;
    padding:5px 10px;
    background:#999;
    border:0;
    color:#fff;
}

.brush .extent {
    stroke: #fff;
    fill-opacity: .125;
    shape-rendering: crispEdges;
}

.line {
    fill: none;
}

.zoom {
    cursor: move;
    fill: none;
    pointer-events: all;
}

#legendContainer{
    position:absolute;
    top:200px;
    left:20px;
    overflow: auto;
    height:490px;
    width:90px;
}

#legend{
    width:75px;
    height:150px;
}

.legend {
    font-size: 12px;
    font-weight: normal;
    text-anchor: left;
}

.legendcheckbox{
    cursor: pointer;
}

</style>

<body>

<!-- load d3 v4 and jquery libraries -->
<script src="http://d3js.org/d3.v4.min.js"></script>

<div id="station_name"></div>
<div id="weather_type"></div>

<div id="container" class="svg-container" >

<div id="legendContainer" class="legendContainer">
    <svg id="legend"></svg>
</div>

<div id="tooltip" style="position:absolute;background-color:lightgray;padding:5px"></div>


<script>

function filter_station_type(data, station, type) {
    return data.filter(function(d) {
        if(d['station_name']==station & d['metric']==type) {return d;} }) }

// Set the dimensions of the canvas / graph
var margin = {top: 10, right: 10, bottom: 100, left: 140},
    margin2 = {top: 480, right: 10, bottom: 20, left: 140},
    width = 1000 - margin.left - margin.right,
    height = 550 - margin.top - margin.bottom,
    height2 = 550 - margin2.top - margin2.bottom;

// Parse the date / time
var parseDate = d3.utcParse("%Y-%m-%dT%H:%M:%S%Z"),
    bisectDate = d3.bisector(function(d){return d.dt;}).left;

// Set the ranges
var x = d3.scaleTime().range([0, width]);
var x2 = d3.scaleTime().range([0, width]);
var y = d3.scaleLinear().range([height, 0]);
var y2 = d3.scaleLinear().range([height2, 0]);

// Define the axes
var xAxis = d3.axisBottom(x).ticks(5)
.tickFormat(d3.timeFormat("%Y-%m-%d %H:%M"))

var xAxis2 = d3.axisBottom(x2).ticks(5)
.tickFormat(d3.timeFormat("%Y-%m-%d"))

var yAxis = d3.axisLeft(y)
.ticks(5);

var brush = d3.brushX()
.extent([[0, 0], [width, height2]])
.on("brush", brushed);

// Define the line
var stateline = d3.line()
.x(function(d) { return x(d.dt); })
.y(function(d) { return y(d.value); });

var stateline2 = d3.line()
.x(function(d) { return x2(d.dt); })
.y(function(d) { return y2(d.value); });

// Adds main graph svg canvas
var svg = d3.select("div#container")
// .append("div")
// .classed("svg-container", true)
.append("svg")
// .attr("preserveAspectRatio", "xMinYMin meet")
// .attr("viewBox", "0 0 600 400")
// .attr("svg-content-responsive", true)
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom);

// This line makes sure the main plot doesn't go beyond its assigned canvas
svg.append("defs").append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);

var focus = svg.append("g")
.attr("class", "focus")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");

var tooltip = d3.select("#tooltip")
.style("display", "none");

var context = svg.append("g")
.attr("class", "context")
.attr("transform", "translate(" + margin2.left + "," + margin2.top + ")");

var color = d3.scaleOrdinal(["#48A36D",  "#0096ff", "#ff007e"]);

// Get the data
temp ="/basic_app/api/WF/"
d3.json(temp, function(error, json){

    json.forEach(function(d) {
        d.dt = parseDate(String(d.dt));
        d.value = +d.value; });

    var station_list = d3.map(json, function(d){return d.station_name;}).keys().sort(),
        weather_type_list = d3.map(json, function(d){return d.metric;}).keys().sort(),
        station_menu = d3.select("#station_name"),
        weather_type_menu = d3.select("#weather_type");

    station_menu.append('select')
    .selectAll('option')
    .data(station_list)
    .enter().append('option')
    .attr('value', function(d) {return d;})
    .text(function(d) {return d;});

    weather_type_menu.append('select')
    .selectAll('option')
    .data(weather_type_list)
    .enter().append('option')
    .attr('value', function(d) {return d;})
    .text(function(d) {return d;});

    update_plot(json, station_menu, weather_type_menu);

    station_menu.on("change", function() {
        update_plot(json, station_menu, weather_type_menu) });

    weather_type_menu.on("change", function() {
        update_plot(json, station_menu, weather_type_menu) });

});


function update_plot(data, menu1, menu2) {
    var station = menu1.select("select").property("value")
        type = menu2.select("select").property("value")

    subset = filter_station_type(data, station, type)
    plot(subset)

};


function plot(data) {

    // Scale the range of the data
    x.domain(d3.extent(data, function(d) { return d.dt; }));
    y.domain([d3.min(data, function(d) { return d.value; }), d3.max(data, function(d) { return d.value; })]);
    x2.domain(x.domain());
    y2.domain(y.domain());

    // Nest the entries by state
    grouped_data = d3.nest()
    .key(function(d) {return d.provider;})
    .entries(data);


    focus.selectAll('.line').remove();

    focus.selectAll('.line')
    .data(grouped_data)
    .enter().append('path')
    .attr('class', 'line')
    .attr("clip-path", "url(#clip)") // makes sure the main plot doesn't go out of its boundary. Required for brush functionality
    .attr("id", function(d) {return 'tag'+d.key.replace(/\s+/g, '')}) // assign ID. Required for interactive legend functionality
    .style('stroke', function(d) {return color(d.key);})
    .attr('d', function(d) {return stateline(d.values);})

    focus.selectAll('.axis').remove();

    focus.append("g")
    .attr("class", "axis axis--x")
    .attr("transform", "translate(0," + height + ")")
    .call(xAxis);

    focus.append("g")
    .attr("class", "axis axis--y")
    .call(yAxis);


    context.selectAll('.line').remove();

    context.selectAll('.line')
    .data(grouped_data)
    .enter().append('path')
    .attr('class', 'line')
    .style('stroke', function(d) {return color(d.key);})
    .attr('d', function(d) {return stateline2(d.values);});

    context.selectAll('.axis').remove();

    context.append("g")
    .attr("class", "axis axis--x")
    .attr("transform", "translate(0," + height2 + ")")
    .call(xAxis2);

    context.selectAll('.brush').remove();

    context.append("g")
    .attr("class", "brush")
    .call(brush)
    .call(brush.move, x.range());

    mb = focus.append("rect")
    .attr("width", width)
    .attr("height", height)
    .attr("opacity", 0)
    .on("mousemove", function(){
        mouse_dt = x.invert(d3.mouse(mb.node())[0]);

        tooltip.style("display", "block")
        .style("left", (d3.mouse(mb.node())[0] + margin.left + 20) + "px")
        .style("top", (d3.mouse(mb.node())[1] + margin.top) + "px")
        .selectAll()
        .data(grouped_data)
        .enter().append("div")
        .html(d => d.key);
    })
    .on("mouseout", function(){if (tooltip) tooltip.style("display", "none");})


    svg.selectAll('.legend').remove();

    var legend = d3.select("#legend")
    .selectAll("text")
    .data(grouped_data);

    legend.enter().append("text")
    .attr("x", 0)
    .attr("y", function(d, i) {return 10 + i*15})
    .attr("class", "legend")
    .style("fill", function(d) {return color(d.key);})
    .text(function(d) {return d.key;})
    .on("click", function(d) {

        if(this.style.fill == "rgb(204, 204, 204)") {
            this.style.fill = color(d.key);}
        else {
            this.style.fill = "rgb(204, 204, 204)";}

        var active = d.active ? false : true,
            new_opacity = active ? 0 : 1;

        d3.selectAll("#tag"+d.key.replace(/\s+/g, ''))
        .transition().duration(100)
        .style("opacity", new_opacity);

        d.active = active;

    })

};


function brushed() {
    var selection = d3.event.selection;
    x.domain(selection.map(x2.invert, x2));
    focus.selectAll(".line").attr("d", function(d) {return stateline(d.values)});
    focus.select('.axis--x').call(xAxis);
};


</script>
</div>
</body>

0 个答案:

没有答案