D3刷组合条形图

 大师兄v断水流 发布于 2023-01-30 09:58

我试图刷牙工作类似于这个例子,但有一个分组的条形图:http://bl.ocks.org/mbostock/1667367

我真的不太了解刷牙是如何工作的(我还没有找到任何好的教程),所以我对于出了什么问题感到有些不知所措.我将尝试在下面包含相关的代码.该图表跟踪按天修复损坏的构建的时间,然后按投资组合分组.到目前为止,刷子已创建,用户可以移动并拖动它,但主图表中的条形图被奇怪地重新绘制,x轴根本不会更新.您将给予的任何帮助将不胜感激.谢谢.

// x0 is the time scale on the X axis
var main_x0 = d3.scale.ordinal().rangeRoundBands([0, main_width-275], 0.2);
var mini_x0 = d3.scale.ordinal().rangeRoundBands([0, main_width-275], 0.2);

// x1 is the portfolio scale on the X axis
var main_x1 = d3.scale.ordinal();
var mini_x1 = d3.scale.ordinal();

// Define the X axis
var main_xAxis = d3.svg.axis()
    .scale(main_x0)
    .tickFormat(dateFormat)
    .orient("bottom");

var mini_xAxis = d3.svg.axis()
    .scale(mini_x0)
    .tickFormat(dateFormat)
    .orient("bottom");

绑定数据后......

// define the axis domains
main_x0.domain(data.result.map( function(d) { return d.date; } )
    .sort(d3.ascending));
mini_x0.domain(data.result.map( function(d) { return d.date; } )
    .sort(d3.ascending));

main_x1.domain(data.result.map( function(d) { return d.portfolio; } )
    .sort(d3.ascending))
    .rangeRoundBands([0, main_x0.rangeBand() ], 0);
mini_x1.domain(data.result.map( function(d) { return d.portfolio; } )
    .sort(d3.ascending))
    .rangeRoundBands([0, main_x0.rangeBand() ], 0);

// Create brush for mini graph
var brush = d3.svg.brush()
  .x(mini_x0)
  .on("brush", brushed);

添加轴后等

// Create the bars
var bar = main.selectAll(".bars")
  .data(nested)
.enter().append("g")
  .attr("class", function(d) { return d.key + "-group bar"; })
  .attr("fill", function(d) { return color(d.key); } );

bar.selectAll("rect").append("rect")
  .data(function(d) { return d.values; })
.enter().append("rect")
  .attr("class", function(d) { return d.portfolio; })
  .attr("transform", function(d) { return "translate(" + main_x0(d.date) + ",0)"; })
  .attr("width", function(d) { return main_x1.rangeBand(); })
  .attr("x", function(d) { return main_x1(d.portfolio); })
  .attr("y", function(d) { return main_y(d.buildFixTime); })
  .attr("height", function(d) { return main_height - main_y(d.buildFixTime); });

这是刷子功能(尝试几种不同的选项)......

function brushed() {
    main_x1.domain(brush.empty() ? mini_x1.domain() : brush.extent());

    //main.select("rect")
      //.attr("x", function(d) { return d.values; })
      //.attr("width", function(d) { return d.values; });
    bar.select("rect")
      .attr("width", function(d) { return main_x1.rangeBand(); })
      .attr("x", function(d) { return main_x1(d.portfolio); });
      //.attr("y", function(d) { console.log(d); return main_y(d.buildFixTime); })
      //.attr("height", function(d) { return main_height - main_y(d.buildFixTime); });

    main.select(".x.axis").call(main_xAxis);
}

AmeliaBR.. 19

问题来自于尝试使用画笔来设置x比例域,当x尺度是序数尺度时.换句话说,x轴的预期域是一个类别列表,而不是max-min数值范围.所以问题就在刷牙功能的顶部:

function brushed() {
    main_x0.domain(brush.empty() ? mini_x0.domain() : brush.extent());

设置的域brush.extent()是两个数字的数组,然后完全抛弃你的序数.

根据wiki,如果附加到画笔函数的其中一个音阶是序数音阶,则返回的brush.extent()值是输出范围中的值,而不是输入域中的值. 序尺度没有一个invert()方法来数值范围转换成域值.

那么,您有几个选项可以继续:

您可以使用主x轴的线性时间刻度而不是序数刻度来重新绘制整个图形.但是你必须编写自己的函数来计算该轴上每天的宽度而不是能够使用.rangeBand().

您可以创建自己的"反转"功能,以确定哪些分类值(日期mini_x0.domain)包含在返回的范围内brush.extent().然后,你就必须重设main_x0.domain,只包括在轴上的日期,过滤掉你的矩形只绘制那些矩形.

或者,你可以离开main_x0.BE,并更改范围来代替.通过使图形的范围更大,可以使条形更大.结合剪切路径来切割绘图区域外的条形图,这具有仅显示条形的某个子集的效果,这无论如何都是您想要的.

但新范围应该是什么?返回的范围brush.extent()是刷牙矩形的开始和结束位置.如果您将这些值用作主图上的范围,则整个图形将被压缩到该宽度.这与你想要的相反.你想要的是最初填充该宽度的图形区域被拉伸以填充整个绘图区域.

因此,如果您的原始x范围是[0,100],并且画笔覆盖了区域[20,60],那么您需要一个满足以下条件的新范围:

新范围宽度的20%标记为0;

新范围宽度的60%标记为100.

因此,

新范围的总宽度为((100-0)/(60-20))*(100-0)= 250;

新范围的起点是(0 - (20/100)*250)= - 50;

新范围的结尾是(-50)+ 250 = 200.

现在你可以做所有的代数来自己搞清楚这个转换.但这实际上只是另一种缩放方程式,所以为什么不创建一个新的缩放函数来在旧范围和放大范围之间进行转换.

具体来说,我们需要一个线性比例,其输出范围设置为绘图区域的实际范围.然后根据我们想要拉伸的拉丝区域的范围设置域以覆盖绘图区域.最后,我们通过使用线性比例来计算出序数比例的范围,以计算出距离屏幕的原始最大值和最小值的距离.从那里,我们可以调整其他序数尺度并重新定位所有矩形.

在代码中:

//Initialization:
var main_xZoom = d3.scale.linear()
    .range([0, main_width - 275])
    .domain([0, main_width - 275]);

//Brushing function:
function brushed() {
    var originalRange = main_xZoom.range();
    main_xZoom.domain(brush.empty() ? 
                     originalRange: 
                     brush.extent() );

    main_x0.rangeRoundBands( [
        main_xZoom(originalRange[0]),
        main_xZoom(originalRange[1])
        ], 0.2);

    main_x1.rangeRoundBands([0, main_x0.rangeBand()], 0);

    bar.selectAll("rect")
        .attr("transform", function (d) {
            return "translate(" + main_x0(d.date) + ",0)";
        })
        .attr("width", function (d) {
            return main_x1.rangeBand();
        })
        .attr("x", function (d) {
            return main_x1(d.portfolio);
        });

    main.select("g.x.axis").call(main_xAxis);
}

基于简化代码的工作小提琴(注意:您仍然需要在主图上设置剪裁矩形):http:
//fiddle.jshell.net/CjaD3/1/

1 个回答
  • 问题来自于尝试使用画笔来设置x比例域,当x尺度是序数尺度时.换句话说,x轴的预期域是一个类别列表,而不是max-min数值范围.所以问题就在刷牙功能的顶部:

    function brushed() {
        main_x0.domain(brush.empty() ? mini_x0.domain() : brush.extent());
    

    设置的域brush.extent()是两个数字的数组,然后完全抛弃你的序数.

    根据wiki,如果附加到画笔函数的其中一个音阶是序数音阶,则返回的brush.extent()值是输出范围中的值,而不是输入域中的值. 序尺度没有一个invert()方法来数值范围转换成域值.

    那么,您有几个选项可以继续:

    您可以使用主x轴的线性时间刻度而不是序数刻度来重新绘制整个图形.但是你必须编写自己的函数来计算该轴上每天的宽度而不是能够使用.rangeBand().

    您可以创建自己的"反转"功能,以确定哪些分类值(日期mini_x0.domain)包含在返回的范围内brush.extent().然后,你就必须重设main_x0.domain,只包括在轴上的日期,过滤掉你的矩形只绘制那些矩形.

    或者,你可以离开main_x0.BE,并更改范围来代替.通过使图形的范围更大,可以使条形更大.结合剪切路径来切割绘图区域外的条形图,这具有仅显示条形的某个子集的效果,这无论如何都是您想要的.

    但新范围应该是什么?返回的范围brush.extent()是刷牙矩形的开始和结束位置.如果您将这些值用作主图上的范围,则整个图形将被压缩到该宽度.这与你想要的相反.你想要的是最初填充该宽度的图形区域被拉伸以填充整个绘图区域.

    因此,如果您的原始x范围是[0,100],并且画笔覆盖了区域[20,60],那么您需要一个满足以下条件的新范围:

    新范围宽度的20%标记为0;

    新范围宽度的60%标记为100.

    因此,

    新范围的总宽度为((100-0)/(60-20))*(100-0)= 250;

    新范围的起点是(0 - (20/100)*250)= - 50;

    新范围的结尾是(-50)+ 250 = 200.

    现在你可以做所有的代数来自己搞清楚这个转换.但这实际上只是另一种缩放方程式,所以为什么不创建一个新的缩放函数来在旧范围和放大范围之间进行转换.

    具体来说,我们需要一个线性比例,其输出范围设置为绘图区域的实际范围.然后根据我们想要拉伸的拉丝区域的范围设置域以覆盖绘图区域.最后,我们通过使用线性比例来计算出序数比例的范围,以计算出距离屏幕的原始最大值和最小值的距离.从那里,我们可以调整其他序数尺度并重新定位所有矩形.

    在代码中:

    //Initialization:
    var main_xZoom = d3.scale.linear()
        .range([0, main_width - 275])
        .domain([0, main_width - 275]);
    
    //Brushing function:
    function brushed() {
        var originalRange = main_xZoom.range();
        main_xZoom.domain(brush.empty() ? 
                         originalRange: 
                         brush.extent() );
    
        main_x0.rangeRoundBands( [
            main_xZoom(originalRange[0]),
            main_xZoom(originalRange[1])
            ], 0.2);
    
        main_x1.rangeRoundBands([0, main_x0.rangeBand()], 0);
    
        bar.selectAll("rect")
            .attr("transform", function (d) {
                return "translate(" + main_x0(d.date) + ",0)";
            })
            .attr("width", function (d) {
                return main_x1.rangeBand();
            })
            .attr("x", function (d) {
                return main_x1(d.portfolio);
            });
    
        main.select("g.x.axis").call(main_xAxis);
    }
    

    基于简化代码的工作小提琴(注意:您仍然需要在主图上设置剪裁矩形):http:
    //fiddle.jshell.net/CjaD3/1/

    2023-01-30 10:01 回答
撰写答案
今天,你开发时遇到什么问题呢?
立即提问
热门标签
PHP1.CN | 中国最专业的PHP中文社区 | PNG素材下载 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有