From: Jukka Kurkela Date: Sun, 14 Mar 2021 15:27:57 +0000 (+0200) Subject: Add option to turn off grouping of bar datasets (#8641) X-Git-Tag: v3.0.0-beta.14~9 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=85123ac0748d92a1637ee97a37e8172fa85d547f;p=thirdparty%2FChart.js.git Add option to turn off grouping of bar datasets (#8641) * Add option to turn off grouping of bar datasets * Disregard time offset --- diff --git a/docs/docs/charts/bar.mdx b/docs/docs/charts/bar.mdx index 555fe38e5..d3123a21f 100644 --- a/docs/docs/charts/bar.mdx +++ b/docs/docs/charts/bar.mdx @@ -173,6 +173,7 @@ The bar chart accepts the following configuration from the associated dataset op | `categoryPercentage` | `number` | `0.8` | Percent (0-1) of the available width each category should be within the sample width. [more...](#barpercentage-vs-categorypercentage) | `barThickness` | `number`\|`string` | | Manually set width of each bar in pixels. If set to `'flex'`, it computes "optimal" sample widths that globally arrange bars side by side. If not set (default), bars are equally sized based on the smallest interval. [more...](#barthickness) | `base` | `number` | | Base value for the bar in data units along the value axis. If not set, defaults to the value axis base value. +| `grouped` | `boolean` | `true` | Should the bars be grouped on index axis. When `true`, all the datasets at same index value will be placed next to each other centering on that index value. When `false`, each bar is placed on its actual index-axis value. | `maxBarThickness` | `number` | | Set this to ensure that bars are not sized thicker than this. | `minBarLength` | `number` | | Set this to ensure that bars have a minimum length in pixels. diff --git a/src/controllers/controller.bar.js b/src/controllers/controller.bar.js index 54be96a96..0d4176f27 100644 --- a/src/controllers/controller.bar.js +++ b/src/controllers/controller.bar.js @@ -255,9 +255,9 @@ export default class BarController extends DatasetController { updateElements(bars, start, count, mode) { const me = this; const reset = mode === 'reset'; - const vscale = me._cachedMeta.vScale; - const base = vscale.getBasePixel(); - const horizontal = vscale.isHorizontal(); + const vScale = me._cachedMeta.vScale; + const base = vScale.getBasePixel(); + const horizontal = vScale.isHorizontal(); const ruler = me._getRuler(); const firstOpts = me.resolveDataElementOptions(start, mode); const sharedOptions = me.getSharedOptions(firstOpts); @@ -266,14 +266,14 @@ export default class BarController extends DatasetController { me.updateSharedOptions(sharedOptions, mode, firstOpts); for (let i = start; i < start + count; i++) { - const vpixels = me._calculateBarValuePixels(i); + const vpixels = reset ? {base, head: base} : me._calculateBarValuePixels(i); const ipixels = me._calculateBarIndexPixels(i, ruler); const properties = { horizontal, - base: reset ? base : vpixels.base, - x: horizontal ? reset ? base : vpixels.head : ipixels.center, - y: horizontal ? ipixels.center : reset ? base : vpixels.head, + base: vpixels.base, + x: horizontal ? vpixels.head : ipixels.center, + y: horizontal ? ipixels.center : vpixels.head, height: horizontal ? ipixels.size : undefined, width: horizontal ? undefined : ipixels.size }; @@ -370,6 +370,7 @@ export default class BarController extends DatasetController { */ _getRuler() { const me = this; + const opts = me.options; const meta = me._cachedMeta; const iScale = meta.iScale; const pixels = []; @@ -379,11 +380,8 @@ export default class BarController extends DatasetController { pixels.push(iScale.getPixelForValue(me.getParsed(i)[iScale.axis], i)); } - // Note: a potential optimization would be to skip computing this - // only if the barThickness option is defined - // Since a scriptable option may return null or undefined that - // means the option would have to be of type number - const min = computeMinSampleSize(iScale); + const barThickness = opts.barThickness; + const min = barThickness || computeMinSampleSize(iScale); return { min, @@ -391,7 +389,10 @@ export default class BarController extends DatasetController { start: iScale._startPixel, end: iScale._endPixel, stackCount: me._getStackCount(), - scale: iScale + scale: iScale, + grouped: opts.grouped, + // bar thickness ratio used for non-grouped bars + ratio: barThickness ? 1 : opts.categoryPercentage * opts.barPercentage }; } @@ -459,17 +460,24 @@ export default class BarController extends DatasetController { */ _calculateBarIndexPixels(index, ruler) { const me = this; + const scale = ruler.scale; const options = me.options; - const stackCount = options.skipNull ? me._getStackCount(index) : ruler.stackCount; - const range = options.barThickness === 'flex' - ? computeFlexCategoryTraits(index, ruler, options, stackCount) - : computeFitCategoryTraits(index, ruler, options, stackCount); - - const stackIndex = me._getStackIndex(me.index, me._cachedMeta.stack); - const center = range.start + (range.chunk * stackIndex) + (range.chunk / 2); - const size = Math.min( - valueOrDefault(options.maxBarThickness, Infinity), - range.chunk * range.ratio); + const maxBarThickness = valueOrDefault(options.maxBarThickness, Infinity); + let center, size; + if (ruler.grouped) { + const stackCount = options.skipNull ? me._getStackCount(index) : ruler.stackCount; + const range = options.barThickness === 'flex' + ? computeFlexCategoryTraits(index, ruler, options, stackCount) + : computeFitCategoryTraits(index, ruler, options, stackCount); + + const stackIndex = me._getStackIndex(me.index, me._cachedMeta.stack); + center = range.start + (range.chunk * stackIndex) + (range.chunk / 2); + size = Math.min(maxBarThickness, range.chunk * range.ratio); + } else { + // For non-grouped bar charts, exact pixel values are used + center = scale.getPixelForValue(me.getParsed(index)[scale.axis], index); + size = Math.min(maxBarThickness, ruler.min * ruler.ratio); + } return { base: center - size / 2, @@ -512,6 +520,7 @@ BarController.defaults = { categoryPercentage: 0.8, barPercentage: 0.9, + grouped: true, animations: { numbers: { diff --git a/test/fixtures/controller.bar/not-grouped/on-time.js b/test/fixtures/controller.bar/not-grouped/on-time.js new file mode 100644 index 000000000..6c2d44cbb --- /dev/null +++ b/test/fixtures/controller.bar/not-grouped/on-time.js @@ -0,0 +1,134 @@ +const data1 = [ + { + x: '2017-11-02T20:30:00', + y: 27 + }, + { + x: '2017-11-03T20:53:00', + y: 30 + }, + { + x: '2017-11-06T05:46:00', + y: 19 + }, + { + x: '2017-11-06T21:03:00', + y: 28 + }, + { + x: '2017-11-07T20:49:00', + y: 29 + }, + { + x: '2017-11-08T21:52:00', + y: 33 + } +]; + +const data2 = [ + { + x: '2017-11-03T13:07:00', + y: 45 + }, + { + x: '2017-11-04T04:50:00', + y: 40 + }, + { + x: '2017-11-06T12:48:00', + y: 38 + }, + { + x: '2017-11-07T12:28:00', + y: 42 + }, + { + x: '2017-11-08T12:45:00', + y: 51 + }, + { + x: '2017-11-09T05:23:00', + y: 57 + } +]; + +const data3 = [ + { + x: '2017-11-03T16:30:00', + y: 32 + }, + { + x: '2017-11-04T11:50:00', + y: 34 + }, + { + x: '2017-11-06T18:30:00', + y: 28 + }, + { + x: '2017-11-07T15:51:00', + y: 31 + }, + { + x: '2017-11-08T17:27:00', + y: 36 + }, + { + x: '2017-11-09T06:53:00', + y: 31 + } +]; + +module.exports = { + description: 'https://github.com/chartjs/Chart.js/issues/5139', + config: { + type: 'bar', + data: { + datasets: [ + { + data: data1, + backgroundColor: 'rgb(0,0,255)', + }, + { + data: data2, + backgroundColor: 'rgb(255,0,0)', + }, + { + data: data3, + backgroundColor: 'rgb(0,255,0)', + }, + ] + }, + options: { + barThickness: 10, + grouped: false, + scales: { + x: { + bounds: 'ticks', + type: 'time', + offset: false, + position: 'bottom', + display: true, + time: { + isoWeekday: true, + unit: 'day' + }, + grid: { + offset: false + } + }, + y: { + beginAtZero: true, + display: false + } + }, + } + }, + options: { + spriteText: true, + canvas: { + width: 1000, + height: 300 + } + } +}; diff --git a/test/fixtures/controller.bar/not-grouped/on-time.png b/test/fixtures/controller.bar/not-grouped/on-time.png new file mode 100644 index 000000000..92506bd3a Binary files /dev/null and b/test/fixtures/controller.bar/not-grouped/on-time.png differ