当鼠标在折线图上的任何位置时显示提示

display tip when mouse is anywhere over line chart

我有一个复合折线图。

当我将鼠标悬停在图表的任何位置时,如何显示相应的值(日期和值),如下所示:

                var composechart = dc.compositeChart("#test_composed");

                composechart
                    .width(990)
                    .height(450)
                    .margins({ top: 50, right: 40, left: 50, bottom: 50 })
                    .x(d3.scaleTime().domain([new Date(2017, 0, 1), new Date(2019, 10, 30)]))
                    .rangeChart(xChart)                 
                    .elasticY(true)                     
                    .xUnits(d3.timeMonths)  
                    .legend(dc.legend().x(80).y(20).itemHeight(13).gap(5))                      
                    .renderHorizontalGridLines(true)
                    .brushOn(false)

                    .compose([
                     dc.lineChart(composechart)  
                        .dimension(salesgrafikDim)                          
                        .group(salesgrafikGroup,"Sales"),                                                    
                     dc.lineChart(composechart)
                        .dimension(satisgrafikDim)
                        .colors('red')
                        .group(quantitygrafikGroup,"Quantity")                                                     
                    ])

                     xChart
                     .width(990)                    
                     .height(40)
                     .margins({top: 0, right: 50, bottom: 20, left: 50})                
                     .dimension(salesgrafikDim)
                     .group(salesgrafikGroup)                                
                     .x(d3.scaleTime().domain([new Date(2017, 0, 1), new Date(2019, 10, 30)]))                          
                     .xUnits(d3.timeMonths);

                     xChart.yAxis().ticks(0);

基本上想要像下面的屏幕截图一样显示工具提示,其中鼠标不需要悬停在一个点上即可显示。

我可以直接使用 D3、svg 和 append 等等。但是我有其他图表和数据表的维度,所以我想将它与 dc.js.

一起使用

如果您不必将鼠标悬停在圆点上即可看到提示,将会更加用户友好且易于理解。

我的图表如下:

由于 dc.js 不直接支持此功能,您将最终进入 D3 以实现此功能。

首先,我们需要禁用现有的标题:

.title(() => '')

和悬停事件:

composite.on('pretransition', chart => {
  chart.children().forEach(
    child => child.selectAll('circle.dot')
      .on('mousemove', null)
      .on('mouseout', null)
    );
})

然后我们可以添加一个处理程序,将鼠标处理程序放在 SVG 本身上

composite.on('postRender', chart => {
  chart.svg().on('mousemove', () => { // 2
    // find closest data point
    const x = chart.x().invert(d3.mouse(chart.svg().node())[0] - chart.margins().left),
      xs = chart.children()[0].group().all().map(kv => kv.key),
      right = d3.bisectLeft(xs, x);
    let closest = right;
    if(right >= xs.length)
      closest = right - 1;
    else if(right > 0) {
      // see if point to the left is closer
      if(x - xs[right-1] < xs[right] - x)
        closest = right - 1;
    }
    //console.log('closest', new Date(x), closest, xs[closest])
    chart.children().forEach(child => { // 3
      child.g().selectAll('circle.dot').each(function(d) {
        if(d.x === xs[closest]) {
          child._showDot(d3.select(this));
          child.g().select('text.data-tip')
            .attr('visibility', 'visible')
            .attr('x', child.x()(d.x))
            .attr('y', child.y()(d.y))
            .text(tooltip_text(d.data))
        } else
          child._hideDot(d3.select(this));
      });
    })
  })
  chart.svg().on('mouseout', () => { // 4
    chart.children().forEach(child => {
      child.selectAll('circle.dot').each(function(d) {
        child._hideDot(d3.select(this));
      });
    })
  })
  chart.children().forEach(child => child.g() // 1
    .append('text')
    .attr('class', 'data-tip')
    .attr('fill', 'black')
    .attr('alignment-baseline', 'top')
    .attr('text-anchor', 'begin')
    .attr('visibility', 'hidden')
    )
});  

我不打算深入细节,但是这个

  1. 向每个 child 图表添加一个文本元素,这将显示提示
  2. 侦听 mousemove 并找到最近的点
  3. 显示每个 child
  4. 中所选点的点和文本
  5. 当鼠标离开图表时隐藏所有点和文本

我 运行 没有时间考虑这个,所以我没有尝试让文本正确定位或使其看起来漂亮。

线性刻度

Linear scale fiddle.

时间尺度

Time scale fiddle.

正如我在评论中提到的,如果线条靠得太近,您将 运行 遇到此设计的麻烦。你可以在这个例子的第一点看到这个问题。