* `'start'`
* `'end'`
+* `'middle'` (only valid on stacked bars: the borders between bars are skipped)
* `'bottom'`
* `'left'`
* `'top'`
| `backgroundColor` | [`Color`](/general/colors.md) | `Chart.defaults.backgroundColor` | Bar fill color.
| `borderWidth` | `number` | `0` | Bar stroke width.
| `borderColor` | [`Color`](/general/colors.md) | `Chart.defaults.borderColor` | Bar stroke color.
-| `borderSkipped` | `string` | `'start'` | Skipped (excluded) border: `'start'`, `'end'`, `'bottom'`, `'left'`, `'top'` or `'right'`.
+| `borderSkipped` | `string` | `'start'` | Skipped (excluded) border: `'start'`, `'end'`, `'middle'`, `'bottom'`, `'left'`, `'top'`, `'right'` or `false`.
| `borderRadius` | `number`\|`object` | `0` | The bar border radius (in pixels).
| [`pointStyle`](#point-styles) | `string`\|`Image` | `'circle'` | Style of the point for legend.
return (vScale.isHorizontal() ? 1 : -1) * (vScale.min >= actualBase ? 1 : -1);
}
+function borderProps(properties) {
+ let reverse, start, end, top, bottom;
+ if (properties.horizontal) {
+ reverse = properties.base > properties.x;
+ start = 'left';
+ end = 'right';
+ } else {
+ reverse = properties.base < properties.y;
+ start = 'bottom';
+ end = 'top';
+ }
+ if (reverse) {
+ top = 'end';
+ bottom = 'start';
+ } else {
+ top = 'start';
+ bottom = 'end';
+ }
+ return {start, end, reverse, top, bottom};
+}
+
+function setBorderSkipped(properties, options, stack, index) {
+ let edge = options.borderSkipped;
+ const res = {};
+
+ if (!edge) {
+ properties.borderSkipped = res;
+ return;
+ }
+
+ const {start, end, reverse, top, bottom} = borderProps(properties);
+
+ if (edge === 'middle' && stack) {
+ properties.enableBorderRadius = true;
+ if ((stack._top || 0) === index) {
+ edge = top;
+ } else if ((stack._bottom || 0) === index) {
+ edge = bottom;
+ } else {
+ res[parseEdge(bottom, start, end, reverse)] = true;
+ edge = top;
+ }
+ }
+
+ res[parseEdge(edge, start, end, reverse)] = true;
+ properties.borderSkipped = res;
+}
+
+function parseEdge(edge, a, b, reverse) {
+ if (reverse) {
+ edge = swap(edge, a, b);
+ edge = startEnd(edge, b, a);
+ } else {
+ edge = startEnd(edge, a, b);
+ }
+ return edge;
+}
+
+function swap(orig, v1, v2) {
+ return orig === v1 ? v2 : orig === v2 ? v1 : orig;
+}
+
+function startEnd(v, start, end) {
+ return v === 'start' ? start : v === 'end' ? end : v;
+}
+
export default class BarController extends DatasetController {
/**
updateElements(bars, start, count, mode) {
const me = this;
const reset = mode === 'reset';
- const vScale = me._cachedMeta.vScale;
+ const {index, _cachedMeta: {vScale}} = me;
const base = vScale.getBasePixel();
const horizontal = vScale.isHorizontal();
const ruler = me._getRuler();
const properties = {
horizontal,
base: vpixels.base,
- enableBorderRadius: !stack || isFloatBar(parsed._custom) || (me.index === stack._top || me.index === stack._bottom),
+ enableBorderRadius: !stack || isFloatBar(parsed._custom) || (index === stack._top || index === stack._bottom),
x: horizontal ? vpixels.head : ipixels.center,
y: horizontal ? ipixels.center : vpixels.head,
height: horizontal ? ipixels.size : Math.abs(vpixels.size),
if (includeOptions) {
properties.options = sharedOptions || me.resolveDataElementOptions(i, bars[i].active ? 'active' : mode);
}
+ setBorderSkipped(properties, properties.options || bars[i].options, stack, index);
me.updateElement(bars[i], i, properties, mode);
}
}
return {left, top, right, bottom};
}
-function parseBorderSkipped(bar) {
- let edge = bar.options.borderSkipped;
- const res = {};
-
- if (!edge) {
- return res;
- }
-
- edge = bar.horizontal
- ? parseEdge(edge, 'left', 'right', bar.base > bar.x)
- : parseEdge(edge, 'bottom', 'top', bar.base < bar.y);
-
- res[edge] = true;
- return res;
-}
-
-function parseEdge(edge, a, b, reverse) {
- if (reverse) {
- edge = swap(edge, a, b);
- edge = startEnd(edge, b, a);
- } else {
- edge = startEnd(edge, a, b);
- }
- return edge;
-}
-
-function swap(orig, v1, v2) {
- return orig === v1 ? v2 : orig === v2 ? v1 : orig;
-}
-
-function startEnd(v, start, end) {
- return v === 'start' ? start : v === 'end' ? end : v;
-}
-
function skipOrLimit(skip, value, min, max) {
return skip ? 0 : _limitValue(value, min, max);
}
function parseBorderWidth(bar, maxW, maxH) {
const value = bar.options.borderWidth;
- const skip = parseBorderSkipped(bar);
+ const skip = bar.borderSkipped;
const o = toTRBL(value);
return {
const value = bar.options.borderRadius;
const o = toTRBLCorners(value);
const maxR = Math.min(maxW, maxH);
- const skip = parseBorderSkipped(bar);
+ const skip = bar.borderSkipped;
// If the value is an object, assume the user knows what they are doing
// and apply as directed.
--- /dev/null
+module.exports = {
+ threshold: 0.01,
+ config: {
+ type: 'bar',
+ data: {
+ labels: [0, 1, 2, 3, 4, 5],
+ datasets: [
+ {
+ backgroundColor: 'red',
+ data: [12, 19, 12, 5, 4, 12],
+ },
+ {
+ backgroundColor: 'green',
+ data: [12, 19, -4, 5, 8, 3],
+ },
+ {
+ backgroundColor: 'blue',
+ data: [7, 11, -12, 12, 0, -7],
+ }
+ ]
+ },
+ options: {
+ borderRadius: Number.MAX_VALUE,
+ borderSkipped: 'middle',
+ borderWidth: 2,
+ scales: {
+ x: {display: false, stacked: true},
+ y: {display: false, stacked: true}
+ }
+ }
+ },
+ options: {
+ canvas: {
+ height: 256,
+ width: 512
+ }
+ }
+};