javascript - 为什么我的图表 D3.js 上的条形图没有以正确的顺序出现?

标签 javascript d3.js

我在创建并排条形图时遇到问题。

我将在这里放置我的代码和图像,说明条形图如何不并排地散布颜色。我是 D3.js 新手,不知道为什么会发生这种情况。

现在的图表:

enter image description here

在我的 jsonFile 中,我有两个销售员,其中显示了每个销售员的销售历史记录。例如,我需要显示 1 月份卖家 A 和卖家 B 上一年的销售历史记录,但正如您在上图中看到的那样,这并不是正在发生的情况。这些栏位于错误的位置。

我的 HTML 代码:

var sales = [
    {
        "vendnm": "CHARNECA",
        "MonthAbrev": "Jan",
        "Vendas_Ano": 21611.950000000004,
        "Vendas_Ant": 16033.31
    },
    {
        "vendnm": "CHARNECA",
        "MonthAbrev": "Fev",
        "Vendas_Ano": 48108.08,
        "Vendas_Ant": 51142.299999999996
    },
    {
        "vendnm": "CHARNECA",
        "MonthAbrev": "Mar",
        "Vendas_Ano": 13427.280000000004,
        "Vendas_Ant": 21274.129999999997
    },
    {
        "vendnm": "CHARNECA",
        "MonthAbrev": "Apr",
        "Vendas_Ano": 28553.83,
        "Vendas_Ant": 15228.569999999998
    },
    {
        "vendnm": "CHARNECA",
        "MonthAbrev": "Mai",
        "Vendas_Ano": 19639.59,
        "Vendas_Ant": 10291.359999999999
    },
    {
        "vendnm": "CHARNECA",
        "MonthAbrev": "Jun",
        "Vendas_Ano": 22530.840000000007,
        "Vendas_Ant": 21905.29
    },
    {
        "vendnm": "CHARNECA",
        "MonthAbrev": "Jul",
        "Vendas_Ano": 20216.759999999995,
        "Vendas_Ant": 8276
    },
    {
        "vendnm": "CHARNECA",
        "MonthAbrev": "Ago",
        "Vendas_Ano": 39832.04,
        "Vendas_Ant": 49810.810000000005
    },
    {
        "vendnm": "CHARNECA",
        "MonthAbrev": "Set",
        "Vendas_Ano": 12861.52,
        "Vendas_Ant": 24425.32
    },
    {
        "vendnm": "CHARNECA",
        "MonthAbrev": "Out",
        "Vendas_Ano": 9227.34,
        "Vendas_Ant": 17804.65
    },
    {
        "vendnm": "CHARNECA",
        "MonthAbrev": "Nov",
        "Vendas_Ano": 0,
        "Vendas_Ant": 9993.34
    },
    {
        "vendnm": "CHARNECA",
        "MonthAbrev": "Dez",
        "Vendas_Ano": 0,
        "Vendas_Ant": 36568.7
    },
    {
        "vendnm": "JOÃO LUIS",
        "MonthAbrev": "Jan",
        "Vendas_Ano": 22681.14,
        "Vendas_Ant": 28587.71
    },
    {
        "vendnm": "JOÃO LUIS",
        "MonthAbrev": "Fev",
        "Vendas_Ano": 31382.11,
        "Vendas_Ant": 37637.420000000006
    },
    {
        "vendnm": "JOÃO LUIS",
        "MonthAbrev": "Mar",
        "Vendas_Ano": 32453.779999999995,
        "Vendas_Ant": 32993.12
    },
    {
        "vendnm": "JOÃO LUIS",
        "MonthAbrev": "Apr",
        "Vendas_Ano": 23445.4,
        "Vendas_Ant": 30835.75
    },
    {
        "vendnm": "JOÃO LUIS",
        "MonthAbrev": "Mai",
        "Vendas_Ano": 16471.71,
        "Vendas_Ant": 18028.07
    },
    {
        "vendnm": "JOÃO LUIS",
        "MonthAbrev": "Jun",
        "Vendas_Ano": 11617.470000000001,
        "Vendas_Ant": 22651.53
    },
    {
        "vendnm": "JOÃO LUIS",
        "MonthAbrev": "Jul",
        "Vendas_Ano": 24699.44,
        "Vendas_Ant": 34152.28999999999
    },
    {
        "vendnm": "JOÃO LUIS",
        "MonthAbrev": "Ago",
        "Vendas_Ano": 35476.94000000001,
        "Vendas_Ant": 22069.699999999997
    },
    {
        "vendnm": "JOÃO LUIS",
        "MonthAbrev": "Set",
        "Vendas_Ano": 37462.92,
        "Vendas_Ant": 23120.08
    },
    {
        "vendnm": "JOÃO LUIS",
        "MonthAbrev": "Out",
        "Vendas_Ano": 22042.140000000003,
        "Vendas_Ant": 28449.95
    },
    {
        "vendnm": "JOÃO LUIS",
        "MonthAbrev": "Nov",
        "Vendas_Ano": 0,
        "Vendas_Ant": 30755.050000000003
    },
    {
        "vendnm": "JOÃO LUIS",
        "MonthAbrev": "Dez",
        "Vendas_Ano": 0,
        "Vendas_Ant": 22838.64
    }
]
var charnecaData = [];
    var joaoData = [];

    for (var i = 0; i < sales.length; i++) {
        if (sales[i]["vendnm"] === "CHARNECA") {
            charnecaData.push(sales[i]);
        } else {
            joaoData.push(sales[i]);
        }
    }

    //functions for toggling between data
    function change(value) {
        if (value === 'lastYear') {
            update(charnecaData);
        } else if (value === 'currentYear') {
            update(joaoData);
        } else {
            update(sales);
        }
    }

    function update(data) {

        xChart.domain(data.map(function (d) { return d.MonthAbrev; }));

        yChart.domain([0, d3.max(data, function (d) { return +d.Vendas_Ant; })]);

        var barWidth = width / data.length;

        var bars = chart.selectAll(".bar")
            .remove()
            .exit()
            .data(data)

        bars.enter()
            .append("rect")
            .attr("class", "bar")
            .attr("x", function (d, i) { return i * barWidth + 1 })
            .attr("y", function (d) { return yChart(d.Vendas_Ant); })
            .attr("height", function (d) { return height - yChart(d.Vendas_Ant); })
            .attr("width", barWidth - 5)
            .attr("fill", function (d) {
                if (d.vendnm === 'CHARNECA') {
                    return "rgb(251,180,174)";
                } else {
                    return "rgb(179,205,227)";
                }
            })
            .on("mouseover", function (a) {
                chart.append("text")
                    .attr("class", "title-text")
                    .style("fill", "rgb(44, 160, 44)")
                    .text(a.vendnm + ' - ' +
                        a.MonthAbrev + ' - ' +
                        Number(Math.round(a.Vendas_Ant * 100) / 100).toLocaleString("es-ES", { minimumFractionDigits: 2 }) + '€'
                    )
                    .attr("text-anchor", "middle")
                    .attr("x", 400)
                    .attr("y", 50);
            })
            .on("mouseout", function (a) {
                chart.select(".title-text").remove();
            })

        //left axis
        chart.select('.y')
            .call(yAxis)
        //bottom axis
        chart.select('.xAxis')
            .attr("transform", "translate(0," + height + ")")
            .call(xAxis)
            .selectAll("text")
            .style("text-anchor", "end")
            .attr("dx", "-.8em")
            .attr("dy", ".15em")
            .attr("transform", function (d) {
                return "rotate(-65)";
            });

    }//end update

    var margin = { top: 20, right: 20, bottom: 30, left: 50 };
    var width = 600;
    var height = 300;

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

    var xChart = d3.scaleBand()
        .range([0, width]);

    var yChart = d3.scaleLinear()
        .range([height, 0]);

    var xAxis = d3.axisBottom(xChart);
    var yAxis = d3.axisLeft(yChart);

    chart.append("g")
        .attr("class", "y axis")
        .call(yAxis)

    chart.append("g")
        .attr("class", "xAxis")
        .attr("transform", "translate(0," + height + ")")
        .call(xAxis)
        .selectAll("text")
        .style("text-anchor", "end")
        .attr("dx", "-.8em")
        .attr("dy", ".15em")
        .attr("transform", function (d) {
            return "rotate(-65)";
        });

    update(sales);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
<head>
    <script src="https://d3js.org/d3.v4.min.js"></script>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
</head>

<style>
    .radio {
        text-align: center;
    }

    .row {
        text-align: center;
    }
</style>

<body>

    <div class='container'>
        <div class='row'>
            <div class='radio'>
                <label class='radio-inline'>
                    <input type="radio" name="gender" value="lastYear" onclick='change(this.value)'> Last Year
                </label>
                <label class='radio-inline'>
                    <input type="radio" name="gender" value="currentYear" onclick='change(this.value)'> Current Year
                </label>
                <label class='radio-inline'>
                    <input type="radio" name="gender" value="both" onclick='change(this.value)' checked> Both
                </label>
            </div>
            <svg class='chart'>
            </svg>
        </div>
    </div>
</body>

正如我所说,我是 D3.js 的新手,我正在基于以下代码进行此测试:https://bl.ocks.org/syncopika/f1c9036b0deb058454f825238a95b6be

我对比了好几次才知道自己哪里做错了,但是就是解决不了问题。

预先感谢您的帮助。

最佳答案

首先,scaleBand() 有一个函数可以为您提供每个点的宽度:

var barWidth = xChart.bandwidth()/2;

然后您需要使用xChart()函数来获取每个数据点的x坐标,而不是根据特定数据点的索引来计算它.

通过坐标的细微调整,最终结果归结为这行代码:

        .attr("x", function (d, i) { return xChart(d.MonthAbrev) + barWidth*((d.vendnm == 'CHARNECA')?0.2:1) })

希望这对您有所帮助,如果这不是您想要的,请告诉我。

var sales = [
    {
        "vendnm": "CHARNECA",
        "MonthAbrev": "Jan",
        "Vendas_Ano": 21611.950000000004,
        "Vendas_Ant": 16033.31
    },
    {
        "vendnm": "CHARNECA",
        "MonthAbrev": "Fev",
        "Vendas_Ano": 48108.08,
        "Vendas_Ant": 51142.299999999996
    },
    {
        "vendnm": "CHARNECA",
        "MonthAbrev": "Mar",
        "Vendas_Ano": 13427.280000000004,
        "Vendas_Ant": 21274.129999999997
    },
    {
        "vendnm": "CHARNECA",
        "MonthAbrev": "Apr",
        "Vendas_Ano": 28553.83,
        "Vendas_Ant": 15228.569999999998
    },
    {
        "vendnm": "CHARNECA",
        "MonthAbrev": "Mai",
        "Vendas_Ano": 19639.59,
        "Vendas_Ant": 10291.359999999999
    },
    {
        "vendnm": "CHARNECA",
        "MonthAbrev": "Jun",
        "Vendas_Ano": 22530.840000000007,
        "Vendas_Ant": 21905.29
    },
    {
        "vendnm": "CHARNECA",
        "MonthAbrev": "Jul",
        "Vendas_Ano": 20216.759999999995,
        "Vendas_Ant": 8276
    },
    {
        "vendnm": "CHARNECA",
        "MonthAbrev": "Ago",
        "Vendas_Ano": 39832.04,
        "Vendas_Ant": 49810.810000000005
    },
    {
        "vendnm": "CHARNECA",
        "MonthAbrev": "Set",
        "Vendas_Ano": 12861.52,
        "Vendas_Ant": 24425.32
    },
    {
        "vendnm": "CHARNECA",
        "MonthAbrev": "Out",
        "Vendas_Ano": 9227.34,
        "Vendas_Ant": 17804.65
    },
    {
        "vendnm": "CHARNECA",
        "MonthAbrev": "Nov",
        "Vendas_Ano": 0,
        "Vendas_Ant": 9993.34
    },
    {
        "vendnm": "CHARNECA",
        "MonthAbrev": "Dez",
        "Vendas_Ano": 0,
        "Vendas_Ant": 36568.7
    },
    {
        "vendnm": "JOÃO LUIS",
        "MonthAbrev": "Jan",
        "Vendas_Ano": 22681.14,
        "Vendas_Ant": 28587.71
    },
    {
        "vendnm": "JOÃO LUIS",
        "MonthAbrev": "Fev",
        "Vendas_Ano": 31382.11,
        "Vendas_Ant": 37637.420000000006
    },
    {
        "vendnm": "JOÃO LUIS",
        "MonthAbrev": "Mar",
        "Vendas_Ano": 32453.779999999995,
        "Vendas_Ant": 32993.12
    },
    {
        "vendnm": "JOÃO LUIS",
        "MonthAbrev": "Apr",
        "Vendas_Ano": 23445.4,
        "Vendas_Ant": 30835.75
    },
    {
        "vendnm": "JOÃO LUIS",
        "MonthAbrev": "Mai",
        "Vendas_Ano": 16471.71,
        "Vendas_Ant": 18028.07
    },
    {
        "vendnm": "JOÃO LUIS",
        "MonthAbrev": "Jun",
        "Vendas_Ano": 11617.470000000001,
        "Vendas_Ant": 22651.53
    },
    {
        "vendnm": "JOÃO LUIS",
        "MonthAbrev": "Jul",
        "Vendas_Ano": 24699.44,
        "Vendas_Ant": 34152.28999999999
    },
    {
        "vendnm": "JOÃO LUIS",
        "MonthAbrev": "Ago",
        "Vendas_Ano": 35476.94000000001,
        "Vendas_Ant": 22069.699999999997
    },
    {
        "vendnm": "JOÃO LUIS",
        "MonthAbrev": "Set",
        "Vendas_Ano": 37462.92,
        "Vendas_Ant": 23120.08
    },
    {
        "vendnm": "JOÃO LUIS",
        "MonthAbrev": "Out",
        "Vendas_Ano": 22042.140000000003,
        "Vendas_Ant": 28449.95
    },
    {
        "vendnm": "JOÃO LUIS",
        "MonthAbrev": "Nov",
        "Vendas_Ano": 0,
        "Vendas_Ant": 30755.050000000003
    },
    {
        "vendnm": "JOÃO LUIS",
        "MonthAbrev": "Dez",
        "Vendas_Ano": 0,
        "Vendas_Ant": 22838.64
    }
]
var charnecaData = [];
    var joaoData = [];

    for (var i = 0; i < sales.length; i++) {
        if (sales[i]["vendnm"] === "CHARNECA") {
            charnecaData.push(sales[i]);
        } else {
            joaoData.push(sales[i]);
        }
    }

    //functions for toggling between data
    function change(value) {
        if (value === 'lastYear') {
            update(charnecaData);
        } else if (value === 'currentYear') {
            update(joaoData);
        } else {
            update(sales);
        }
    }

    function update(data) {

        xChart.domain(data.map(function (d) { return d.MonthAbrev; }));

        yChart.domain([0, d3.max(data, function (d) { return +d.Vendas_Ant; })]);

        var barWidth = xChart.bandwidth()/2;

        var bars = chart.selectAll(".bar")
            .remove()
            .exit()
            .data(data)

        bars.enter()
            .append("rect")
            .attr("class", "bar")
            .attr("x", function (d, i) { return xChart(d.MonthAbrev) + barWidth*((d.vendnm == 'CHARNECA')?0.2:1) })
            .attr("y", function (d) { return yChart(d.Vendas_Ant); })
            .attr("height", function (d) { return height - yChart(d.Vendas_Ant); })
            .attr("width", barWidth - 5)
            .attr("fill", function (d) {
                if (d.vendnm === 'CHARNECA') {
                    return "rgb(251,180,174)";
                } else {
                    return "rgb(179,205,227)";
                }
            })
            .on("mouseover", function (a) {
                //console.log(a)
                chart.append("text")
                    .attr("class", "title-text")
                    .style("fill", "rgb(44, 160, 44)")
                    .text(a.vendnm + ' - ' +
                        a.MonthAbrev + ' - ' +
                        Number(Math.round(a.Vendas_Ant * 100) / 100).toLocaleString("es-ES", { minimumFractionDigits: 2 }) + '€'
                    )
                    .attr("text-anchor", "middle")
                    .attr("x", 400)
                    .attr("y", 50);
            })
            .on("mouseout", function (a) {
                chart.select(".title-text").remove();
            })

        //left axis
        chart.select('.y')
            .call(yAxis)
        //bottom axis
        chart.select('.xAxis')
            .attr("transform", "translate(0," + height + ")")
            .call(xAxis)
            .selectAll("text")
            .style("text-anchor", "end")
            .attr("dx", "-.8em")
            .attr("dy", ".15em")
            .attr("transform", function (d) {
                return "rotate(-65)";
            });

    }//end update

    var margin = { top: 20, right: 20, bottom: 30, left: 50 };
    var width = 600;
    var height = 300;

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

    var xChart = d3.scaleBand()
        .range([0, width]);

    var yChart = d3.scaleLinear()
        .range([height, 0]);

    var xAxis = d3.axisBottom(xChart);
    var yAxis = d3.axisLeft(yChart);

    chart.append("g")
        .attr("class", "y axis")
        .call(yAxis)

    chart.append("g")
        .attr("class", "xAxis")
        .attr("transform", "translate(0," + height + ")")
        .call(xAxis)
        .selectAll("text")
        .style("text-anchor", "end")
        .attr("dx", "-.8em")
        .attr("dy", ".15em")
        .attr("transform", function (d) {
            return "rotate(-65)";
        });

    update(sales);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
<head>
    <script src="https://d3js.org/d3.v4.min.js"></script>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
</head>

<style>
    .radio {
        text-align: center;
    }

    .row {
        text-align: center;
    }
</style>

<body>

    <div class='container'>
        <div class='row'>
            <div class='radio'>
                <label class='radio-inline'>
                    <input type="radio" name="gender" value="lastYear" onclick='change(this.value)'> Last Year
                </label>
                <label class='radio-inline'>
                    <input type="radio" name="gender" value="currentYear" onclick='change(this.value)'> Current Year
                </label>
                <label class='radio-inline'>
                    <input type="radio" name="gender" value="both" onclick='change(this.value)' checked> Both
                </label>
            </div>
            <svg class='chart'>
            </svg>
        </div>
    </div>
</body>

关于javascript - 为什么我的图表 D3.js 上的条形图没有以正确的顺序出现?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59311057/

相关文章:

javascript - 如何更新 Lollipop 图表的填充和排序值?

javascript - d3 强制定向图不选择文本

javascript - nvd3 应用程序内存泄漏

javascript - d3.js 文本标签中的 html

javascript - Angular Http 订阅 - 无法分配给组件中的变量

javascript - 将值设置为 &lt;input type ="number"/> 中的货币

javascript - 多个谷歌图表 API,在同一网页上

javascript - 使用 d3 js 库的 data 方法将事件绑定(bind)到动态生成的内容

javascript - RESTful 客户端中的 Ajax 请求

javascript - 对 Json 进行排序并从当前位置获取上下数据