如何解决Chartjs 分组条形图重叠
我使用的是围绕 chart.js 2 构建的 react-chartjs-2。 Chartjs 已经推出了版本 3,其中有很大的变化,但是 react-chartjs 还没有为 chartjs-3 开发包装器。
我想跳过分组条形图中的空值,否则会留下空白。一个选项被添加到 chartjs-3 以跳过空值,但在 chartjs-2 中没有。使用 chartjs-2 时,绘图如下所示,空值有间隙。
所以我使用这个提交更改#7849并对chartjs-2.9.2进行相同的更改。条宽已增加以填充空值区域,但它向右移动并重叠,如下所示。为什么它的行为像这样?任何帮助,将不胜感激。
"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 举报,一经查实,本站将立刻删除。