【问题标题】:Tooltip for svg heatmap/colorscale in d3jsd3js 中 svg 热图/色标的工具提示
【发布时间】:2016-10-31 07:29:34
【问题描述】:

我正在 AngularJS 中调整这个渐变 example。这是我正在使用的一些数据集:

var stays=[  
                 {  
                    day:2, 
                    hour:1, 
                    time_spent:127   
                 },
                 {  
                    day:4,
                    hour:1,
                    time_spent:141
                 },
                 {  
                    day:1,
                    hour:1,
                    time_spent:134
                 },
                 {  
                    day:5,
                    hour:1,
                    time_spent:174
                 },
                 {  
                    day:3,
                    hour:1,
                    time_spent:131
                 },
                 {  
                    day:6,
                    hour:1,
                    time_spent:333
                 }];

问题是我想为热图中创建的每个方块构建一个工具提示。工具提示在这里:

var heatMap = svg.selectAll(".hour")
                  .data(stays)
                  .enter().append("rect")
                  .attr("x", function(d) { return (d.hour - 1) * gridSize; })
                  .attr("y", function(d) { return (d.day - 1) * gridSize; })
                  .attr("class", "hour bordered")
                  .attr("width", gridSize)
                  .attr("height", gridSize)
                  .style("stroke", "white")
                  .style("stroke-opacity", 0.6)
                  .style("stroke-width", 0.8)
                  .style("fill", function(d) { return colorScale(d.time_spent); })
                  .on("mouseover", function(d, i) {

                      // Construct tooltip
                      var tooltip_html = '';
                      tooltip_html += '<div class="header"><strong>' + 'Stays' + ' </strong></div><br>';

                      // Add info to the tooltip
                      angular.forEach(stays, function (d) {
                          tooltip_html += '<div><span><strong>' + makeid() + '</strong></span>';
                          tooltip_html += '<span>' + ' ' + d.time_spent + '</span></div>';
                          console.log(d.time_spent);
                      }, days);

                      // Set tooltip width
                      tooltip.html(tooltip_html)
                        .style("width", 300 + "px")
                        .style("left", (d3.event.layerX+10) + "px")
                        .style("top", (d3.event.layerY+10) + "px");

                      // Tooltip transition and more styling
                      tooltip.style('display', 'block')
                      .transition()
                        .ease('ease-in')
                        .duration(100)    
                        .style("opacity", .9);
                  })
                  .on("mouseout", function(d) {    
                      tooltip.transition()
                              .duration(100)
                              .ease('ease-in')
                              .style('opacity', 0); 
                  });

这里的想法是,对于我访问的每个方块,工具提示都会显示相关的标签(我仍然没有使用标签,但它们将成为我展示的数据集的一部分,我正在使用makeid() 函数创建随机名称)到该正方形,以及相关 time_spent 数据的细分。我现在使用的是完整的数字列表,而不是与每个方块相关的数字。想法?谢谢。

【问题讨论】:

    标签: javascript angularjs d3.js svg


    【解决方案1】:

    与其为每个单元格构建提示 HTML,不如考虑将数据附加到每个单元格矩形。创建单个数据 div 并在光标移到单元格上时填充它。

    我将此添加到热图渐变示例中。试试看吧。

    var accidents=[{day:2,hour:1,count:127},{day:4,hour:1,count:141},{day:1,hour:1,count:134},{day:5,hour:1,count:174},{day:3,hour:1,count:131},{day:6,hour:1,count:333},{day:7,hour:1,count:311},{day:2,hour:2,count:79},{day:4,hour:2,count:99},{day:1,hour:2,count:117},{day:5,hour:2,count:123},{day:3,hour:2,count:92},{day:6,hour:2,count:257},{day:7,hour:2,count:293},{day:2,hour:3,count:55},{day:4,hour:3,count:73},{day:1,hour:3,count:107},{day:5,hour:3,count:89},{day:3,hour:3,count:66},{day:6,hour:3,count:185},{day:7,hour:3,count:262},{day:2,hour:4,count:39},{day:4,hour:4,count:67},{day:1,hour:4,count:59},{day:5,hour:4,count:83},{day:3,hour:4,count:45},{day:6,hour:4,count:180},{day:7,hour:4,count:220},{day:2,hour:5,count:48},{day:4,hour:5,count:57},{day:1,hour:5,count:73},{day:5,hour:5,count:76},{day:3,hour:5,count:72},{day:6,hour:5,count:168},{day:7,hour:5,count:199},{day:2,hour:6,count:129},{day:4,hour:6,count:102},{day:1,hour:6,count:129},{day:5,hour:6,count:140},{day:3,hour:6,count:117},{day:6,hour:6,count:148},{day:7,hour:6,count:193},{day:2,hour:7,count:314},{day:4,hour:7,count:284},{day:1,hour:7,count:367},{day:5,hour:7,count:270},{day:3,hour:7,count:310},{day:6,hour:7,count:179},{day:7,hour:7,count:192},{day:2,hour:8,count:806},{day:4,hour:8,count:811},{day:1,hour:8,count:850},{day:5,hour:8,count:609},{day:3,hour:8,count:846},{day:6,hour:8,count:208},{day:7,hour:8,count:144},{day:2,hour:9,count:1209},{day:4,hour:9,count:1214},{day:1,hour:9,count:1205},{day:5,hour:9,count:960},{day:3,hour:9,count:1073},{day:6,hour:9,count:286},{day:7,hour:9,count:152},{day:2,hour:10,count:750},{day:4,hour:10,count:808},{day:1,hour:10,count:610},{day:5,hour:10,count:655},{day:3,hour:10,count:684},{day:6,hour:10,count:482},{day:7,hour:10,count:253},{day:2,hour:11,count:591},{day:4,hour:11,count:593},{day:1,hour:11,count:573},{day:5,hour:11,count:695},{day:3,hour:11,count:622},{day:6,hour:11,count:676},{day:7,hour:11,count:326},{day:2,hour:12,count:653},{day:4,hour:12,count:679},{day:1,hour:12,count:639},{day:5,hour:12,count:736},{day:3,hour:12,count:687},{day:6,hour:12,count:858},{day:7,hour:12,count:402},{day:2,hour:13,count:738},{day:4,hour:13,count:749},{day:1,hour:13,count:631},{day:5,hour:13,count:908},{day:3,hour:13,count:888},{day:6,hour:13,count:880},{day:7,hour:13,count:507},{day:2,hour:14,count:792},{day:4,hour:14,count:847},{day:1,hour:14,count:752},{day:5,hour:14,count:1033},{day:3,hour:14,count:942},{day:6,hour:14,count:983},{day:7,hour:14,count:636},{day:2,hour:15,count:906},{day:4,hour:15,count:1031},{day:1,hour:15,count:954},{day:5,hour:15,count:1199},{day:3,hour:15,count:1014},{day:6,hour:15,count:1125},{day:7,hour:15,count:712},{day:2,hour:16,count:1101},{day:4,hour:16,count:1158},{day:1,hour:16,count:1029},{day:5,hour:16,count:1364},{day:3,hour:16,count:1068},{day:6,hour:16,count:1062},{day:7,hour:16,count:736},{day:2,hour:17,count:1303},{day:4,hour:17,count:1426},{day:1,hour:17,count:1270},{day:5,hour:17,count:1455},{day:3,hour:17,count:1407},{day:6,hour:17,count:883},{day:7,hour:17,count:666},{day:2,hour:18,count:1549},{day:4,hour:18,count:1653},{day:1,hour:18,count:1350},{day:5,hour:18,count:1502},{day:3,hour:18,count:1507},{day:6,hour:18,count:830},{day:7,hour:18,count:652},{day:2,hour:19,count:998},{day:4,hour:19,count:1070},{day:1,hour:19,count:787},{day:5,hour:19,count:1027},{day:3,hour:19,count:1019},{day:6,hour:19,count:575},{day:7,hour:19,count:519},{day:2,hour:20,count:661},{day:4,hour:20,count:756},{day:1,hour:20,count:596},{day:5,hour:20,count:730},{day:3,hour:20,count:648},{day:6,hour:20,count:494},{day:7,hour:20,count:486},{day:2,hour:21,count:431},{day:4,hour:21,count:539},{day:1,hour:21,count:430},{day:5,hour:21,count:509},{day:3,hour:21,count:457},{day:6,hour:21,count:443},{day:7,hour:21,count:421},{day:2,hour:22,count:352},{day:4,hour:22,count:428},{day:1,hour:22,count:362},{day:5,hour:22,count:462},{day:3,hour:22,count:390},{day:6,hour:22,count:379},{day:7,hour:22,count:324},{day:2,hour:23,count:329},{day:4,hour:23,count:381},{day:1,hour:23,count:293},{day:5,hour:23,count:393},{day:3,hour:23,count:313},{day:6,hour:23,count:374},{day:7,hour:23,count:288},{day:2,hour:24,count:211},{day:4,hour:24,count:249},{day:1,hour:24,count:204},{day:5,hour:24,count:417},{day:3,hour:24,count:211},{day:6,hour:24,count:379},{day:7,hour:24,count:203}];
    
    
    ///////////////////////////////////////////////////////////////////////////
    //////////////////// Set up and initiate svg containers ///////////////////
    ///////////////////////////////////////////////////////////////////////////
    
    var days = ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
        times = d3.range(24);
    
    var margin = {
        top: 170,
        right: 50,
        bottom: 70,
        left: 50
    };
    
    var width = Math.max(Math.min(window.innerWidth, 1000), 500) - margin.left - margin.right - 20,
        gridSize = Math.floor(width / times.length),
        height = gridSize * (days.length+2);
    
    //SVG container
    var svg = d3.select('#trafficAccidents')
        .append("svg")
        .attr("width", width + margin.left + margin.right)
        .attr("height", height + margin.top + margin.bottom)
        .append("g")
        .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
    
    //Reset the overall font size
    var newFontSize = width * 62.5 / 900;
    d3.select("html").style("font-size", newFontSize + "%");
    
    ///////////////////////////////////////////////////////////////////////////
    //////////////////////////// Draw Heatmap /////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////
    
    //Based on the heatmap example of: http://blockbuilder.org/milroc/7014412
    
    var colorScale = d3.scale.linear()
        .domain([0, d3.max(accidents, function(d) {return d.count; })/2, d3.max(accidents, function(d) {return d.count; })])
        .range(["#FFFFDD", "#3E9583", "#1F2D86"])
        //.interpolate(d3.interpolateHcl);
    
    var dayLabels = svg.selectAll(".dayLabel")
        .data(days)
        .enter().append("text")
        .text(function (d) { return d; })
        .attr("x", 0)
        .attr("y", function (d, i) { return i * gridSize; })
        .style("text-anchor", "end")
        .attr("transform", "translate(-6," + gridSize / 1.5 + ")")
        .attr("class", function (d, i) { return ((i >= 0 && i <= 4) ? "dayLabel mono axis axis-workweek" : "dayLabel mono axis"); });
    
    var timeLabels = svg.selectAll(".timeLabel")
        .data(times)
        .enter().append("text")
        .text(function(d) { return d; })
        .attr("x", function(d, i) { return i * gridSize; })
        .attr("y", 0)
        .style("text-anchor", "middle")
        .attr("transform", "translate(" + gridSize / 2 + ", -6)")
        .attr("class", function(d, i) { return ((i >= 8 && i <= 17) ? "timeLabel mono axis axis-worktime" : "timeLabel mono axis"); });
    
    var heatMap = svg.selectAll(".hour")
        .data(accidents)
        .enter().append("rect")
         //----attach data to rect---
         .attr("data","This is the data for this cell")
         .attr("onmouseover","showData(evt)")
         .attr("onmouseout","hideData(evt)")
        .attr("x", function(d) { return (d.hour - 1) * gridSize; })
        .attr("y", function(d) { return (d.day - 1) * gridSize; })
        .attr("class", "hour bordered")
        .attr("width", gridSize)
        .attr("height", gridSize)
        .style("stroke", "white")
        .style("stroke-opacity", 0.6)
        .style("fill", function(d) { return colorScale(d.count); });
    
    //Append title to the top
    svg.append("text")
        .attr("class", "title")
        .attr("x", width/2)
        .attr("y", -90)
        .style("text-anchor", "middle")
        .text("Number of Traffic accidents per Day & Hour combination");
    svg.append("text")
        .attr("class", "subtitle")
        .attr("x", width/2)
        .attr("y", -60)
        .style("text-anchor", "middle")
        .text("The Netherlands | 2014");
    
    //Append credit at bottom
    svg.append("text")
        .attr("class", "credit")
        .attr("x", width/2)
        .attr("y", gridSize * (days.length+1) + 80)
        .style("text-anchor", "middle")
        .text("Based on Miles McCrocklin's Heatmap block");
    
    ///////////////////////////////////////////////////////////////////////////
    //////////////// Create the gradient for the legend ///////////////////////
    ///////////////////////////////////////////////////////////////////////////
    
    //Extra scale since the color scale is interpolated
    var countScale = d3.scale.linear()
        .domain([0, d3.max(accidents, function(d) {return d.count; })])
        .range([0, width])
    
    //Calculate the variables for the temp gradient
    var numStops = 10;
    countRange = countScale.domain();
    countRange[2] = countRange[1] - countRange[0];
    countPoint = [];
    for(var i = 0; i < numStops; i++) {
        countPoint.push(i * countRange[2]/(numStops-1) + countRange[0]);
    }//for i
    
    //Create the gradient
    svg.append("defs")
        .append("linearGradient")
        .attr("id", "legend-traffic")
        .attr("x1", "0%").attr("y1", "0%")
        .attr("x2", "100%").attr("y2", "0%")
        .selectAll("stop")
        .data(d3.range(numStops))
        .enter().append("stop")
        .attr("offset", function(d,i) {
            return countScale( countPoint[i] )/width;
        })
        .attr("stop-color", function(d,i) {
            return colorScale( countPoint[i] );
        });
    
    ///////////////////////////////////////////////////////////////////////////
    ////////////////////////// Draw the legend ////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////
    
    var legendWidth = Math.min(width*0.8, 400);
    //Color Legend container
    var legendsvg = svg.append("g")
        .attr("class", "legendWrapper")
        .attr("transform", "translate(" + (width/2) + "," + (gridSize * days.length + 40) + ")");
    
    //Draw the Rectangle
    legendsvg.append("rect")
        .attr("class", "legendRect")
        .attr("x", -legendWidth/2)
        .attr("y", 0)
        //.attr("rx", hexRadius*1.25/2)
        .attr("width", legendWidth)
        .attr("height", 10)
        .style("fill", "url(#legend-traffic)");
    
    //Append title
    legendsvg.append("text")
        .attr("class", "legendTitle")
        .attr("x", 0)
        .attr("y", -10)
        .style("text-anchor", "middle")
        .text("Number of Accidents");
    
    //Set scale for x-axis
    var xScale = d3.scale.linear()
         .range([-legendWidth/2, legendWidth/2])
         .domain([ 0, d3.max(accidents, function(d) { return d.count; })] );
    
    //Define x-axis
    var xAxis = d3.svg.axis()
          .orient("bottom")
          .ticks(5)
          //.tickFormat(formatPercent)
          .scale(xScale);
    
    //Set up X axis
    legendsvg.append("g")
        .attr("class", "axis")
        .attr("transform", "translate(0," + (10) + ")")
        .call(xAxis);
    
     //--show/hide data---
    function showData(evt)
    {
        var target=evt.target
        target.setAttribute("opacity",".8")
    
        //---locate dataDiv near cursor--
        var x = evt.clientX;
        var y = evt.clientY;
        //---scrolling page---
        var offsetX=window.pageXOffset
        var offsetY=window.pageYOffset
    
        dataDiv.style.left=10+x+offsetX+"px"
        dataDiv.style.top=20+y+offsetY+"px"
        //---data--
        var data=target.getAttribute("data")
    
        //---format as desired---
        var html=data
    
        dataDiv.innerHTML=html
    
        dataDiv.style.visibility="visible"
    
    }
    function hideData(evt)
    {
        dataDiv.style.visibility="hidden"
        var target=evt.target
        target.removeAttribute("opacity")
    }
    <head>
        <meta charset="utf-8">
    
        <!-- D3.js -->
        <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js" charset="utf-8"></script>
    
        <!-- Google Font -->
        <link href='http://fonts.googleapis.com/css?family=Open+Sans:300,400' rel='stylesheet' type='text/css'>
    
    
    
        <style>
            html { font-size: 62.5%; }
    
            body {
              	font-size: 1rem;
              	font-family: 'Open Sans', sans-serif;
              	font-weight: 400;
              	fill: #8C8C8C;
              	text-align: center;
            }
    
            .timeLabel, .dayLabel {
                font-size: 1.6rem;
                fill: #AAAAAA;
                font-weight: 300;
            }
    
            text.axis-workweek, text.axis-worktime {
                fill: #404040;
                font-weight: 400;
            }
    
            .title {
                font-size: 2.8rem;
                fill: #4F4F4F;
                font-weight: 300;
            }
    
            .subtitle {
                font-size: 1.4rem;
                fill: #AAAAAA;
                font-weight: 300;
            }
    
            .credit {
                font-size: 1.2rem;
                fill: #AAAAAA;
                font-weight: 400;
            }
    
            .axis path, .axis tick, .axis line {
                  fill: none;
                  stroke: none;
              }
    
            text {
                  font-size: 1.2rem;
                  fill: #AAAAAA;
                  font-weight: 400;
            }
    
            .legendTitle {
                  font-size: 1.6rem;
                  fill: #4F4F4F;
                  font-weight: 300;
            }
        </style>
    
    </head>
    <body>
    
        <div id="trafficAccidents"></div>
    
    <div  id=dataDiv  style='box-shadow: 4px 4px 4px #888888;-webkit-box-shadow:2px 3px 4px #888888;padding:2px;position:absolute;top:0px;left:0px;visibility:hidden;background-color:linen;border: solid 1px black;border-radius:5px;'></div>
    
    
    
    </body>

    【讨论】:

      【解决方案2】:

      我认为你可能把它弄得太复杂了。

      尝试使用 foxToolTip.js

      https://github.com/MichaelRFox/foxToolTip.jS

      创建独特的后,我只需使用 .each 方法添加独特的工具提示。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-05-11
        • 2021-12-31
        • 1970-01-01
        相关资源
        最近更新 更多