微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

Chartjs 分组条形图重叠

如何解决Chartjs 分组条形图重叠

我使用的是围绕 chart.js 2 构建的 react-chartjs-2。 Chartjs 已经推出了版本 3,其中有很大的变化,但是 react-chartjs 还没有为 chartjs-3 开发包装器。

我想跳过分组条形图中的空值,否则会留下空白。一个选项被添加到 chartjs-3 以跳过空值,但在 chartjs-2 中没有。使用 chartjs-2 时,绘图如下所示,空值有间隙。

enter image description here

所以我使用这个提交更改#7849并对chartjs-2.9.2进行相同的更改。条宽已增加以填充空值区域,但它向右移动并重叠,如下所示。为什么它的行为像这样?任何帮助,将不胜感激。

enter image description here

修改contoller.bar.js代码如下

"use strict";

var DatasetController = require("../core/core.datasetController");
var defaults = require("../core/core.defaults");
var elements = require("../elements/index");
var helpers = require("../helpers/index");

var deprecated = helpers._deprecated;
var valueOrDefault = helpers.valueOrDefault;

defaults._set("bar",{
    hover: {
        mode: "label",},scales: {
        xAxes: [
            {
                type: "category",offset: true,gridLines: {
                    offsetGridLines: true,],yAxes: [
            {
                type: "linear",});

defaults._set("global",{
    datasets: {
        bar: {
            categoryPercentage: 0.8,barPercentage: 0.9,});

/**
 * Computes the "optimal" sample size to maintain bars equally sized while preventing overlap.
 * @private
 */
function computeMinSampleSize(scale,pixels) {
    var min = scale._length;
    var prev,curr,i,ilen;

    for (i = 1,ilen = pixels.length; i < ilen; ++i) {
        min = Math.min(min,Math.abs(pixels[i] - pixels[i - 1]));
    }

    for (i = 0,ilen = scale.getTicks().length; i < ilen; ++i) {
        curr = scale.getPixelForTick(i);
        min = i > 0 ? Math.min(min,Math.abs(curr - prev)) : min;
        prev = curr;
    }

    return min;
}

/**
 * Computes an "ideal" category based on the absolute bar thickness or,if undefined or null,* uses the smallest interval (see computeMinSampleSize) that prevents bar overlapping. This
 * mode currently always generates bars equally sized (until we introduce scriptable options?).
 * @private
 */
function computeFitCategoryTraits(index,ruler,options,stackCount) {
    var thickness = options.barThickness;
    var curr = ruler.pixels[index];
    var min = helpers.isNullOrUndef(thickness)
        ? computeMinSampleSize(ruler.scale,ruler.pixels)
        : -1;
    var size,ratio;

    if (helpers.isNullOrUndef(thickness)) {
        size = min * options.categoryPercentage;
        ratio = options.barPercentage;
    } else {
        // When bar thickness is enforced,category and bar percentages are ignored.
        // Note(SB): we Could add support for relative bar thickness (e.g. barThickness: '50%')
        // and deprecate barPercentage since this value is ignored when thickness is absolute.
        size = thickness * stackCount;
        ratio = 1;
    }

    return {
        chunk: size / stackCount,ratio: ratio,start: curr - size / 2,};
}

/**
 * Computes an "optimal" category that globally arranges bars side by side (no gap when
 * percentage options are 1),based on the prevIoUs and following categories. This mode
 * generates bars with different widths when data are not evenly spaced.
 * @private
 */
function computeFlexCategoryTraits(index,stackCount) {
    var pixels = ruler.pixels;
    var curr = pixels[index];
    var prev = index > 0 ? pixels[index - 1] : null;
    var next = index < pixels.length - 1 ? pixels[index + 1] : null;
    var percent = options.categoryPercentage;
    var start,size;

    if (prev === null) {
        // first data: its size is double based on the next point or,// if it's also the last data,we use the scale size.
        prev = curr - (next === null ? ruler.end - ruler.start : next - curr);
    }

    if (next === null) {
        // last data: its size is also double based on the prevIoUs point.
        next = curr + curr - prev;
    }

    start = curr - ((curr - Math.min(prev,next)) / 2) * percent;
    size = (Math.abs(next - prev) / 2) * percent;

    return {
        chunk: size / stackCount,ratio: options.barPercentage,start: start,};
}

module.exports = DatasetController.extend({
    dataElementType: elements.Rectangle,/**
     * @private
     */
    _dataElementOptions: [
        "backgroundColor","borderColor","borderSkipped","borderWidth","barPercentage","barThickness","categoryPercentage","maxBarThickness","minBarLength",initialize: function () {
        var me = this;
        var Meta,scaleOpts;

        DatasetController.prototype.initialize.apply(me,arguments);

        Meta = me.getMeta();
        Meta.stack = me.getDataset().stack;
        Meta.bar = true;

        scaleOpts = me._getIndexScale().options;
        deprecated(
            "bar chart",scaleOpts.barPercentage,"scales.[x/y]Axes.barPercentage","dataset.barPercentage"
        );
        deprecated(
            "bar chart",scaleOpts.barThickness,"scales.[x/y]Axes.barThickness","dataset.barThickness"
        );
        deprecated(
            "bar chart",scaleOpts.categoryPercentage,"scales.[x/y]Axes.categoryPercentage","dataset.categoryPercentage"
        );
        deprecated(
            "bar chart",me._getValueScale().options.minBarLength,"scales.[x/y]Axes.minBarLength","dataset.minBarLength"
        );
        deprecated(
            "bar chart",scaleOpts.maxBarThickness,"scales.[x/y]Axes.maxBarThickness","dataset.maxBarThickness"
        );
    },update: function (reset) {
        var me = this;
        var rects = me.getMeta().data;
        var i,ilen;

        me._ruler = me.getRuler();

        for (i = 0,ilen = rects.length; i < ilen; ++i) {
            me.updateElement(rects[i],reset);
        }
    },updateElement: function (rectangle,index,reset) {
        var me = this;
        var Meta = me.getMeta();
        var dataset = me.getDataset();
        var options = me._resolveDataElementOptions(rectangle,index);

        rectangle._xScale = me.getScaleForId(Meta.xAxisID);
        rectangle._yScale = me.getScaleForId(Meta.yAxisID);
        rectangle._datasetIndex = me.index;
        rectangle._index = index;
        rectangle._model = {
            backgroundColor: options.backgroundColor,borderColor: options.borderColor,borderSkipped: options.borderSkipped,borderWidth: options.borderWidth,datasetLabel: dataset.label,label: me.chart.data.labels[index],};

        if (helpers.isArray(dataset.data[index])) {
            rectangle._model.borderSkipped = null;
        }

        me._updateElementGeometry(rectangle,reset,options);

        rectangle.pivot();
    },/**
     * @private
     */
    _updateElementGeometry: function (rectangle,options) {
        var me = this;
        var model = rectangle._model;
        var vscale = me._getValueScale();
        var base = vscale.getBasePixel();
        var horizontal = vscale.isHorizontal();
        var ruler = me._ruler || me.getRuler();
        var vpixels = me.calculateBarValuePixels(me.index,options);
        var ipixels = me.calculateBarIndexPixels(
            me.index,options
        );

        model.horizontal = horizontal;
        model.base = reset ? base : vpixels.base;
        model.x = horizontal ? (reset ? base : vpixels.head) : ipixels.center;
        model.y = horizontal ? ipixels.center : reset ? base : vpixels.head;
        model.height = horizontal ? ipixels.size : undefined;
        model.width = horizontal ? undefined : ipixels.size;
    },/**
     * Returns the stacks based on groups and bar visibility.
     * @param {number} [last] - The dataset index
     * @returns {string[]} The list of stack IDs
     * @private
     */
    _getStacks: function (last,dataIndex) {
        var me = this;
        var scale = me._getIndexScale();
        var Metasets = scale._getMatchingVisibleMetas(me._type);
        var stacked = scale.options.stacked;
        var ilen = Metasets.length;
        var stacks = [];
        var i,Meta;

        for (i = 0; i < ilen; ++i) {
            Meta = Metasets[i];
            if (
                typeof dataIndex !== "undefined" &&
                helpers.isNullOrUndef(
                    me.chart.data.datasets[Meta.index].data[dataIndex]
                )
            ) {
                continue;
            }
            // stacked   | Meta.stack
            //           | found | not found | undefined
            // false     |   x   |     x     |     x
            // true      |       |     x     |
            // undefined |       |     x     |     x
            if (
                stacked === false ||
                stacks.indexOf(Meta.stack) === -1 ||
                (stacked === undefined && Meta.stack === undefined)
            ) {
                stacks.push(Meta.stack);
            }
            if (Meta.index === last) {
                break;
            }
        }
        return stacks;
    },/**
     * Returns the effective number of stacks based on groups and bar visibility.
     * @private
     */
    getStackCount: function (index) {
        return this._getStacks(undefined,index).length;
    },/**
     * Returns the stack index for the given dataset based on groups and bar visibility.
     * @param {number} [datasetIndex] - The dataset index
     * @param {string} [name] - The stack name to find
     * @returns {number} The stack index
     * @private
     */
    getStackIndex: function (datasetIndex,name) {
        var stacks = this._getStacks(datasetIndex);
        var index = name !== undefined ? stacks.indexOf(name) : -1; // indexOf returns -1 if element is not present

        return index === -1 ? stacks.length - 1 : index;
    },/**
     * @private
     */
    getRuler: function () {
        var me = this;
        var scale = me._getIndexScale();
        var pixels = [];
        var i,ilen;

        for (i = 0,ilen = me.getMeta().data.length; i < ilen; ++i) {
            pixels.push(scale.getPixelForValue(null,me.index));
        }

        return {
            pixels: pixels,start: scale._startPixel,end: scale._endPixel,stackCount: me.getStackCount(),scale: scale,};
    },/**
     * Note: pixel values are not clamped to the scale area.
     * @private
     */
    calculateBarValuePixels: function (datasetIndex,options) {
        var me = this;
        var chart = me.chart;
        var scale = me._getValueScale();
        var isHorizontal = scale.isHorizontal();
        var datasets = chart.data.datasets;
        var Metasets = scale._getMatchingVisibleMetas(me._type);
        var value = scale._parseValue(datasets[datasetIndex].data[index]);
        var minBarLength = options.minBarLength;
        var stacked = scale.options.stacked;
        var stack = me.getMeta().stack;
        var start =
            value.start === undefined
                ? 0
                : value.max >= 0 && value.min >= 0
                ? value.min
                : value.max;
        var length =
            value.start === undefined
                ? value.end
                : value.max >= 0 && value.min >= 0
                ? value.max - value.min
                : value.min - value.max;
        var ilen = Metasets.length;
        var i,iMeta,ivalue,base,head,size,stackLength;

        if (stacked || (stacked === undefined && stack !== undefined)) {
            for (i = 0; i < ilen; ++i) {
                iMeta = Metasets[i];

                if (iMeta.index === datasetIndex) {
                    break;
                }

                if (iMeta.stack === stack) {
                    stackLength = scale._parseValue(
                        datasets[iMeta.index].data[index]
                    );
                    ivalue =
                        stackLength.start === undefined
                            ? stackLength.end
                            : stackLength.min >= 0 && stackLength.max >= 0
                            ? stackLength.max
                            : stackLength.min;

                    if (
                        (value.min < 0 && ivalue < 0) ||
                        (value.max >= 0 && ivalue > 0)
                    ) {
                        start += ivalue;
                    }
                }
            }
        }

        base = scale.getPixelForValue(start);
        head = scale.getPixelForValue(start + length);
        size = head - base;

        if (minBarLength !== undefined && Math.abs(size) < minBarLength) {
            size = minBarLength;
            if (
                (length >= 0 && !isHorizontal) ||
                (length < 0 && isHorizontal)
            ) {
                head = base - minBarLength;
            } else {
                head = base + minBarLength;
            }
        }

        return {
            size: size,base: base,head: head,center: head + size / 2,/**
     * @private
     */
    calculateBarIndexPixels: function (datasetIndex,options) {
        var me = this;
        const stackCount = true ? me.getStackCount(index) : ruler.stackCount;

        var range =
            options.barThickness === "flex"
                ? computeFlexCategoryTraits(index,stackCount)
                : computeFitCategoryTraits(index,stackCount);

        var stackIndex = me.getStackIndex(datasetIndex,me.getMeta().stack);

        var center = range.start + range.chunk * stackIndex + range.chunk / 2;
        console.log(datasetIndex,stackIndex,center,range);
        var size = Math.min(
            valueOrDefault(options.maxBarThickness,Infinity),range.chunk * range.ratio
        );

        return {
            base: center - size / 2,head: center + size / 2,center: center,size: size,draw: function () {
        var me = this;
        var chart = me.chart;
        var scale = me._getValueScale();
        var rects = me.getMeta().data;
        var dataset = me.getDataset();
        var ilen = rects.length;
        var i = 0;

        helpers.canvas.clipArea(chart.ctx,chart.chartArea);

        for (; i < ilen; ++i) {
            var val = scale._parseValue(dataset.data[i]);
            if (!isNaN(val.min) && !isNaN(val.max)) {
                rects[i].draw();
            }
        }

        helpers.canvas.unclipArea(chart.ctx);
    },/**
     * @private
     */
    _resolveDataElementOptions: function () {
        var me = this;
        var values = helpers.extend(
            {},DatasetController.prototype._resolveDataElementOptions.apply(
                me,arguments
            )
        );
        var indexOpts = me._getIndexScale().options;
        var valueOpts = me._getValueScale().options;

        values.barPercentage = valueOrDefault(
            indexOpts.barPercentage,values.barPercentage
        );
        values.barThickness = valueOrDefault(
            indexOpts.barThickness,values.barThickness
        );
        values.categoryPercentage = valueOrDefault(
            indexOpts.categoryPercentage,values.categoryPercentage
        );
        values.maxBarThickness = valueOrDefault(
            indexOpts.maxBarThickness,values.maxBarThickness
        );
        values.minBarLength = valueOrDefault(
            valueOpts.minBarLength,values.minBarLength
        );

        return values;
    },});

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。