From: Evert Timberg Date: Mon, 12 Oct 2020 14:22:13 +0000 (-0400) Subject: Cartesian axis label cross alignment (#7874) X-Git-Tag: v3.0.0-beta.4~12 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=29f13583288cd08bc4f36789c76143e750895245;p=thirdparty%2FChart.js.git Cartesian axis label cross alignment (#7874) * Refactor Y axis alignment to it's own method * Label Cross alignment * Add documentation of new setting * Update docs with live sample --- diff --git a/docs/docs/axes/cartesian/index.md b/docs/docs/axes/cartesian/index.mdx similarity index 71% rename from docs/docs/axes/cartesian/index.md rename to docs/docs/axes/cartesian/index.mdx index 2fa707cc9..2e7cf2280 100644 --- a/docs/docs/axes/cartesian/index.md +++ b/docs/docs/axes/cartesian/index.mdx @@ -2,6 +2,8 @@ title: Cartesian Axes --- +import { useEffect } from 'react'; + Axes that follow a cartesian grid are known as 'Cartesian Axes'. Cartesian axes are used for line, bar, and bubble charts. Four cartesian axes are included in Chart.js by default. * [linear](./linear.md#linear-cartesian-axis) @@ -47,6 +49,8 @@ The following options are common to all cartesian axes but do not apply to other | Name | Type | Default | Description | ---- | ---- | ------- | ----------- +| `alignment` | `string` | | `'center'` | The tick alignment along the axis. Can be `'start'`, `'center'`, or `'end'`. +| `crossAlignment` | `string` | | `'near'` | The tick alignment perpendicular to the axis. Can be `'near'`, `'center'`, or `'far'`. See [Tick Alignment](#tick-alignment) | `sampleSize` | `number` | `ticks.length` | The number of ticks to examine when deciding how many labels will fit. Setting a smaller value will be faster, but may be less accurate when there is large variability in label length. | `autoSkip` | `boolean` | `true` | If true, automatically calculates how many labels can be shown and hides labels accordingly. Labels will be rotated up to `maxRotation` before skipping any. Turn `autoSkip` off to show all labels no matter what. | `autoSkipPadding` | `number` | `0` | Padding between the ticks on the horizontal axis when `autoSkip` is enabled. @@ -56,6 +60,65 @@ The following options are common to all cartesian axes but do not apply to other | `mirror` | `boolean` | `false` | Flips tick labels around axis, displaying the labels inside the chart instead of outside. *Note: Only applicable to vertical scales.* | `padding` | `number` | `0` | Padding between the tick label and the axis. When set on a vertical axis, this applies in the horizontal (X) direction. When set on a horizontal axis, this applies in the vertical (Y) direction. +### Tick Alignment + +The alignment of ticks is primarily controlled using two settings on the tick configuration object: `alignment` and `crossAlignment`. The `alignment` setting configures how labels align with the tick mark along the axis direction (i.e. horizontal for a horizontal axis and vertical for a vertical axis). The `crossAlignment` setting configures how labels align with the tick mark in the perpendicular direction (i.e. vertical for a horizontal axis and horizontal for a vertical axis). In the example below, the `crossAlignment` setting is used to left align the labels on the Y axis. + +```jsx live +function exampleAlignment() { + useEffect(() => { + const cfg = { + type: 'bar', + data: { + labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'], + datasets: [{ + axis: 'y', + label: 'My First Dataset', + data: [65, 59, 80, 81, 56, 55, 40], + fill: false, + backgroundColor: [ + 'rgba(255, 99, 132, 0.2)', + 'rgba(255, 159, 64, 0.2)', + 'rgba(255, 205, 86, 0.2)', + 'rgba(75, 192, 192, 0.2)', + 'rgba(54, 162, 235, 0.2)', + 'rgba(153, 102, 255, 0.2)', + 'rgba(201, 203, 207, 0.2)' + ], + borderColor: [ + 'rgb(255, 99, 132)', + 'rgb(255, 159, 64)', + 'rgb(255, 205, 86)', + 'rgb(75, 192, 192)', + 'rgb(54, 162, 235)', + 'rgb(153, 102, 255)', + 'rgb(201, 203, 207)' + ], + borderWidth: 1 + }] + }, + options: { + indexAxis: 'y', + scales: { + x: { + beginAtZero: true + }, + y: { + ticks: { + crossAlignment: 'far', + } + } + } + } + }; + new Chart(document.getElementById('chartjs-1').getContext('2d'), cfg); + }); + return
; +} +``` + +**Note:** the `crossAlignment` setting is not used the the tick rotation is not `0`, the axis position is `'center'` or the position is with respect to a data value. + ### Axis ID The properties `dataset.xAxisID` or `dataset.yAxisID` have to match to `scales` property. This is especially needed if multi-axes charts are used. diff --git a/docs/docs/axes/styling.md b/docs/docs/axes/styling.md index eed36ab4d..a9c8f1a0b 100644 --- a/docs/docs/axes/styling.md +++ b/docs/docs/axes/styling.md @@ -42,7 +42,6 @@ The tick configuration is nested under the scale configuration in the `ticks` ke | Name | Type | Scriptable | Default | Description | ---- | ---- | :-------------------------------: | ------- | ----------- -| `alignment` | `string` | | `'center'` | The tick alignment along the axis. Can be `'start'`, `'center'`, or `'end'`. | `callback` | `function` | | | Returns the string representation of the tick value as it should be displayed on the chart. See [callback](../axes/labelling.md#creating-custom-tick-formats). | `display` | `boolean` | | `true` | If true, show tick labels. | `font` | `Font` | Yes | `defaults.font` | See [Fonts](../general/fonts.md) diff --git a/src/core/core.scale.js b/src/core/core.scale.js index 69a69be76..5dc8d7e2d 100644 --- a/src/core/core.scale.js +++ b/src/core/core.scale.js @@ -63,6 +63,7 @@ defaults.set('scale', { minor: {}, major: {}, alignment: 'center', + crossAlignment: 'near', } }); @@ -1250,56 +1251,59 @@ export default class Scale extends Element { const axis = me.axis; const options = me.options; const {position, ticks: optionTicks} = options; - const isMirrored = optionTicks.mirror; const isHorizontal = me.isHorizontal(); const ticks = me.ticks; - const tickPadding = optionTicks.padding; + const {alignment, crossAlignment, padding} = optionTicks; const tl = getTickMarkLength(options.gridLines); + const tickAndPadding = tl + padding; const rotation = -toRadians(me.labelRotation); const items = []; let i, ilen, tick, label, x, y, textAlign, pixel, font, lineHeight, lineCount, textOffset; let textBaseline = 'middle'; if (position === 'top') { - y = me.bottom - tl - tickPadding; + y = me.bottom - tickAndPadding; textAlign = me._getXAxisLabelAlignment(); } else if (position === 'bottom') { - y = me.top + tl + tickPadding; + y = me.top + tickAndPadding; textAlign = me._getXAxisLabelAlignment(); } else if (position === 'left') { - x = me.right - (isMirrored ? 0 : tl) - tickPadding; - textAlign = isMirrored ? 'left' : 'right'; + const ret = this._getYAxisLabelAlignment(tl); + textAlign = ret.textAlign; + x = ret.x; } else if (position === 'right') { - x = me.left + (isMirrored ? 0 : tl) + tickPadding; - textAlign = isMirrored ? 'right' : 'left'; + const ret = this._getYAxisLabelAlignment(tl); + textAlign = ret.textAlign; + x = ret.x; } else if (axis === 'x') { if (position === 'center') { - y = ((chartArea.top + chartArea.bottom) / 2) + tl + tickPadding; + y = ((chartArea.top + chartArea.bottom) / 2) + tickAndPadding; } else if (isObject(position)) { const positionAxisID = Object.keys(position)[0]; const value = position[positionAxisID]; - y = me.chart.scales[positionAxisID].getPixelForValue(value) + tl + tickPadding; + y = me.chart.scales[positionAxisID].getPixelForValue(value) + tickAndPadding; } textAlign = me._getXAxisLabelAlignment(); } else if (axis === 'y') { if (position === 'center') { - x = ((chartArea.left + chartArea.right) / 2) - tl - tickPadding; + x = ((chartArea.left + chartArea.right) / 2) - tickAndPadding; } else if (isObject(position)) { const positionAxisID = Object.keys(position)[0]; const value = position[positionAxisID]; x = me.chart.scales[positionAxisID].getPixelForValue(value); } - textAlign = 'right'; + textAlign = this._getYAxisLabelAlignment(tl).textAlign; } if (axis === 'y') { - if (optionTicks.alignment === 'start') { + if (alignment === 'start') { textBaseline = 'top'; - } else if (optionTicks.alignment === 'end') { + } else if (alignment === 'end') { textBaseline = 'bottom'; } } + const labelSizes = me._getLabelSizes(); for (i = 0, ilen = ticks.length; i < ilen; ++i) { tick = ticks[i]; label = tick.label; @@ -1308,15 +1312,30 @@ export default class Scale extends Element { font = me._resolveTickFontOptions(i); lineHeight = font.lineHeight; lineCount = isArray(label) ? label.length : 1; + const halfCount = lineCount / 2; if (isHorizontal) { x = pixel; if (position === 'top') { - textOffset = (Math.sin(rotation) * (lineCount / 2) + 0.5) * lineHeight; - textOffset -= (rotation === 0 ? (lineCount - 0.5) : Math.cos(rotation) * (lineCount / 2)) * lineHeight; - } else { - textOffset = Math.sin(rotation) * (lineCount / 2) * lineHeight; - textOffset += (rotation === 0 ? 0.5 : Math.cos(rotation) * (lineCount / 2)) * lineHeight; + if (crossAlignment === 'near' || rotation !== 0) { + textOffset = (Math.sin(rotation) * halfCount + 0.5) * lineHeight; + textOffset -= (rotation === 0 ? (lineCount - 0.5) : Math.cos(rotation) * halfCount) * lineHeight; + } else if (crossAlignment === 'center') { + textOffset = -1 * (labelSizes.highest.height / 2); + textOffset -= halfCount * lineHeight; + } else { + textOffset = (-1 * labelSizes.highest.height) + (0.5 * lineHeight); + } + } else if (position === 'bottom') { + if (crossAlignment === 'near' || rotation !== 0) { + textOffset = Math.sin(rotation) * halfCount * lineHeight; + textOffset += (rotation === 0 ? 0.5 : Math.cos(rotation) * halfCount) * lineHeight; + } else if (crossAlignment === 'center') { + textOffset = labelSizes.highest.height / 2; + textOffset -= halfCount * lineHeight; + } else { + textOffset = labelSizes.highest.height - ((lineCount - 0.5) * lineHeight); + } } } else { y = pixel; @@ -1358,6 +1377,58 @@ export default class Scale extends Element { return align; } + _getYAxisLabelAlignment(tl) { + const me = this; + const {position, ticks} = me.options; + const {crossAlignment, mirror, padding} = ticks; + const labelSizes = me._getLabelSizes(); + const tickAndPadding = tl + padding; + const widest = labelSizes.widest.width; + + let textAlign; + let x; + + if (position === 'left') { + if (mirror) { + textAlign = 'left'; + x = me.right - padding; + } else { + x = me.right - tickAndPadding; + + if (crossAlignment === 'near') { + textAlign = 'right'; + } else if (crossAlignment === 'center') { + textAlign = 'center'; + x -= (widest / 2); + } else { + textAlign = 'left'; + x -= widest; + } + } + } else if (position === 'right') { + if (mirror) { + textAlign = 'right'; + x = me.left + padding; + } else { + x = me.left + tickAndPadding; + + if (crossAlignment === 'near') { + textAlign = 'left'; + } else if (crossAlignment === 'center') { + textAlign = 'center'; + x += widest / 2; + } else { + textAlign = 'right'; + x += widest; + } + } + } else { + textAlign = 'right'; + } + + return {textAlign, x}; + } + /** * @protected */ diff --git a/test/fixtures/core.scale/crossAlignment/cross-align-bottom-center.js b/test/fixtures/core.scale/crossAlignment/cross-align-bottom-center.js new file mode 100644 index 000000000..5eae38856 --- /dev/null +++ b/test/fixtures/core.scale/crossAlignment/cross-align-bottom-center.js @@ -0,0 +1,29 @@ +module.exports = { + config: { + type: 'bar', + data: { + datasets: [{ + data: [1, 2, 3], + }], + labels: [['Label1', 'line 2', 'line3'], 'Label2', 'Label3'] + }, + options: { + legend: false, + title: false, + scales: { + x: { + ticks: { + crossAlignment: 'center', + }, + }, + } + } + }, + options: { + spriteText: true, + canvas: { + height: 256, + width: 512 + } + } +}; diff --git a/test/fixtures/core.scale/crossAlignment/cross-align-bottom-center.png b/test/fixtures/core.scale/crossAlignment/cross-align-bottom-center.png new file mode 100644 index 000000000..b817e5bc3 Binary files /dev/null and b/test/fixtures/core.scale/crossAlignment/cross-align-bottom-center.png differ diff --git a/test/fixtures/core.scale/crossAlignment/cross-align-bottom-far.js b/test/fixtures/core.scale/crossAlignment/cross-align-bottom-far.js new file mode 100644 index 000000000..f7daff226 --- /dev/null +++ b/test/fixtures/core.scale/crossAlignment/cross-align-bottom-far.js @@ -0,0 +1,29 @@ +module.exports = { + config: { + type: 'bar', + data: { + datasets: [{ + data: [1, 2, 3], + }], + labels: [['Label1', 'line 2', 'line3'], 'Label2', 'Label3'] + }, + options: { + legend: false, + title: false, + scales: { + x: { + ticks: { + crossAlignment: 'far', + }, + }, + } + } + }, + options: { + spriteText: true, + canvas: { + height: 256, + width: 512 + } + } +}; diff --git a/test/fixtures/core.scale/crossAlignment/cross-align-bottom-far.png b/test/fixtures/core.scale/crossAlignment/cross-align-bottom-far.png new file mode 100644 index 000000000..6dd7a50fa Binary files /dev/null and b/test/fixtures/core.scale/crossAlignment/cross-align-bottom-far.png differ diff --git a/test/fixtures/core.scale/crossAlignment/cross-align-bottom-near.js b/test/fixtures/core.scale/crossAlignment/cross-align-bottom-near.js new file mode 100644 index 000000000..69175babc --- /dev/null +++ b/test/fixtures/core.scale/crossAlignment/cross-align-bottom-near.js @@ -0,0 +1,29 @@ +module.exports = { + config: { + type: 'bar', + data: { + datasets: [{ + data: [1, 2, 3], + }], + labels: [['Label1', 'line 2', 'line3'], 'Label2', 'Label3'] + }, + options: { + legend: false, + title: false, + scales: { + x: { + ticks: { + crossAlignment: 'near', + }, + }, + } + } + }, + options: { + spriteText: true, + canvas: { + height: 256, + width: 512 + } + } +}; diff --git a/test/fixtures/core.scale/crossAlignment/cross-align-bottom-near.png b/test/fixtures/core.scale/crossAlignment/cross-align-bottom-near.png new file mode 100644 index 000000000..a7bfa8de1 Binary files /dev/null and b/test/fixtures/core.scale/crossAlignment/cross-align-bottom-near.png differ diff --git a/test/fixtures/core.scale/crossAlignment/cross-align-left-center.js b/test/fixtures/core.scale/crossAlignment/cross-align-left-center.js new file mode 100644 index 000000000..d51df0aa3 --- /dev/null +++ b/test/fixtures/core.scale/crossAlignment/cross-align-left-center.js @@ -0,0 +1,31 @@ +module.exports = { + config: { + type: 'bar', + data: { + datasets: [{ + data: [1, 2, 3], + }], + labels: ['Long long label 1', 'Label2', 'Label3'] + }, + options: { + indexAxis: 'y', + legend: false, + title: false, + scales: { + y: { + position: 'left', + ticks: { + crossAlignment: 'center', + }, + }, + } + } + }, + options: { + spriteText: true, + canvas: { + height: 256, + width: 512 + } + } +}; diff --git a/test/fixtures/core.scale/crossAlignment/cross-align-left-center.png b/test/fixtures/core.scale/crossAlignment/cross-align-left-center.png new file mode 100644 index 000000000..747d70493 Binary files /dev/null and b/test/fixtures/core.scale/crossAlignment/cross-align-left-center.png differ diff --git a/test/fixtures/core.scale/crossAlignment/cross-align-left-far.js b/test/fixtures/core.scale/crossAlignment/cross-align-left-far.js new file mode 100644 index 000000000..0e1232c92 --- /dev/null +++ b/test/fixtures/core.scale/crossAlignment/cross-align-left-far.js @@ -0,0 +1,31 @@ +module.exports = { + config: { + type: 'bar', + data: { + datasets: [{ + data: [1, 2, 3], + }], + labels: ['Long long label 1', 'Label2', 'Label3'] + }, + options: { + indexAxis: 'y', + legend: false, + title: false, + scales: { + y: { + position: 'left', + ticks: { + crossAlignment: 'far', + }, + }, + } + } + }, + options: { + spriteText: true, + canvas: { + height: 256, + width: 512 + } + } +}; diff --git a/test/fixtures/core.scale/crossAlignment/cross-align-left-far.png b/test/fixtures/core.scale/crossAlignment/cross-align-left-far.png new file mode 100644 index 000000000..be066d19b Binary files /dev/null and b/test/fixtures/core.scale/crossAlignment/cross-align-left-far.png differ diff --git a/test/fixtures/core.scale/crossAlignment/cross-align-left-near.js b/test/fixtures/core.scale/crossAlignment/cross-align-left-near.js new file mode 100644 index 000000000..591e92c66 --- /dev/null +++ b/test/fixtures/core.scale/crossAlignment/cross-align-left-near.js @@ -0,0 +1,31 @@ +module.exports = { + config: { + type: 'bar', + data: { + datasets: [{ + data: [1, 2, 3], + }], + labels: ['Long long label 1', 'Label2', 'Label3'] + }, + options: { + indexAxis: 'y', + legend: false, + title: false, + scales: { + y: { + position: 'left', + ticks: { + crossAlignment: 'near', + }, + }, + } + } + }, + options: { + spriteText: true, + canvas: { + height: 256, + width: 512 + } + } +}; diff --git a/test/fixtures/core.scale/crossAlignment/cross-align-left-near.png b/test/fixtures/core.scale/crossAlignment/cross-align-left-near.png new file mode 100644 index 000000000..50d27b83a Binary files /dev/null and b/test/fixtures/core.scale/crossAlignment/cross-align-left-near.png differ diff --git a/test/fixtures/core.scale/crossAlignment/cross-align-right-center.js b/test/fixtures/core.scale/crossAlignment/cross-align-right-center.js new file mode 100644 index 000000000..4e56f15b0 --- /dev/null +++ b/test/fixtures/core.scale/crossAlignment/cross-align-right-center.js @@ -0,0 +1,31 @@ +module.exports = { + config: { + type: 'bar', + data: { + datasets: [{ + data: [1, 2, 3], + }], + labels: ['Long long label 1', 'Label2', 'Label3'] + }, + options: { + indexAxis: 'y', + legend: false, + title: false, + scales: { + y: { + position: 'right', + ticks: { + crossAlignment: 'center', + }, + }, + } + } + }, + options: { + spriteText: true, + canvas: { + height: 256, + width: 512 + } + } +}; diff --git a/test/fixtures/core.scale/crossAlignment/cross-align-right-center.png b/test/fixtures/core.scale/crossAlignment/cross-align-right-center.png new file mode 100644 index 000000000..baa12cb7f Binary files /dev/null and b/test/fixtures/core.scale/crossAlignment/cross-align-right-center.png differ diff --git a/test/fixtures/core.scale/crossAlignment/cross-align-right-far.js b/test/fixtures/core.scale/crossAlignment/cross-align-right-far.js new file mode 100644 index 000000000..fc7906f46 --- /dev/null +++ b/test/fixtures/core.scale/crossAlignment/cross-align-right-far.js @@ -0,0 +1,31 @@ +module.exports = { + config: { + type: 'bar', + data: { + datasets: [{ + data: [1, 2, 3], + }], + labels: ['Long long label 1', 'Label2', 'Label3'] + }, + options: { + indexAxis: 'y', + legend: false, + title: false, + scales: { + y: { + position: 'right', + ticks: { + crossAlignment: 'far', + }, + }, + } + } + }, + options: { + spriteText: true, + canvas: { + height: 256, + width: 512 + } + } +}; diff --git a/test/fixtures/core.scale/crossAlignment/cross-align-right-far.png b/test/fixtures/core.scale/crossAlignment/cross-align-right-far.png new file mode 100644 index 000000000..ae02309c1 Binary files /dev/null and b/test/fixtures/core.scale/crossAlignment/cross-align-right-far.png differ diff --git a/test/fixtures/core.scale/crossAlignment/cross-align-right-near.js b/test/fixtures/core.scale/crossAlignment/cross-align-right-near.js new file mode 100644 index 000000000..acdc5bca7 --- /dev/null +++ b/test/fixtures/core.scale/crossAlignment/cross-align-right-near.js @@ -0,0 +1,31 @@ +module.exports = { + config: { + type: 'bar', + data: { + datasets: [{ + data: [1, 2, 3], + }], + labels: ['Long long label 1', 'Label2', 'Label3'] + }, + options: { + indexAxis: 'y', + legend: false, + title: false, + scales: { + y: { + position: 'right', + ticks: { + crossAlignment: 'near', + }, + }, + } + } + }, + options: { + spriteText: true, + canvas: { + height: 256, + width: 512 + } + } +}; diff --git a/test/fixtures/core.scale/crossAlignment/cross-align-right-near.png b/test/fixtures/core.scale/crossAlignment/cross-align-right-near.png new file mode 100644 index 000000000..d0cd81122 Binary files /dev/null and b/test/fixtures/core.scale/crossAlignment/cross-align-right-near.png differ diff --git a/test/fixtures/core.scale/crossAlignment/cross-align-top-center.js b/test/fixtures/core.scale/crossAlignment/cross-align-top-center.js new file mode 100644 index 000000000..4ed7034d6 --- /dev/null +++ b/test/fixtures/core.scale/crossAlignment/cross-align-top-center.js @@ -0,0 +1,30 @@ +module.exports = { + config: { + type: 'bar', + data: { + datasets: [{ + data: [1, 2, 3], + }], + labels: [['Label1', 'line 2', 'line3'], 'Label2', 'Label3'] + }, + options: { + legend: false, + title: false, + scales: { + x: { + position: 'top', + ticks: { + crossAlignment: 'center', + }, + }, + } + } + }, + options: { + spriteText: true, + canvas: { + height: 256, + width: 512 + } + } +}; diff --git a/test/fixtures/core.scale/crossAlignment/cross-align-top-center.png b/test/fixtures/core.scale/crossAlignment/cross-align-top-center.png new file mode 100644 index 000000000..4f5eb963c Binary files /dev/null and b/test/fixtures/core.scale/crossAlignment/cross-align-top-center.png differ diff --git a/test/fixtures/core.scale/crossAlignment/cross-align-top-far.js b/test/fixtures/core.scale/crossAlignment/cross-align-top-far.js new file mode 100644 index 000000000..2d6f8ceb3 --- /dev/null +++ b/test/fixtures/core.scale/crossAlignment/cross-align-top-far.js @@ -0,0 +1,30 @@ +module.exports = { + config: { + type: 'bar', + data: { + datasets: [{ + data: [1, 2, 3], + }], + labels: [['Label1', 'line 2', 'line3'], 'Label2', 'Label3'] + }, + options: { + legend: false, + title: false, + scales: { + x: { + position: 'top', + ticks: { + crossAlignment: 'far', + }, + }, + } + } + }, + options: { + spriteText: true, + canvas: { + height: 256, + width: 512 + } + } +}; diff --git a/test/fixtures/core.scale/crossAlignment/cross-align-top-far.png b/test/fixtures/core.scale/crossAlignment/cross-align-top-far.png new file mode 100644 index 000000000..ea5c88d50 Binary files /dev/null and b/test/fixtures/core.scale/crossAlignment/cross-align-top-far.png differ diff --git a/test/fixtures/core.scale/crossAlignment/cross-align-top-near.js b/test/fixtures/core.scale/crossAlignment/cross-align-top-near.js new file mode 100644 index 000000000..e2a97974e --- /dev/null +++ b/test/fixtures/core.scale/crossAlignment/cross-align-top-near.js @@ -0,0 +1,30 @@ +module.exports = { + config: { + type: 'bar', + data: { + datasets: [{ + data: [1, 2, 3], + }], + labels: [['Label1', 'line 2', 'line3'], 'Label2', 'Label3'] + }, + options: { + legend: false, + title: false, + scales: { + x: { + position: 'top', + ticks: { + crossAlignment: 'near', + }, + }, + } + } + }, + options: { + spriteText: true, + canvas: { + height: 256, + width: 512 + } + } +}; diff --git a/test/fixtures/core.scale/crossAlignment/cross-align-top-near.png b/test/fixtures/core.scale/crossAlignment/cross-align-top-near.png new file mode 100644 index 000000000..932166863 Binary files /dev/null and b/test/fixtures/core.scale/crossAlignment/cross-align-top-near.png differ diff --git a/types/scales/index.d.ts b/types/scales/index.d.ts index fe19273c6..81e1cce2f 100644 --- a/types/scales/index.d.ts +++ b/types/scales/index.d.ts @@ -143,6 +143,14 @@ export interface ICartesianScaleOptions extends ICoreScaleOptions { * @default 0 */ autoSkipPadding: number; + + /** + * How is the label positioned perpendicular to the axis direction. + * This only applies when the rotation is 0 and the axis position is one of "top", "left", "right", or "bottom" + * @default 'near' + */ + crossAlignment: 'near' | 'center' | 'far'; + /** * Distance in pixels to offset the label from the centre point of the tick (in the x direction for the x axis, and the y direction for the y axis). Note: this can cause labels at the edges to be cropped by the edge of the canvas * @default 0