2017-04-10 46 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

相關問題