2016-03-14 142 views
1

编辑:这是一个问题的小提琴。问题与工具提示D3线图

https://jsfiddle.net/8tpz5s9w/1/

我有实现一个简单的D3提示的问题。

我有一个多行图表,它适用于其中一行,但仅适用于我的第二行的第一个点。所有行的X值是相同的,只是不同的y值。

小贴士?相关的代码部分:

var div = d3.select("body").append("div") 
    .attr("class", "tooltip")  
    .style("opacity", 0);  

points.selectAll('.point') 
     .data(function(d) { 
      console.log('.point d is: ', d); 
      d.values.forEach(function(kv) { 
      console.log('kv is:', kv); 
      return kv.name = d.name; 
      }) 
      return d.values; 
     }) 
     .enter() 
     .append('circle') 
     .attr('circleId', function(d, i) { 
      return 'circleId-'+(i+1); 
     }) 
     .attr('cx', function(d) { 
      return x(d.Period); 
     }) 
     .attr('cy', function(d) { 
      return y(d.Value); 
     }) 
     .attr('r', function(d) { 
      return dotRadius() 
     }) 
     .on("mouseover", function(d) { 
      console.log('mouseover d is :', d); 
      div.transition()  
      .duration(200)  
      .style("opacity", .9);  
      div.html(d.name + ': ' + d.Value) 
      .style("left", (d3.event.pageX - 24) + "px") 
      .style("top", (d3.event.pageY - 56) + "px"); 
     })   
     .on("mouseout", function(d) { 
      div.transition()  
      .duration(500)  
      .style("opacity", 0); 
     }); 

整个代码减去CSS:

jQuery(document).ready(function ($) { 

    var margin = {top: 20, right: 30, bottom: 60, left: 45}, 
    width = 375 - margin.left - margin.right, 
    height = 225 - margin.top - margin.bottom, 
    dotRadius = function() { return 3 }; 
    // dispatch = d3.dispatch("pointMouseover", "pointMouseout"); 

    var x = d3.time.scale() 
    .range([0, width]); 

    var y = d3.scale.linear() 
    .range([height, 0]); 

    var xAxis = d3.svg.axis() 
    .scale(x) 
    .orient('bottom') 
    .tickFormat(d3.time.format('%b')); 

    var yAxis = d3.svg.axis() 
    .scale(y) 
    .orient('left'); 

    var div = d3.select("body").append("div") 
    .attr("class", "tooltip")  
    .style("opacity", 0); 

    // This is a function that determines the colours of the lines drawn, up to 10. 
    var color = d3.scale.category10(); 

    // This is used to format the time for our data. 
    var formatTime = d3.time.format("%Y-%m-%d"); 

    var line = d3.svg.line() 
    .x(function(d) { return x(d.Period); }) 
    .y(function(d) { return y(d.Value); }); 


    var svg = d3.select("#pipeline-chart-render") 
    .attr('width', width + margin.left + margin.right) 
    .attr('height', height + margin.top + margin.bottom) 
    .append('g') 
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); 

    svg.append("g") 
    .attr("class", "x axis") 
    .attr("transform", "translate(0," + height + ")") 

    // This separates the data into the lines we want, although the data is stored 
    // In the same original object. 
    color.domain(d3.keys(data[0].values[0]).filter(function(key) { 
    if (key === 'Amount' 
    || key === 'Quantity') { 
     return key 
    } 
    })); 


    // This returns the data into two separate objects which can be graphed. 
    // In this case, Amount and Quantity. 
    var datasets = color.domain().map(function(name) { 
    return { 
     name: name, 
     values: data.map(function(d) { 
     return { 
      Period: formatTime.parse(d.values[0].Time), 
      Value: +d.values[0][name]}; 
     }) 
    }; 
    }); 

    console.log('datasets is: ', datasets); 

    // set the minYDomainValue to zero instead of letting it be a lingering magic number. 
    var minDomainValue = 0 

    var minDate = d3.min(datasets, function(d0){ 
    return d3.min(d0.values, function(d1){ 
     return d1.Period; 
    }) 
    }), 
    maxDate = d3.max(datasets, function(d0){ 
    return d3.max(d0.values, function(d1){ 
     return d1.Period; 
    }) 
    }); 

    x.domain([minDate, maxDate]); 
    y.domain([ 
    minDomainValue, 
    d3.max(datasets, function(c) { return d3.max(c.values, function(v) { return v.Value; }); }) 
    ]) 

    // Append the x-axis class and move axis around. 
    svg.append("g") 
    .attr("class", "x axis") 
    .attr("transform", "translate(0," + height + ")") 
    .call(xAxis) 
    .append('text') 
    .attr('x', 150) 
    .attr('y', 36) 
    .style('text-anchor', 'middle') 
    .text('2015'); 

    // Append the y-axis class. 
    svg.append("g") 
    .attr("class", "y axis") 
    .call(yAxis) 
    .append("text") 
    .attr("transform", "rotate(-90)") 
    .attr("y", -30) 
    .attr('x', -30) 
    .style("text-anchor", "end") 
    .text("Quantity/Amount"); 

    // The following is for defining the area BETWEEN graphs. 
    var areaAboveQuantity = d3.svg.area() 
    .x(line.x()) 
    .y0(line.y()) 
    .y1(0); 

    var areaBelowQuantity = d3.svg.area() 
    .x(line.x()) 
    .y0(line.y()) 
    .y1(height); 

    var areaAboveAmount = d3.svg.area() 
    .x(line.x()) 
    .y0(line.y()) 
    .y1(0); 
    var areaBelowAmount = d3.svg.area() 
    .x(line.x()) 
    .y0(line.y()) 
    .y1(height); 

    var pipeline = svg.selectAll('.pipeline') 
    .data(datasets); 

    pipeline.enter() 
    .append('g') 
    .attr('class', 'pipeline'); 


    pipeline.append('path') 
    .attr('class', 'line') 
    .attr('id', function(d, i) { 
     return 'pipeline-'+(i+1); 
    }) 
    .attr('d', function(d) { console.log('line d is: ', d); return line(d.values); }) 
    .attr("data-legend",function(d) { return d.name}) 
    .style("stroke", function(d) { return color(d.name); }) 

    pipeline.exit().remove() 

    // Rendering the points on the graph. 
    var points = svg.selectAll('.pipelinePoint') 
    .data(datasets); 

    points 
    .enter() 
    .append('g') 
    .attr('class', 'pipelinePoint'); 

    points.selectAll('.point') 
    .data(function(d) { 
     console.log('.point d is: ', d); 
     d.values.forEach(function(kv) { 
     console.log('kv is:', kv); 
     return kv.name = d.name; 
     }) 
     return d.values; 
    }) 
    .enter() 
    .append('circle') 
    .attr('circleId', function(d, i) { 
     return 'circleId-'+(i+1); 
    }) 
    .attr('cx', function(d) { 
     return x(d.Period); 
    }) 
    .attr('cy', function(d) { 
     return y(d.Value); 
    }) 
    .attr('r', function(d) { 
     return dotRadius() 
    }) 
    .on("mouseover", function(d) { 
     console.log('mouseover d is :', d); 
     div.transition()  
     .duration(200)  
     .style("opacity", .9);  
     div.html(d.name + ': ' + d.Value) 
     .style("left", (d3.event.pageX - 24) + "px") 
     .style("top", (d3.event.pageY - 56) + "px"); 
    })   
    .on("mouseout", function(d) { 
     div.transition()  
     .duration(500)  
     .style("opacity", 0); 
    }); 



    var defs = svg.append('defs'); 

    defs.append('clipPath') 
    .attr('id', 'clip-quantity') 
    .append('path') 
    .datum(datasets) 
    .attr('d', function(d) { 
     return areaAboveQuantity(d[1].values); 
    }); 

    defs.append('clipPath') 
    .attr('id', 'clip-amount') 
    .append('path') 
    .datum(datasets) 
    .attr('d', function(d) { 
     return areaAboveAmount(d[0].values); 
    }); 


    svg.append('path') 
    .datum(datasets) 
    .attr('class', 'area') 
    .attr('d', function(d) { 
     return areaBelowQuantity(d[1].values) 
    }); 

    // Quantity IS ABOVE Amount 
    svg.append('path') 
    .datum(datasets) 
    .attr('d', function(d) { 
     areaBelowQuantity(d[1].values); 
    }) 
    .attr('clip-path', 'url(#clip-amount)') 
    .style('fill', 'steelblue') 
    .style('opacity', '0.2'); 


    // Amount IS ABOVE Quanity 
    svg.append('path') 
    .datum(datasets) 
    .attr('d', function(d) { 
     return areaBelowAmount(d[0].values); 
    }) 
    .attr('clip-path', 'url(#clip-quantity)') 
    .style('fill', 'steelblue') 
    .style('opacity', '0.2') 
    // .on("mouseover", function(d) { 
    // console.log('mouseover d is :', d); 
    // div.transition()  
    //  .duration(200)  
    //  .style("opacity", .9);  
    // div.html('Hello') 
    //  .style("left", (d3.event.pageX) + "px") 
    //  .style("top", (d3.event.pageY - 28) + "px"); 
    // })   
    // .on("mouseout", function(d) { 
    // div.transition()  
    //  .duration(500)  
    //  .style("opacity", 0); 
    // }); 


    legend1 = svg.append("g") 
    .attr("class","legend") 
    .attr("transform","translate(-20,180)") 
    .style("font-size","12px") 
    .call(d3Legend) 

}) 

var d3Legend = function(g) { 
    g.each(function() { 
    var g= d3.select(this), 
     items = {}, 
     svg = d3.select(g.property("nearestViewportElement")), 
     legendPadding = g.attr("data-style-padding") || 5, 
     lb = g.selectAll(".legend-box").data([true]), 
     li = g.selectAll(".legend-items").data([true]) 

    lb.enter().append("rect").classed("legend-box",true) 
    li.enter().append("g").classed("legend-items",true) 

    svg.selectAll("[data-legend]").each(function() { 
     var self = d3.select(this) 
     items[self.attr("data-legend")] = { 
     pos : self.attr("data-legend-pos") || this.getBBox().y, 
     color : self.attr("data-legend-color") != undefined ? self.attr("data-legend-color") : self.style("fill") != 'none' ? self.style("fill") : self.style("stroke") 
     } 
    }) 

    items = d3.entries(items).sort(function(a,b) { return a.value.pos-b.value.pos}) 

    li.selectAll("text") 
     .data(items,function(d) { return d.key}) 
     .call(function(d) { d.enter().append("text")}) 
     .call(function(d) { d.exit().remove()}) 
     .attr("y",function(d,i) { return i+"em"}) 
     .attr("x","1em") 
     .text(function(d) { ;return d.key}) 

    li.selectAll("circle") 
     .data(items,function(d) { return d.key}) 
     .call(function(d) { d.enter().append("circle")}) 
     .call(function(d) { d.exit().remove()}) 
     .attr("cy",function(d,i) { return i-0.25+"em"}) 
     .attr("cx",0) 
     .attr("r","0.4em") 
     .style("fill",function(d) { return d.value.color}) 

    // Reposition and resize the box 
    var lbbox = li[0][0].getBBox() 
    lb.attr("x",(lbbox.x-legendPadding)) 
     .attr("y",(lbbox.y-legendPadding)) 
     .attr("height",(lbbox.height+2*legendPadding)) 
     .attr("width",(lbbox.width+2*legendPadding)) 
    }) 
    return g 
}; 

var data = [ 
    { 
    key: 1, 
    values: [ 
     { 
     Amount: 33, 
     Quantity: 22, 
     Time: '2015-01-01' 
     } 
    ] 
    }, 
    { 
    key: 2, 
    values: [ 
     { 
     Amount: 52, 
     Quantity: 20, 
     Time: '2015-02-01' 
     } 
    ] 
    }, 
    { 
    key: 3, 
    values: [ 
     { 
     Amount: 63, 
     Quantity: 30, 
     Time: '2015-03-01' 
     } 
    ] 
    }, 
    { 
    key: 4, 
    values: [ 
     { 
     Amount: 92, 
     Quantity: 60, 
     Time: '2015-04-01' 
     } 
    ] 
    }, 
    { 
    key: 5, 
    values: [ 
     { 
     Amount: 50, 
     Quantity: 29, 
     Time: '2015-05-01' 
     } 
    ] 
    }, 
    { 
    key: 6, 
    values: [ 
     { 
     Amount: 53, 
     Quantity: 25, 
     Time: '2015-06-01' 
     } 
    ] 
    }, 
    { 
    key: 7, 
    values: [ 
     { 
     Amount: 46, 
     Quantity: 12, 
     Time: '2015-07-01' 
     } 
    ] 
    }, 
    { 
    key: 8, 
    values: [ 
     { 
     Amount: 52, 
     Quantity: 15, 
     Time: '2015-08-01' 
     } 
    ] 
    }, 
    { 
    key: 9, 
    values: [ 
     { 
     Amount: 55, 
     Quantity: 20, 
     Time: '2015-09-01' 
     } 
    ] 
    }, 
    { 
    key: 10, 
    values: [ 
     { 
     Amount: 35, 
     Quantity: 17, 
     Time: '2015-10-01' 
     } 
    ] 
    }, 
    { 
    key: 11, 
    values: [ 
     { 
     Amount: 80, 
     Quantity: 45, 
     Time: '2015-11-01' 
     } 
    ] 
    }, 
    { 
    key: 12, 
    values: [ 
     { 
     Amount: 64, 
     Quantity: 24, 
     Time: '2015-12-01' 
     } 
    ] 
    } 
] 
+0

设置jsfiddle。更容易阅读代码,当你真的可以看到发生了什么 – thatOneGuy

+0

在这里你去:) https://jsfiddle.net/8tpz5s9w/1/ – Tulun

回答

1

其因你的路越来越在能够悬停在该节点的方式。您需要设置pointer-eventsnone这个像这样:

我创建了一个类 - noPointers:

.noPointers{ 
    pointer-events:none; 
} 

而且在方式应用于此的路径,多数民众赞成(线369):

// Amount IS ABOVE Quanity 
    svg.append('path') 
     .datum(datasets).attr('class', 'noPointers')//added class here 

更新提琴:https://jsfiddle.net/reko91/8tpz5s9w/3/

+0

是的,这很好地工作。这只是一个CSS解决方案而感到悲伤。 – Tulun

+0

哈哈通常的答案就是这么简单:)只需知道在哪里看:))) – thatOneGuy