d3 v4, почему на оси X есть повторяющиеся отметки (как работать с tickValues)?

1

Вот мое сокращение:

var CreateChart = (function() {
    var chtChartConstrictor = document.getElementById("chtChartConstrictor");
    var myChart;

    var drawChart = function(options) {
        // Set dimensions and margins of the graph
        var margin = {
            top: 20,
            right: 0,
            bottom: 0,
            left: 0,
        };
        var chtChartConstrictorMetrics = chtChartConstrictor.getBoundingClientRect();
        var width = chtChartConstrictorMetrics.width;
        var height = chtChartConstrictorMetrics.height;

        var parseTime = d3.timeParse("%Y-%m-%d");
        var formatTime = d3.timeFormat("%Y-%m-%d");
        // format the data
        options.line1.forEach(function(d) {
            d.date = parseTime(d.date);
            d.close = +d.close;
        });
        // parse the date / time

        if (options.chartType === "line") {
            
            // set the ranges
            let x = d3.scaleTime().range([0, width]).domain(
                d3.extent(options.line1, function(d) {
                    return d.date;
                })
            );
            let y = d3.scaleLinear().range([height - margin.top - margin.bottom, 0]).domain([
                0,
                d3.max(options.line1, function(d) {
                    return d.close;
                }),
            ]);

            // define the area
            var area = d3
                .area()
                .x(function(d) {
                    
                    return x(d.date);
                })
                .y0(height)
                .y1(function(d) {
                    
                    return y(d.close);
                });
            // define the line
            var valueline = d3
                .line()
                .x(function(d) {
                    
                    return x(d.date);
                })
                .y(function(d) {
                    return y(d.close);
                });

            var svg = d3
                .select("#chtChartConstrictor")
                .append("svg")
                .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("path").attr("class", "cht-LineChart_Line").attr("d", valueline(options.line1));
            // add the area
            svg.append("path").data(options.line1).attr("class", "cht-LineChart_Fill").attr("d", area(options.line1));

            // add the X Axis
            svg.append("g").attr("class", "cht-LineChart_XAxis").attr("transform", "translate(0," + (height - 40) + ")").call(customXAxis);
            function customXAxis(g) {
                g.call(d3.axisBottom(x).tickFormat(d3.timeFormat("%Y-%m-%d")));
                g.select(".domain").remove();
                g.selectAll("line").remove();
                g.selectAll("text").attr("class", "cht-LineChart_XAxisNumber");
            }
            // add the Y Axis
            svg.append("g").attr("class", "cht-LineChart_YAxis").attr("transform", "translate(-5,0)").call(customYAxis);
            function customYAxis(g) {
                g.call(d3.axisRight(y));
                g.select(".domain").remove();
                g.selectAll("line").remove();
                g.select(".cht-LineChart_YAxis text:first-of-type").remove();
                g.selectAll("text").attr("class", "cht-LineChart_YAxisNumber");
            }

            // Gridline
            var gridlines = d3.axisLeft().tickFormat("").tickSize(-width).scale(y);

            svg.append("g").attr("class", "cht-LineChart_GridLineContainer").call(gridlines);
            svg.selectAll(".cht-LineChart_GridLineContainer line").attr("class", "cht-LineChart_GridLine");
            // Tidy up the generation by removing the text and path as we don't use them
            svg.selectAll(".cht-LineChart_GridLineContainer text").remove();
            svg.selectAll(".cht-LineChart_GridLineContainer path").remove();
        }
    };

    var destroyChart = function() {};

    var publicAPI = {
        DrawChart: drawChart,
        DestroyChart: destroyChart,
    };
    return publicAPI;
})();

CreateChart.DrawChart({
    chartType: "line",
    line1: [
        { date: "2016-12-25", close: 43 },
        { date: "2016-12-26", close: 57 },
        { date: "2016-12-27", close: 55 },
        { date: "2016-12-28", close: 44 },
        { date: "2016-12-29", close: 45 },
        { date: "2016-12-30", close: 30 },
        { date: "2016-12-31", close: 36 },
    ],
    line2: [],
});
#chtChartConstrictor {
	height: 320px;
	width: 100%;
}

.cht-LineChart_Line {
	fill: transparent;
	stroke: #f90;
   stroke-width: 2px;
}

.cht-LineChart_Fill {
	fill: rgba(73,255,255,.5);
}

.cht-LineChart_GridLine {
    stroke-dasharray: 4px 4px;
    stroke: #ddd;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>
<div id="chtChartConstrictor"></div>

Я не могу понять, почему я получаю несколько повторяющихся тиков (дат) на оси X? Может ли кто-нибудь отправить меня на правильный путь? Я бы хотел отметить тик для каждого из точек данных и ничего больше.

Теги:
d3.js

1 ответ

1
Лучший ответ

В D3 ось, построенная с использованием шкалы времени, автоматически генерирует свои тики. То есть генератор оси решает, сколько тиков он будет создавать: они не соответствуют точкам данных.

При этом тики, которые вы видите, не дублируются: они фактически соответствуют разным моментам времени... однако, поскольку некоторые из них принадлежат к одному дню, эти тики, похоже, дублируются. Но это не так.

Есть несколько способов исправить это. Один из самых простых - использовать tickValues (как вы предлагаете в заголовке вопроса), чтобы передавать только даты в вашем массиве данных:

.tickValues(options.line1.map(function(d){
    return d.date
}))

Вот ваш код с этим изменением:

var CreateChart = (function() {
    var chtChartConstrictor = document.getElementById("chtChartConstrictor");
    var myChart;

    var drawChart = function(options) {
        // Set dimensions and margins of the graph
        var margin = {
            top: 20,
            right: 0,
            bottom: 0,
            left: 0,
        };
        var chtChartConstrictorMetrics = chtChartConstrictor.getBoundingClientRect();
        var width = chtChartConstrictorMetrics.width;
        var height = chtChartConstrictorMetrics.height;

        var parseTime = d3.timeParse("%Y-%m-%d");
        var formatTime = d3.timeFormat("%Y-%m-%d");
        // format the data
        options.line1.forEach(function(d) {
            d.date = parseTime(d.date);
            d.close = +d.close;
        });
        // parse the date / time

        if (options.chartType === "line") {
            
            // set the ranges
            let x = d3.scaleTime().range([0, width]).domain(
                d3.extent(options.line1, function(d) {
                    return d.date;
                })
            );
            let y = d3.scaleLinear().range([height - margin.top - margin.bottom, 0]).domain([
                0,
                d3.max(options.line1, function(d) {
                    return d.close;
                }),
            ]);

            // define the area
            var area = d3
                .area()
                .x(function(d) {
                    
                    return x(d.date);
                })
                .y0(height)
                .y1(function(d) {
                    
                    return y(d.close);
                });
            // define the line
            var valueline = d3
                .line()
                .x(function(d) {
                    
                    return x(d.date);
                })
                .y(function(d) {
                    return y(d.close);
                });

            var svg = d3
                .select("#chtChartConstrictor")
                .append("svg")
                .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("path").attr("class", "cht-LineChart_Line").attr("d", valueline(options.line1));
            // add the area
            svg.append("path").data(options.line1).attr("class", "cht-LineChart_Fill").attr("d", area(options.line1));

            // add the X Axis
            svg.append("g").attr("class", "cht-LineChart_XAxis").attr("transform", "translate(0," + (height - 40) + ")").call(customXAxis);
            function customXAxis(g) {
                g.call(d3.axisBottom(x)
                .tickValues(options.line1.map(function(d){return d.date}))
                .tickFormat(d3.timeFormat("%Y-%m-%d")));
                g.select(".domain").remove();
                g.selectAll("line").remove();
                g.selectAll("text").attr("class", "cht-LineChart_XAxisNumber");
            }
            // add the Y Axis
            svg.append("g").attr("class", "cht-LineChart_YAxis").attr("transform", "translate(-5,0)").call(customYAxis);
            function customYAxis(g) {
                g.call(d3.axisRight(y));
                g.select(".domain").remove();
                g.selectAll("line").remove();
                g.select(".cht-LineChart_YAxis text:first-of-type").remove();
                g.selectAll("text").attr("class", "cht-LineChart_YAxisNumber");
            }

            // Gridline
            var gridlines = d3.axisLeft().tickFormat("").tickSize(-width).scale(y);

            svg.append("g").attr("class", "cht-LineChart_GridLineContainer").call(gridlines);
            svg.selectAll(".cht-LineChart_GridLineContainer line").attr("class", "cht-LineChart_GridLine");
            // Tidy up the generation by removing the text and path as we don't use them
            svg.selectAll(".cht-LineChart_GridLineContainer text").remove();
            svg.selectAll(".cht-LineChart_GridLineContainer path").remove();
        }
    };

    var destroyChart = function() {};

    var publicAPI = {
        DrawChart: drawChart,
        DestroyChart: destroyChart,
    };
    return publicAPI;
})();

CreateChart.DrawChart({
    chartType: "line",
    line1: [
        { date: "2016-12-25", close: 43 },
        { date: "2016-12-26", close: 57 },
        { date: "2016-12-27", close: 55 },
        { date: "2016-12-28", close: 44 },
        { date: "2016-12-29", close: 45 },
        { date: "2016-12-30", close: 30 },
        { date: "2016-12-31", close: 36 },
    ],
    line2: [],
});
#chtChartConstrictor {
	height: 320px;
	width: 100%;
}

.cht-LineChart_Line {
	fill: transparent;
	stroke: #f90;
   stroke-width: 2px;
}

.cht-LineChart_Fill {
	fill: rgba(73,255,255,.5);
}

.cht-LineChart_GridLine {
    stroke-dasharray: 4px 4px;
    stroke: #ddd;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>
<div id="chtChartConstrictor"></div>

Следует иметь в виду, что это решение имеет свои проблемы: если ваши даты не равномерно распределены (например, [01-Dec, 02-Dec, 03-Dec, 06-Dec, 07-Dec]), тики не будут равномерно распределены, что визуально неприятно.

  • 0
    Спасибо Герадо. Это имеет смысл. :)

Ещё вопросы

Сообщество Overcoder
Наверх
Меню