d3js中svg heatmap / colorscale的工具提示

时间:2016-06-28 19:56:26

标签: javascript angularjs d3.js svg

我正在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); 
                  });

这里的想法是,对于我访问的每个方格,工具提示将显示相关的标签(我仍然不使用标签,但它们将成为我已经展示的数据集的一部分,I& #39; m使用makeid()函数创建随机名称)以及相关time_spent数据的细分。根据我现在使用的内容,它正在编写完整的数字列表,而不是与每个方块相关的数字。想法?谢谢。

2 个答案:

答案 0 :(得分:0)

不是为每个单元格构建tootip HTML,而是考虑将数据附加到每个单元格rect。创建一个数据div并在光标移过单元格时填充它。

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

&#13;
&#13;
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")
}
&#13;
<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>
&#13;
&#13;
&#13;

答案 1 :(得分:0)

我认为你可能会让这个过于复杂。

尝试使用foxToolTip.js

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

创建独特的“我”后,只需使用.each方法添加唯一的工具提示即可。