2017-04-10 52 views
3

我创建了一个声学数据的圆形热图表,圆圈的每一层都是同一天,并且有24个圆圈表示一天24小时。我想实现一些东西,所以我可以增加与被挖出的段相同日期的所有段的段高度,同时相应地减小所有其他段的高度以保持圆弧半径相同。目前我只能选择同一日期的所有细分,但我无法弄清楚如何操纵高度。有人能指出我正确的方向吗?D3圆形热图增加鼠标悬停时段的高度

这里是一个图片,你将鼠标悬停在段现在: enter image description here

这里是我的代码:

var radial_labels = ['2016-10-22', '2016-10-23', '2016-10-24', '2016-10-25', '2016-10-26', '2016-10-27', '2016-10-28', '2016-10-29', '2016-10-30']; 

    var segment_labels = ['0:00', '1:00', '2:00', '3:00', '4:00', '5:00', '6:00', '7:00', '8:00', '9:00', '10:00', '11:00','12:00','13:00','14:00','15:00','16:00','17:00','18:00','19:00','20:00','21:00','22:00','23:00']; 

    loadCircularHeatMap(data,"#chart",radial_labels, segment_labels); 


    function loadCircularHeatMap (dataset, dom_element_to_append_to,radial_labels,segment_labels) { 

    var margin = {top: 50, right: 50, bottom: 50, left: 50}; 
    var width = 1000 - margin.left - margin.right; 

    var height = width; 
    var innerRadius = 100;// width/14; 

    var segmentHeight = (width - margin.top - margin.bottom - 2*innerRadius)/(2*radial_labels.length); 

    var chart = circularHeatChart() 
    .innerRadius(innerRadius) 
    .segmentHeight(segmentHeight) 
    .domain([0,0.5,1]) 
    .range(["#ffffd9", "#7fcdbb" ,"#225ea8"]) 
    .radialLabels(radial_labels) 
    .segmentLabels(segment_labels); 

    chart.accessor(function(d) {return d.Average;}) 

    var svg = d3.select(dom_element_to_append_to) 
    .selectAll('svg') 
    .data([dataset]) 
    .enter() 
    .append('svg') 
    .attr("width", width + margin.left + margin.right) 
    .attr("height", height + margin.top + margin.bottom) 
    .append('g') 
    .attr("transform", 
     "translate(" + ((width)/2 - (radial_labels.length*segmentHeight + innerRadius) ) + "," + margin.top + ")") 
    .call(chart); 




    var tooltip = d3.select(dom_element_to_append_to) 
    .append('div') 
    .attr('class', 'tooltip'); 

    tooltip.append('div') 
    .attr('class', 'time'); 
    tooltip.append('div') 
    .attr('class', 'average'); 
    tooltip.append('div') 
    .attr('class', 'day'); 

    svg.selectAll("path") 
    .on('mouseover', function(d) { 
     console.log(d.Day); 
     // increase the segment height of the one being hovered as well as all others of the same date 
     // while decreasing the height of all others accordingly 

     d3.selectAll("path.segment-"+d.Day).style("opacity", function (p) {return 0.6}); 

     tooltip.select('.time').html("<b> Time: " + d.Time + "</b>"); 
     tooltip.select('.day').html("<b> Date: " + d.Day + "</b>"); 
     tooltip.select('.average').html("<b> Value: " + d.Average + "</b>"); 
     tooltip.style('display', 'block'); 
     tooltip.style('opacity',2); 
    }) 
    .on('mousemove', function(d) { 
     tooltip.style('top', (d3.event.layerY + 10) + 'px') 
     .style('left', (d3.event.layerX - 25) + 'px'); 
    }) 
    .on('mouseout', function(d) { 
     tooltip.style('display', 'none'); 
     tooltip.style('opacity',0); 
     // var time = d.Time; 
     // var timeCleaned = time.split(":").join("-"); 
     // var segment = d3.select("#segment-"+d.Day +"-"+timeCleaned); //designate selector variable for brevity 
     // var fillcolor = segment.select("desc").text(); //access original color from desc 
     // segment.style("fill", fillcolor); 

     d3.selectAll("path.segment-"+d.Day).style("opacity", function (p) {return 1}); 
    }) 
    .append("desc") //append the current color as a desc element 
    .text(function(d){ 
      var color = d3.scale.linear().domain([0,0.5,1]).range(["#ffffd9", "#7fcdbb" ,"#225ea8"]); 
      // how to access a function within reusable charts 
      console.log(color(d.Average)); 
      return color(d.Average); 
     }); 
    } 

function circularHeatChart() { 
    var margin = {top: 20, right: 50, bottom: 50, left: 20}, 
    innerRadius = 20, 
    numSegments = 24, 
    segmentHeight = 20, 
    domain = null, 
    range = ["white", "red"], 
    accessor = function(d) {return d;}, 
    radialLabels = segmentLabels = []; 

    function chart(selection) { 
     selection.each(function(data) { 
      var svg = d3.select(this); 

      var offset = innerRadius + Math.ceil(data.length/numSegments) * segmentHeight; 
      g = svg.append("g") 
       .classed("circular-heat", true) 
       .attr("transform", "translate(" + parseInt(margin.left + offset) + "," + parseInt(margin.top + offset) + ")"); 

      var autoDomain = false; 
      if (domain === null) { 
       domain = d3.extent(data, accessor); 
       autoDomain = true; 
      } 
      var color = d3.scale.linear().domain(domain).range(range); 
      if(autoDomain) 
       domain = null; 

      g.selectAll("path").data(data) 
       .enter().append("path") 
       // .attr("class","segment") 
       .attr("class",function(d){return "segment-"+d.Day}) 
       .attr("id",function(d){ 
        var time = d.Time; 
        var timeCleaned = time.split(":").join("-"); 
        return "segment-"+d.Day +"-"+timeCleaned;}) 
       .attr("d", d3.svg.arc().innerRadius(ir).outerRadius(or).startAngle(sa).endAngle(ea)) 
       .attr("stroke", function(d) {return '#252525';}) 
       .attr("fill", function(d) {return color(accessor(d));}); 

      // Unique id so that the text path defs are unique - is there a better way to do this? 
      var id = d3.selectAll(".circular-heat")[0].length; 


      //Segment labels 
      var segmentLabelOffset = 5; 
      var r = innerRadius + Math.ceil(data.length/numSegments) * segmentHeight + segmentLabelOffset; 
      labels = svg.append("g") 
       .classed("labels", true) 
       .classed("segment", true) 
       .attr("transform", "translate(" + parseInt(margin.left + offset) + "," + parseInt(margin.top + offset) + ")"); 

      labels.append("def") 
       .append("path") 
       .attr("id", "segment-label-path-"+id) 
       .attr("d", "m0 -" + r + " a" + r + " " + r + " 0 1 1 -1 0"); 

      labels.selectAll("text") 
       .data(segmentLabels).enter() 
       .append("text") 
       .append("textPath") 
       .attr("xlink:href", "#segment-label-path-"+id) 
       .style("font-size", "12px") 
       .attr("startOffset", function(d, i) {return i * 100/numSegments + 1.5+ "%";}) 
       .text(function(d) {return d;}); 
     }); 

    } 

    /* Arc functions */ 
    ir = function(d, i) { 
     return innerRadius + Math.floor(i/numSegments) * segmentHeight; 
    } 
    or = function(d, i) { 
     return innerRadius + segmentHeight + Math.floor(i/numSegments) * segmentHeight; 
    } 
    sa = function(d, i) { 
     return (i * 2 * Math.PI)/numSegments; 
    } 
    ea = function(d, i) { 
     return ((i + 1) * 2 * Math.PI)/numSegments; 
    } 

    /* Configuration getters/setters */ 
    chart.margin = function(_) { 
     if (!arguments.length) return margin; 
     margin = _; 
     return chart; 
    }; 

    chart.innerRadius = function(_) { 
     if (!arguments.length) return innerRadius; 
     innerRadius = _; 
     return chart; 
    }; 

    chart.numSegments = function(_) { 
     if (!arguments.length) return numSegments; 
     numSegments = _; 
     return chart; 
    }; 

    chart.segmentHeight = function(_) { 
     if (!arguments.length) return segmentHeight; 
     segmentHeight = _; 
     return chart; 
    }; 

    chart.domain = function(_) { 
     if (!arguments.length) return domain; 
     domain = _; 
     return chart; 
    }; 

    chart.range = function(_) { 
     if (!arguments.length) return range; 
     range = _; 
     return chart; 
    }; 

    chart.radialLabels = function(_) { 
     if (!arguments.length) return radialLabels; 
     if (_ == null) _ = []; 
     radialLabels = _; 
     return chart; 
    }; 

    chart.segmentLabels = function(_) { 
     if (!arguments.length) return segmentLabels; 
     if (_ == null) _ = []; 
     segmentLabels = _; 
     return chart; 
    }; 

    chart.accessor = function(_) { 
     if (!arguments.length) return accessor; 
     accessor = _; 
     return chart; 
    }; 

    return chart; 
} 

这里是它是什么目前演示,如: http://jhjanicki.github.io/circular_heat_acoustic

+2

我用你的代码和数据数组做了一个小提琴,它帮助你更轻松:https://jsfiddle.net/gerardofurtado/fw9k5gLf/ –

+0

谢谢你的努力! – jhjanicki

+0

通过转换增加段高度? – blackmiaool

回答

2

My demo

核心代码片段如下:

var targetIndex=Math.floor(i/numSegments);//the layer you are hovering 
var zoomSize=10;//inner 10px and outer 10px 
var layerCnt=data.length/numSegments; 


d3.selectAll("path.segment")//.arc indicates segment 
    .transition().duration(200)//transtion effect 
    .attr("d", d3.svg.arc()//set d again 
     .innerRadius(ir) 
     .outerRadius(or) 
     .startAngle(sa) 
     .endAngle(ea)) 


function getRadius(floor) { 
    if(floor===0){//inner radius doesn't change 
     return innerRadius; 
    } 
    if(floor===layerCnt){//outer radius doesn't change 
     return innerRadius+layerCnt*segmentHeight; 
    } 
    if(floor<=targetIndex){//it's math 
     return innerRadius + floor * segmentHeight - zoomSize *(floor/targetIndex);  
    }else{//math again 
     return innerRadius + floor * segmentHeight + zoomSize*((layerCnt-floor)/(layerCnt-targetIndex)); 
    }      
} 

function ir(d, i) {      
    return getRadius(Math.floor(i/numSegments)); 
} 

function or(d, i) { 
    return getRadius(Math.floor(i/numSegments) + 1); 
} 
+0

真棒谢谢你,正是我正在寻找! – jhjanicki

+0

我正在阅读代码,你能解释为什么有必要把它放在draw函数中并使用setTimeout吗? – jhjanicki

+0

@jhjanicki我在那里离开了我的选秀......我更新了我的回答,以尽量减少变化。 – blackmiaool

2

由于这是一条路径,因此无法提高其高度。但是你可以缩放路径。

我的算法是

  • 获取段的质心的鼠标。
  • 将鼠标上的路径缩放到路径的质心1.5 w.r.t。
  • 移动所选路径页首
  • 缩减对小鼠的路径出去1

片段获得的质心:

function getBoundingBoxCenter (selection) { 
    // get the DOM element from a D3 selection 
    // you could also use "this" inside .each() 
    var element = selection.node(); 
    // use the native SVG interface to get the bounding box 
    var bbox = element.getBBox(); 
    // return the center of the bounding box 
    return [bbox.x + bbox.width/2, bbox.y + bbox.height/2]; 
} 

摘录于上顶

所选元件移动 ​​

在鼠标上方做:

 var sel = d3.select(this); 
    sel.moveToFront() 

     var centroid = getBoundingBoxCenter(d3.select(this)); 
     //zoom in to the centroid 
     d3.select(this).attr("transform", function(p) { 
       return "translate(" + centroid[0] + "," + centroid[1] + ")" 
         + "scale(" + 1.5 + ")" 
         + "translate(" + -centroid[0] + "," + -centroid[1] + ")"; 
    }); 

在鼠标移出做:

 var centroid = getBoundingBoxCenter(d3.select(this)); 
     d3.select(this).attr("transform", function(p) { 
       return "translate(" + centroid[0] + "," + centroid[1] + ")" 
         + "scale(" + 1 + ")" 
         + "translate(" + -centroid[0] + "," + -centroid[1] + ")"; 
    }); 

特别感谢@Gerardo为把这件事上的小提琴:)

工作代码here

+1

谢谢你的解释,我现在明白了更好路径如何工作。扩展你的代码非常简单,所以不是只有一个片断,同一图层中的所有片段都会放大,其余的图层(那些不是同一个日期的图层)缩小了? – jhjanicki

+0

ohh缩放整行的问题是段会重叠在一起...它不会看起来不错,但是你可以尝试一下。 – Cyril

+0

好的,无论如何,我可能会给它一个镜头。所以从我的理解来看,也许我试图完成的路径很难? – jhjanicki

相关问题