【问题标题】:Stacked Area Chart in nvd3js - X Axis overflownvd3js中的堆积面积图-X轴溢出
【发布时间】:2014-02-07 01:12:35
【问题描述】:

我正在尝试使用 d3js 和 nvd3.js 实现类似于 example 的“堆积面积图”。此外,我想使用像this one 这样的上下文画笔来选择一个日期范围,这会影响堆积面积图。实际上,这已经在起作用,但是一旦所选日期范围不包含第一个日期,它就会以某种方式在 Y 轴顶部绘制一些线。请看下图:

这是我的代码:

堆积面积图

var margin = {
    top : 10,
    right : 20,
    bottom : 100,
    left : 20
}, width = 960, height = 300;

var svg_stack = d3.select("#stack").append("svg").attr("width", width + margin.left + margin.right).attr("height", (height + margin.top + margin.bottom));
function initStackChart() {
    nv.addGraph(function() {
        var chart = nv.models.stackedAreaChart().x(function(d) {
            return Date.parse(new Date(d[0]))
        }).y(function(d) {
            return d[1]
        }).clipEdge(false);

        chart.xAxis.tickFormat(function(d) {
            return d3.time.format('%x')(new Date(d))
        });

        chart.yAxis.tickFormat(d3.format(',.2f'));

        if (!!time_range) {
            chart.xDomain([time_range[0], time_range[1]]);
        }

        d3.select('#stack svg').datum(temp_data).transition().duration(100).call(chart);

        nv.utils.windowResize(chart.update);
        return chart;
    });
}

画笔

var margin = {top: 10, right: 20, bottom: 0, left: 20},
  width = 960,
  height = 50;

var contextHeight = 50;
  contextWidth = width;

var parseDate = d3.time.format("%Y-%m-%d").parse;

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

var xAxis =    d3.svg.axis().scale(x).tickSize(contextHeight).tickPadding(-10).orient("bottom");

var brush = d3.svg.brush()
.x(x)
.on("brush", brushed);

var area2 = d3.svg.area()
.interpolate("monotone")
.x(function(d) { return x(d.time); })
.y0(contextHeight)
.y1(0);

var svg_brush = d3.select("#brush").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom);

svg_brush.append("defs").append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);

var context = svg_brush.append("g").attr("class","context")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");

function initBrush(data)
{
  x.domain(d3.extent(data.map(function(d) { return d.time; })));
  context.append("g")
    .attr("class", "x axis top")
    .attr("transform", "translate(0,0)")
    .call(xAxis);

  context.append("g")
    .attr("class", "x brush")
    .call(brush)
    .selectAll("rect")
    .attr("y", 0)
    .attr("height", contextHeight);
};

function brushed() {
  var b = brush.empty() ? x.domain() : brush.extent();
  console.log(b);
  time_range=b;
  initStackChart();
}

数据

var temp_data = [
{
key: "Node0",
values:
  [      
    [  1364795940000, 10 ],
    [  1365020480000, 30 ],
    [  1365630480000, 30 ],
    [  1366000480012, 30 ],
    [  1366012740000, 0  ]  
  ]
},
{
key: "Node1",
values:
  [      
    [  1364795940000, 10 ],
    [  1365020480000, 20 ],
    [  1365630480000, 34 ],
    [  1366000480012, 82 ],
    [  1366012740000, 0  ]  
  ]
},
{
key: "Node2",
values:
  [      
    [  1364795940000, 20 ],
    [  1365020480000, 10 ],
    [  1365630480000, 0 ],
    [  1366000480012, 100 ],
    [  1366012740000, 80  ]   
  ]
},
{
key: "Node3",
values:
  [      
    [  1364795940000, 10 ],
    [  1365020480000, 60 ],
    [  1365630480000, 10 ],
    [  1366000480012, 10 ],
    [  1366012740000, 10  ]   
  ]
},
{
key: "Node4",
values:
  [      
    [  1364795940000, 16 ],
    [  1365020480000, 32 ],
    [  1365630480000, 10 ],
    [  1366000480012, 90 ],
    [  1366012740000, 10  ]  
  ]
},
{
key: "Node5",
values:
  [      
    [  1364795940000, 10 ],
    [  1365020480000, 50 ],
    [  1365630480000, 10 ],
    [  1366000480012, 20 ],
    [  1366012740000, 110  ]  
  ]
},
{
key: "Node6",
values:
  [      
    [  1364795940000, 19 ],
    [  1365020480000, 55 ],
    [  1365630480000, 32 ],
    [  1366000480012, 12 ],
    [  1366012740000, 12  ]  
  ]
},
{
key: "Node7",
values:
  [      
    [  1364795940000, 0 ],
    [  1365020480000, 20 ],
    [  1365630480000, 40 ],
    [  1366000480012, 30 ],
    [  1366012740000, 20  ]  
  ]
},
{
key: "Node8",
values:
  [      
    [  1364795940000, 12 ],
    [  1365020480000, 31 ],
    [  1365630480000, 40 ],
    [  1366000480012, 20 ],
    [  1366012740000, 15  ]  
  ]
},
{ 
key: "Node9",
values:
  [      
    [  1364795940000, 10 ],
    [  1365020480000, 35 ],
    [  1365630480000, 50 ],
    [  1366000480012, 30 ],
    [  1366012740000, 90 ]  
  ]
}
]

谢谢。

【问题讨论】:

  • 在我看来,您想过滤您传入的 tempData 以绘制图表。
  • 我自己不太确定。正确的方法是什么?我想通过在画笔上定义一个范围来“放大”图表。是否通常改为过滤数据?
  • 缩放图表应该可以,但不知何故你也在缩放剪切路径。您可能想查看 NVD3 的 line chart with viewfinder 示例,了解他们是如何做到的。

标签: javascript date d3.js nvd3.js


【解决方案1】:

在图表设置中将 .clipEdge(false); 更改为 .clipEdge(true);

编辑

好的,我已经设法使用以下代码在NVD3 live code site 上重新创建了您的问题(数据和标记与其堆叠图示例相同):

nv.addGraph(function() {
  var chart = nv.models.stackedAreaChart()
                .x(function(d) { return d[0] })
                .y(function(d) { return d[1] })
                .clipEdge(true);

  var chart2 = nv.models.stackedAreaChart()
                .x(function(d) { return d[0] })
                .y(function(d) { return d[1] })
                .xDomain([1096516800000, 1270008000000])
                .clipEdge(true);

  chart.xAxis
      .showMaxMin(false)
      .tickFormat(function(d) { return d3.time.format('%x')(new Date(d)) });    
  chart.yAxis
      .tickFormat(d3.format(',.2f'));

  chart2.xAxis
      .showMaxMin(false)
      .tickFormat(function(d) { return d3.time.format('%x')(new Date(d)) });    
  chart2.yAxis
      .tickFormat(d3.format(',.2f'));

  d3.select('#chart svg')
    .datum(data)
      .transition().duration(500).call(chart)
      .transition().delay(3000).duration(500)
        .call(chart2);

  nv.utils.windowResize(chart.update);

  return chart2;
});

这基本上就是您正在做的——创建一个全新的图表函数,并在同一个容器上调用它。图表功能主要选择所有相同的对象,并更改它们的属性——从而实现平滑过渡。但是,它提供给<clipPath> 元素的随机 id 代码(以确保每个元素都有一个唯一的 id)不再与它用作“剪辑路径”属性的那个匹配。您可以将其称为 NVD3 代码中的错误,但部分原因是您以意想不到的方式使用该函数。

相比之下,如果我使用这段代码:

nv.addGraph(function() {
  var chart = nv.models.stackedAreaChart()
                .x(function(d) { return d[0] })
                .y(function(d) { return d[1] })
                .clipEdge(true);

  chart.xAxis
      .showMaxMin(false)
      .tickFormat(function(d) { return d3.time.format('%x')(new Date(d)) });

  chart.yAxis
      .tickFormat(d3.format(',.2f'));

  var svg = d3.select('#chart svg')
    .datum(data)
      .transition().duration(500).call(chart);

  nv.utils.windowResize(chart.update);

  var change = window.setTimeout(function(){
        chart.xDomain([1096516800000, 1270008000000]);
        chart.update();
  }, 3000);

  return chart;
});

剪切路径仍然可以很好地工作。注意到区别了吗?我没有创建和调用一个全新的图表函数,而是使用新域更新了图表函数,并调用了函数的update() 方法。尝试重新安排您的刷机功能以这种方式进行更新,不仅应该解决您的剪切路径问题,而且您的代码也应该更快。

编辑 2

那么如何用你的原始代码来实现呢?

首先,您需要将在nv.addGraph() 中创建的图表函数对象保存到您的brushed() 函数可以访问的变量中。

然后,在您的 brushed() 函数中,修改保存的图表函数以应用新的 x 域,然后调用函数对象的更新方法。

var margin = {
    top : 10,
    right : 20,
    bottom : 100,
    left : 20
}, width = 960, height = 300;

var chart; // NEW! declare a variable that can be accessed by both
             // initialization and update functions

var svg_stack = d3.select("#stack")
                  .append("svg")
                  .attr("width", width + margin.left + margin.right)
                  .attr("height", (height + margin.top + margin.bottom));
function initStackChart() {
    nv.addGraph(function() {
        chart = nv.models.stackedAreaChart() 
                   // NEW! no "var" statement!
                   // this gets assigned to the chart variable declared above 

          /* rest of chart initialization code, the same as before */

   });
}

/* All the initialization code for the timeline brushing goes here, until: */

function brushed() {
  var b = brush.empty() ? x.domain() : brush.extent();
  console.log(b);
  time_range=b;

  chart.xDomain(b);  //modify the saved chart object
  chart.update();    //update the chart using the saved function
}

【讨论】:

  • 其实这是我在改成false之前尝试过的。它产生同样的失败。不过,谢谢你的帖子。
  • @caiuspb 我不知道为什么它不起作用,除非问题是您在绘制新图之前永远不会删除旧图。你能发布你的 DOM 更新后的样子吗?当然,不是每个节点,只有主要的 元素,包括它们的类、剪辑路径和翻译属性。
  • 据我所知,我不会删除任何旧图。我为我的 DOM 元素制作了两张截图。第一个在任何修改之前:s14.directupload.net/images/140117/hvejlfpz.png 第二个包含错误:s14.directupload.net/images/140117/5urcym8g.png。有没有其他方法可以将 DOM 元素复制为 ascii 字符串?谢谢。
  • @caiuspb 感谢提供图片,尽管我应该提到我还需要查看 clipPath 元素。您通常可以右键单击 DOM 检查器以将元素复制为 HTML 文本,但随后您必须手动删除内部内容。无论哪种方式,我都能重现问题并弄清楚发生了什么——请参阅答案的编辑。
  • @caiuspb。对不起,我把事情弄糊涂了。将setTimeout() 调用的结果保存到变量是一种很好的代码格式,这样您就可以取消计时器,但这就是change 的全部 - 对我用来延迟更新的计时器的引用,以便您可以看到它发生.重要的部分是在初始化图表函数时保存它——目前它是从nv.addGraph() 返回的,但随后被丢弃。我的评论太长了,所以我会添加另一个更新来澄清......
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-07-01
  • 1970-01-01
  • 2018-06-16
  • 1970-01-01
  • 2019-08-08
  • 2021-11-29
  • 2014-03-22
相关资源
最近更新 更多