From 41bbede618088e39a0c8e4d25cb00ac174742078 Mon Sep 17 00:00:00 2001 From: Jukka Kurkela Date: Mon, 25 Feb 2019 10:03:12 +0200 Subject: [PATCH] Add support for per side border width for rectangle (#6077) --- docs/charts/bar.md | 11 +- src/elements/element.rectangle.js | 239 +++++++++--------- .../controller.bar/borderSkipped/value.js | 5 + .../controller.bar/borderSkipped/value.png | Bin 4889 -> 1989 bytes .../borderWidth/indexable-object.js | 56 ++++ .../borderWidth/indexable-object.png | Bin 0 -> 1871 bytes .../controller.bar/borderWidth/indexable.png | Bin 4969 -> 1855 bytes .../controller.bar/borderWidth/negative.js | 49 ++++ .../controller.bar/borderWidth/negative.png | Bin 0 -> 1766 bytes .../controller.bar/borderWidth/object.js | 42 +++ .../controller.bar/borderWidth/object.png | Bin 0 -> 2113 bytes .../borderWidth/scriptable-object.js | 54 ++++ .../borderWidth/scriptable-object.png | Bin 0 -> 1640 bytes .../controller.bar/borderWidth/value.png | Bin 5109 -> 2064 bytes .../controller.bar/chart-area-clip.js | 42 +++ .../controller.bar/chart-area-clip.png | Bin 0 -> 1711 bytes .../controller.bar/horizontal-borders.js | 42 +++ .../controller.bar/horizontal-borders.png | Bin 0 -> 1522 bytes test/specs/element.rectangle.tests.js | 183 +------------- 19 files changed, 420 insertions(+), 303 deletions(-) create mode 100644 test/fixtures/controller.bar/borderWidth/indexable-object.js create mode 100644 test/fixtures/controller.bar/borderWidth/indexable-object.png create mode 100644 test/fixtures/controller.bar/borderWidth/negative.js create mode 100644 test/fixtures/controller.bar/borderWidth/negative.png create mode 100644 test/fixtures/controller.bar/borderWidth/object.js create mode 100644 test/fixtures/controller.bar/borderWidth/object.png create mode 100644 test/fixtures/controller.bar/borderWidth/scriptable-object.js create mode 100644 test/fixtures/controller.bar/borderWidth/scriptable-object.png create mode 100644 test/fixtures/controller.bar/chart-area-clip.js create mode 100644 test/fixtures/controller.bar/chart-area-clip.png create mode 100644 test/fixtures/controller.bar/horizontal-borders.js create mode 100644 test/fixtures/controller.bar/horizontal-borders.png diff --git a/docs/charts/bar.md b/docs/charts/bar.md index 3f6e5cdd202..02f080614a1 100644 --- a/docs/charts/bar.md +++ b/docs/charts/bar.md @@ -71,7 +71,7 @@ the color of the bars is generally set this way. | [`backgroundColor`](#styling) | [`Color`](../general/colors.md) | Yes | Yes | `'rgba(0, 0, 0, 0.1)'` | [`borderColor`](#styling) | [`Color`](../general/colors.md) | Yes | Yes | `'rgba(0, 0, 0, 0.1)'` | [`borderSkipped`](#borderskipped) | `string` | Yes | Yes | `'bottom'` -| [`borderWidth`](#styling) | `number` | Yes | Yes | `0` +| [`borderWidth`](#borderwidth) | number|object | Yes | Yes | `0` | [`data`](#data-structure) | `object[]` | - | - | **required** | [`hoverBackgroundColor`](#interactions) | [`Color`](../general/colors.md) | - | Yes | `undefined` | [`hoverBorderColor`](#interactions) | [`Color`](../general/colors.md) | - | Yes | `undefined` @@ -97,7 +97,7 @@ The style of each bar can be controlled with the following properties: | `backgroundColor` | The bar background color. | `borderColor` | The bar border color. | [`borderSkipped`](#borderskipped) | The edge to skip when drawing bar. -| `borderWidth` | The bar border width (in pixels). +| [`borderWidth`](#borderwidth) | The bar border width (in pixels). All these values, if `undefined`, fallback to the associated [`elements.rectangle.*`](../configuration/elements.md#rectangle-configuration) options. @@ -107,11 +107,18 @@ This setting is used to avoid drawing the bar stroke at the base of the fill. In general, this does not need to be changed except when creating chart types that derive from a bar chart. +**Note:** for negative bars in vertical chart, `top` and `bottom` are flipped. Same goes for `left` and `right` in horizontal chart. + Options are: * `'bottom'` * `'left'` * `'top'` * `'right'` +* `false` + +#### borderWidth + +If this value is a number, it is applied to all sides of the rectangle (left, top, right, bottom), except [`borderSkipped`](#borderskipped). If this value is an object, the `left` property defines the left border width. Similarly the `right`, `top` and `bottom` properties can also be specified. Omitted borders and [`borderSkipped`](#borderskipped) are skipped. ### Interactions diff --git a/src/elements/element.rectangle.js b/src/elements/element.rectangle.js index fe3702cda4e..5e5a2eac459 100644 --- a/src/elements/element.rectangle.js +++ b/src/elements/element.rectangle.js @@ -2,6 +2,7 @@ var defaults = require('../core/core.defaults'); var Element = require('../core/core.element'); +var helpers = require('../helpers/index'); var defaultColor = defaults.global.defaultColor; @@ -16,8 +17,8 @@ defaults._set('global', { } }); -function isVertical(bar) { - return bar._view.width !== undefined; +function isVertical(vm) { + return vm && vm.width !== undefined; } /** @@ -26,24 +27,21 @@ function isVertical(bar) { * @return {Bounds} bounds of the bar * @private */ -function getBarBounds(bar) { - var vm = bar._view; - var x1, x2, y1, y2; - - if (isVertical(bar)) { - // vertical - var halfWidth = vm.width / 2; - x1 = vm.x - halfWidth; - x2 = vm.x + halfWidth; +function getBarBounds(vm) { + var x1, x2, y1, y2, half; + + if (isVertical(vm)) { + half = vm.width / 2; + x1 = vm.x - half; + x2 = vm.x + half; y1 = Math.min(vm.y, vm.base); y2 = Math.max(vm.y, vm.base); } else { - // horizontal bar - var halfHeight = vm.height / 2; + half = vm.height / 2; x1 = Math.min(vm.x, vm.base); x2 = Math.max(vm.x, vm.base); - y1 = vm.y - halfHeight; - y2 = vm.y + halfHeight; + y1 = vm.y - half; + y2 = vm.y + half; } return { @@ -54,96 +52,107 @@ function getBarBounds(bar) { }; } -module.exports = Element.extend({ - draw: function() { - var ctx = this._chart.ctx; - var vm = this._view; - var left, right, top, bottom, signX, signY, borderSkipped; - var borderWidth = vm.borderWidth; - - if (!vm.horizontal) { - // bar - left = vm.x - vm.width / 2; - right = vm.x + vm.width / 2; - top = vm.y; - bottom = vm.base; - signX = 1; - signY = bottom > top ? 1 : -1; - borderSkipped = vm.borderSkipped || 'bottom'; - } else { - // horizontal bar - left = vm.base; - right = vm.x; - top = vm.y - vm.height / 2; - bottom = vm.y + vm.height / 2; - signX = right > left ? 1 : -1; - signY = 1; - borderSkipped = vm.borderSkipped || 'left'; - } +function swap(orig, v1, v2) { + return orig === v1 ? v2 : orig === v2 ? v1 : orig; +} - // Canvas doesn't allow us to stroke inside the width so we can - // adjust the sizes to fit if we're setting a stroke on the line - if (borderWidth) { - // borderWidth shold be less than bar width and bar height. - var barSize = Math.min(Math.abs(left - right), Math.abs(top - bottom)); - borderWidth = borderWidth > barSize ? barSize : borderWidth; - var halfStroke = borderWidth / 2; - // Adjust borderWidth when bar top position is near vm.base(zero). - var borderLeft = left + (borderSkipped !== 'left' ? halfStroke * signX : 0); - var borderRight = right + (borderSkipped !== 'right' ? -halfStroke * signX : 0); - var borderTop = top + (borderSkipped !== 'top' ? halfStroke * signY : 0); - var borderBottom = bottom + (borderSkipped !== 'bottom' ? -halfStroke * signY : 0); - // not become a vertical line? - if (borderLeft !== borderRight) { - top = borderTop; - bottom = borderBottom; - } - // not become a horizontal line? - if (borderTop !== borderBottom) { - left = borderLeft; - right = borderRight; - } - } +function parseBorderSkipped(vm) { + var edge = vm.borderSkipped; + var res = {}; - ctx.beginPath(); - ctx.fillStyle = vm.backgroundColor; - ctx.strokeStyle = vm.borderColor; - ctx.lineWidth = borderWidth; - - // Corner points, from bottom-left to bottom-right clockwise - // | 1 2 | - // | 0 3 | - var corners = [ - [left, bottom], - [left, top], - [right, top], - [right, bottom] - ]; - - // Find first (starting) corner with fallback to 'bottom' - var borders = ['bottom', 'left', 'top', 'right']; - var startCorner = borders.indexOf(borderSkipped, 0); - if (startCorner === -1) { - startCorner = 0; - } + if (!edge) { + return res; + } - function cornerAt(index) { - return corners[(startCorner + index) % 4]; + if (vm.horizontal) { + if (vm.base > vm.x) { + edge = swap(edge, 'left', 'right'); } + } else if (vm.base < vm.y) { + edge = swap(edge, 'bottom', 'top'); + } + + res[edge] = true; + return res; +} + +function parseBorderWidth(vm, maxW, maxH) { + var value = vm.borderWidth; + var skip = parseBorderSkipped(vm); + var t, r, b, l; + + if (helpers.isObject(value)) { + t = +value.top || 0; + r = +value.right || 0; + b = +value.bottom || 0; + l = +value.left || 0; + } else { + t = r = b = l = +value || 0; + } + + return { + t: skip.top || (t < 0) ? 0 : t > maxH ? maxH : t, + r: skip.right || (r < 0) ? 0 : r > maxW ? maxW : r, + b: skip.bottom || (b < 0) ? 0 : b > maxH ? maxH : b, + l: skip.left || (l < 0) ? 0 : l > maxW ? maxW : l + }; +} - // Draw rectangle from 'startCorner' - var corner = cornerAt(0); - ctx.moveTo(corner[0], corner[1]); +function boundingRects(vm) { + var bounds = getBarBounds(vm); + var width = bounds.right - bounds.left; + var height = bounds.bottom - bounds.top; + var border = parseBorderWidth(vm, width / 2, height / 2); - for (var i = 1; i < 4; i++) { - corner = cornerAt(i); - ctx.lineTo(corner[0], corner[1]); + return { + outer: { + x: bounds.left, + y: bounds.top, + w: width, + h: height + }, + inner: { + x: bounds.left + border.l, + y: bounds.top + border.t, + w: width - border.l - border.r, + h: height - border.t - border.b } + }; +} + +function inRange(vm, x, y) { + var skipX = x === null; + var skipY = y === null; + var bounds = !vm || (skipX && skipY) ? false : getBarBounds(vm); + + return bounds + && (skipX || x >= bounds.left && x <= bounds.right) + && (skipY || y >= bounds.top && y <= bounds.bottom); +} + +module.exports = Element.extend({ + draw: function() { + var ctx = this._chart.ctx; + var vm = this._view; + var rects = boundingRects(vm); + var outer = rects.outer; + var inner = rects.inner; + + ctx.fillStyle = vm.backgroundColor; + ctx.fillRect(outer.x, outer.y, outer.w, outer.h); - ctx.fill(); - if (borderWidth) { - ctx.stroke(); + if (outer.w === inner.w && outer.h === inner.h) { + return; } + + ctx.save(); + ctx.beginPath(); + ctx.rect(outer.x, outer.y, outer.w, outer.h); + ctx.clip(); + ctx.fillStyle = vm.borderColor; + ctx.rect(inner.x, inner.y, inner.w, inner.h); + ctx.fill('evenodd'); + ctx.restore(); }, height: function() { @@ -152,48 +161,28 @@ module.exports = Element.extend({ }, inRange: function(mouseX, mouseY) { - var inRange = false; - - if (this._view) { - var bounds = getBarBounds(this); - inRange = mouseX >= bounds.left && mouseX <= bounds.right && mouseY >= bounds.top && mouseY <= bounds.bottom; - } - - return inRange; + return inRange(this._view, mouseX, mouseY); }, inLabelRange: function(mouseX, mouseY) { - var me = this; - if (!me._view) { - return false; - } - - var inRange = false; - var bounds = getBarBounds(me); - - if (isVertical(me)) { - inRange = mouseX >= bounds.left && mouseX <= bounds.right; - } else { - inRange = mouseY >= bounds.top && mouseY <= bounds.bottom; - } - - return inRange; + var vm = this._view; + return isVertical(vm) + ? inRange(vm, mouseX, null) + : inRange(vm, null, mouseY); }, inXRange: function(mouseX) { - var bounds = getBarBounds(this); - return mouseX >= bounds.left && mouseX <= bounds.right; + return inRange(this._view, mouseX, null); }, inYRange: function(mouseY) { - var bounds = getBarBounds(this); - return mouseY >= bounds.top && mouseY <= bounds.bottom; + return inRange(this._view, null, mouseY); }, getCenterPoint: function() { var vm = this._view; var x, y; - if (isVertical(this)) { + if (isVertical(vm)) { x = vm.x; y = (vm.y + vm.base) / 2; } else { @@ -207,7 +196,7 @@ module.exports = Element.extend({ getArea: function() { var vm = this._view; - return isVertical(this) + return isVertical(vm) ? vm.width * Math.abs(vm.y - vm.base) : vm.height * Math.abs(vm.x - vm.base); }, diff --git a/test/fixtures/controller.bar/borderSkipped/value.js b/test/fixtures/controller.bar/borderSkipped/value.js index 139a2c68905..fed9592636b 100644 --- a/test/fixtures/controller.bar/borderSkipped/value.js +++ b/test/fixtures/controller.bar/borderSkipped/value.js @@ -22,6 +22,11 @@ module.exports = { { // option in element (fallback) data: [0, 5, -10, null], + }, + { + // option in dataset + data: [0, 5, -10, null], + borderSkipped: false } ] }, diff --git a/test/fixtures/controller.bar/borderSkipped/value.png b/test/fixtures/controller.bar/borderSkipped/value.png index 56ef05a41fe65a38ffee53a6b4a25964e0eee2e7..7f4179c87b13b21345439be6f2f7266b2b9b46d4 100644 GIT binary patch literal 1989 zcmeAS@N?(olHy`uVBq!ia0y~yU;;83893O0)X@p&(is@odpunnLn`LHy|yt|#*@M2 zqOZrc8rB0F%GBg;vee$>@_6piFlYDqqq>d@p4l$g>liMb{O2|=P&+=*@c*sNdHv^= z|9)*RmN|ZY|GwJp{QZCbz588X`~5)W-n)6*fB*gW)${w`UH9L|*YE%M)w8^AKT!0s z#Xj5Dz0WJ_e)iNeHgF39o!h`ILZt8X`?=R|zpZ1a`2JIv;d$jgyO-xkEtwJy+pDp-9^zS) RGZR#~db;|#taD0e0sxW#R#N}~ literal 4889 zcmeHLe^3*57Js{4=mvPjx)5>9LDQ#i#pklRzUaO^6Nw5|b5V%tz2?Cp< zpyvtpYz>t|&})kfsMwLh90>7%k+yPzs89kVskJ48A>oH4m_Y8k@&CKInRcdsB>!Z; zFQ0ku?fbs>c`xr+V!{T#ho1)k@HfWC{t*C2j~I~d=&S4Gzjp(8>o&&5ye&&M4!@E1 zo3DM?zUR&byduhI_5a7YzHOrJA$AkKuWL{Ji0@+m)iH^S+p9LJ>g~04-Fd-cab0-u z_noV))EAbQ9ZP721q`nc3}#JqF51jJ6XZjJ4SXW^UrK?0>wyg+P86>8qByyd??JNs zHPh84KWFW(QJK$3%iiEWd@jBTnZ4(w-;k-dVK0aPy zv)KkLQPIoG+GlO^k`=4$ANUqa%F4=eYx)g0&hty7XTrTYjHb&0Wj9pw1&)LEkLI)y zl?k~e9r`(g!SJm{qq#MgRX)=>9_IDpsW!tV4XrVPQRAH<8qyH8c!W z6uemOX%KY2Clwn+8Kw18U5VUqEKxRQ+ujG?}C1&i}rvhamNkZ_Icm_x|uIPRsPTVF*R`kxB z>A^Sycg`fWtYmamIIiBj%9{dQ+NYBp1*Hhb>7FfjhK>RSjUo3kd6ZcQYbm6iV7h5J z55wztR!?=>Ory{Tc{59BUd50Oum7PV3vMz;S)?|{=)*59A*uLa z8g6foyLf_&N%~IXX}}!rYOlk{7Dk1~C$DGKY4`%(;FoVa;>N;-SFh)Yoi%~7iG?Il z#PT49KEXo#{$-8pf=~c19&3CLC+z;NfVcwkE6$Mjv26X`Hm?rz_~hi|#DU29X7i3h z1xL0T?TY13xb;!d!OW5rncOs8Qd08MNT$UozI8UCrlmt~w$IGWJTMxKKZRC4IOtnk zAyzaEc+fPecECM?09R_YLo1S-YMWw4@SY{uFCE6CYYObMb zuj5x8;ZfugRQG??jYm~fzJ|&_wIdaAQ&b!Shdk09YdDT`Je#{7 z&qo~BX0r4rgd$@#e3ei@_r@|HO8JUZwh=@%JWM~|B=^G_dh&A9a3K%b&+Y?x#1T}m zf8wu;sXoo|4aZ?x0R=yP2+Hp`^0>L&Z}DeHV7Sy-O#UE5%tt#u9@3Yy;47GV3Bz}D zAnUt#&@Ycny*yIHL8|9^-woT2nCFxtP`TiS?tZNB8(Z`_7;}_Xo@AQUNw8fa(GF|1 z?QKowNtwM-YjQl6z9lJ6u|Bd|Wx0`+1$ILoYIAeh5yLwa&G#p*UusS51?IDw6yM}g zn_aKh%bPU$Hfb5x?JT)I4$HjFQc+h|*B&jq&h@+FLbEOvb??;6qt|(>kXO@)y0$Bt z1fUv|&jw?dG>W9kS0kcUKRh?dL}J*%IhQ-ah7a9tx%SojXfrYSAkt5H3;kd3-{o9lY}>#<{r$`>_~&RcygDx678dCN zXlqm2vW54t55ip%bIXZ~2hj>4a$@Vs#BB_0Zi#YkO75Y#e7yO+Pnf+J+KN0z8!lz52WZ^)Hvj+t diff --git a/test/fixtures/controller.bar/borderWidth/indexable-object.js b/test/fixtures/controller.bar/borderWidth/indexable-object.js new file mode 100644 index 00000000000..97f9af5d46f --- /dev/null +++ b/test/fixtures/controller.bar/borderWidth/indexable-object.js @@ -0,0 +1,56 @@ +module.exports = { + config: { + type: 'bar', + data: { + labels: [0, 1, 2, 3, 4, 5], + datasets: [ + { + // option in dataset + data: [0, 5, 10, null, -10, -5], + borderSkipped: false, + borderWidth: [ + {}, + {bottom: 1, left: 1, top: 1, right: 1}, + {bottom: 1, left: 2, top: 1, right: 2}, + {bottom: 1, left: 3, top: 1, right: 3}, + {bottom: 1, left: 4, top: 1, right: 4}, + {bottom: 1, left: 5, top: 1, right: 5} + ] + }, + { + // option in element (fallback) + data: [0, 5, 10, null, -10, -5], + } + ] + }, + options: { + legend: false, + title: false, + elements: { + rectangle: { + backgroundColor: 'transparent', + borderColor: '#80808080', + borderSkipped: false, + borderWidth: [ + {bottom: 1, left: 5, top: 1, right: 5}, + {bottom: 1, left: 4, top: 1, right: 4}, + {bottom: 1, left: 3, top: 1, right: 3}, + {bottom: 1, left: 2, top: 1, right: 2}, + {bottom: 1, left: 1, top: 1, right: 1}, + {} + ] + } + }, + scales: { + xAxes: [{display: false}], + yAxes: [{display: false}] + } + } + }, + options: { + canvas: { + height: 256, + width: 512 + } + } +}; diff --git a/test/fixtures/controller.bar/borderWidth/indexable-object.png b/test/fixtures/controller.bar/borderWidth/indexable-object.png new file mode 100644 index 0000000000000000000000000000000000000000..75471f52679ce285a93d1a4c780c7da6d9e4db48 GIT binary patch literal 1871 zcmeAS@N?(olHy`uVBq!ia0y~yU;;83893O0)X@p&(is@o#XMacLn`LHy|FMaCSAns z;)G3moO_#2JM}i7a$eJ9<2du_#o8}T+g1hN>)^*me|1v%hId0GR;WuyozfafChBH67y*=MPw)pVl$B)l5F*N8f zDljl`2mu|^pfJj$PB=U`Yu)$wpT+sRn`O(@-|djE3kHSky={-rGwiT``0nt>`-~q7 zj^6&xlrWEhA;N`$qd}92AwdwRXc-4XgDXn|!we+`1~EYfri89h1;aEPCW_bb`5pfKFOSR1?pXxPp2t`i7!E{#0fL`P9BhCY`pfse#};;TO&`5{`SSCZ-SfWc z9vA&@$@)OHQjuX_%Z~w0|N^u zu#j<>Fv_GtI5ga5JU%1?%h?bk_L0KNS1FiU9~bUHx3vIVCg!0QSyLm;e9( literal 0 HcmV?d00001 diff --git a/test/fixtures/controller.bar/borderWidth/indexable.png b/test/fixtures/controller.bar/borderWidth/indexable.png index 30011d3b722e474f7ae790f8a1c477f3679195b8..d3f1b85c8675193c6deceae1afe9da63a6bab7ff 100644 GIT binary patch literal 1855 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 ZVSYys`@d?L^aJp-ZL_y~=_c0EUjgZ(L^ diff --git a/test/fixtures/controller.bar/borderWidth/negative.js b/test/fixtures/controller.bar/borderWidth/negative.js new file mode 100644 index 00000000000..5a5189d7fbe --- /dev/null +++ b/test/fixtures/controller.bar/borderWidth/negative.js @@ -0,0 +1,49 @@ +module.exports = { + config: { + type: 'bar', + data: { + labels: [0, 1, 2, 3, 4, 5], + datasets: [ + { + // option in dataset + data: [0, 5, 10, null, -10, -5], + borderWidth: -2 + }, + { + // option in element (fallback) + data: [0, 5, 10, null, -10, -5], + }, + { + data: [0, 5, 10, null, -10, -5], + borderWidth: {left: -5, top: -5, bottom: -5, right: -5}, + borderSkipped: false + }, + { + data: [0, 5, 10, null, -10, -5], + borderWidth: {} + }, + ] + }, + options: { + legend: false, + title: false, + elements: { + rectangle: { + backgroundColor: '#888', + borderColor: '#f00', + borderWidth: -4 + } + }, + scales: { + xAxes: [{display: false}], + yAxes: [{display: false}] + } + } + }, + options: { + canvas: { + height: 256, + width: 512 + } + } +}; diff --git a/test/fixtures/controller.bar/borderWidth/negative.png b/test/fixtures/controller.bar/borderWidth/negative.png new file mode 100644 index 0000000000000000000000000000000000000000..ca2a445d99250cb59e9bc4d06b00b762801bfad4 GIT binary patch literal 1766 zcmeAS@N?(olHy`uVBq!ia0y~yU;;83893O0)X@p&(is@o)_S@)hE&XXd)+tpu!Dr_ z#RiVHAdc1q&DMA6X^C7%gSeIkaH&qnYf^aEf3>>r$=m1~lS=nr=KMEt>$T@mufLrz zeqOULzvj)2ikOFe%~;&Eor$1TN=H|5^0Jb!G?=U=h!Lm4<28k~h0m>d`wSU4FN6&M(r zSQ;1v7#JLx96)jkj0y}a3=R{3LQD(_9zY>R1_2cYmIg?KIgDx>fFNNIc*Ib^U@;n3 tLogjS%;#iaurOg%7%lN=QS$da;9h%i_KZWT^k*{wfv2mV%Q~loCID${;hF#d literal 0 HcmV?d00001 diff --git a/test/fixtures/controller.bar/borderWidth/object.js b/test/fixtures/controller.bar/borderWidth/object.js new file mode 100644 index 00000000000..9133b30aec5 --- /dev/null +++ b/test/fixtures/controller.bar/borderWidth/object.js @@ -0,0 +1,42 @@ +module.exports = { + config: { + type: 'bar', + data: { + labels: [0, 1, 2, 3, 4, 5], + datasets: [ + { + // option in dataset + data: [0, 5, 10, null, -10, -5], + borderSkipped: false, + borderWidth: {bottom: 1, left: 2, top: 3, right: 4} + }, + { + // option in element (fallback) + data: [0, 5, 10, null, -10, -5], + } + ] + }, + options: { + legend: false, + title: false, + elements: { + rectangle: { + backgroundColor: 'transparent', + borderColor: '#888', + borderSkipped: false, + borderWidth: {bottom: 4, left: 3, top: 2, right: 1} + } + }, + scales: { + xAxes: [{display: false}], + yAxes: [{display: false}] + } + } + }, + options: { + canvas: { + height: 256, + width: 512 + } + } +}; diff --git a/test/fixtures/controller.bar/borderWidth/object.png b/test/fixtures/controller.bar/borderWidth/object.png new file mode 100644 index 0000000000000000000000000000000000000000..04576006af463429104cd7595d23318d113cf6cd GIT binary patch literal 2113 zcmeAS@N?(olHy`uVBq!ia0y~yU;;83893O0)X@p&(is>yI6Pe(Ln`LHy|pnjHC&`M z@#wk(XCnT@ zzVF|&pJ9b61B2KPj)qtL5BTSO)nmxwWMKFw&Zxk!ij$#%m8F4Ug&I(S00Yy4Squyj zz^H1-Vq!>O991xM!lAK~@xzPBLJa(Nz?d@v#vBVIqXDS}{R}%^9R6Li{qb_{hVFNt zzSea!Ffcrb&)a_c`Rb&vWxH#4-TwIUknDWvd#^+78SbgSV@QyIn6RLevEba#Z!3P! zFW$F=#o?=L{gXMLbr=}heseSwpPT+JeQxo-rK}EL@Bh8`{l7IZmHqH%X<&G9Lj|Zr z7?_-rCj+y-A22=j`2w9~0W{Rg44C8|jw%}NLEzxWVDpIs64k&$2~^O7DTjQ9n7p#~W!Hm(3=AoPh`RKJGJ~EKQ^WFlhNn_zA9%NJ23ETa N44$rjF6*2UngBaj#$^Bi literal 0 HcmV?d00001 diff --git a/test/fixtures/controller.bar/borderWidth/scriptable-object.js b/test/fixtures/controller.bar/borderWidth/scriptable-object.js new file mode 100644 index 00000000000..5bd2903efa7 --- /dev/null +++ b/test/fixtures/controller.bar/borderWidth/scriptable-object.js @@ -0,0 +1,54 @@ +module.exports = { + config: { + type: 'bar', + data: { + labels: [0, 1, 2, 3, 4, 5], + datasets: [ + { + // option in dataset + data: [0, 5, 10, null, -10, -5], + borderSkipped: false, + borderWidth: function(ctx) { + var value = ctx.dataset.data[ctx.dataIndex] || 0; + return {top: Math.abs(value)}; + } + }, + { + // option in element (fallback) + data: [0, 5, 10, null, -10, -5] + } + ] + }, + options: { + legend: false, + title: false, + elements: { + rectangle: { + backgroundColor: 'transparent', + borderColor: '#80808080', + borderSkipped: false, + borderWidth: function(ctx) { + return {left: ctx.dataIndex * 2}; + } + } + }, + scales: { + xAxes: [{display: false}], + yAxes: [ + { + display: false, + ticks: { + beginAtZero: true + } + } + ] + } + } + }, + options: { + canvas: { + height: 256, + width: 512 + } + } +}; diff --git a/test/fixtures/controller.bar/borderWidth/scriptable-object.png b/test/fixtures/controller.bar/borderWidth/scriptable-object.png new file mode 100644 index 0000000000000000000000000000000000000000..10e65f5ced56ec31adbd1f15db2e98ef96bd1f45 GIT binary patch literal 1640 zcmeAS@N?(olHy`uVBq!ia0y~yU;;83893O0)X@p&(is@o^gUf1Ln`LHy=K^z94^sv z(Q{6vpzv9-IaQqJ=4!n1ogr5Ka`KG}Y>Bscmxix6$GKEol3UjO;{ z@wDH6|6Mq6r1H*>r91ILMidh&K-dF-% z@J)u1fuS0xjA5M$P{jnGdPkrz8z%#U#4r?iu(0^t`{!2o?cevbQifFZ#z z%lP3E9{MVaLmLCbf>{hF1Q-}Xgc&567#gye6r33t0(==#fZ@4{vw??^!NHVqf+7RM z3N;2Jpkbq$s2mUs4fU1v_5a_-JU?!4ea_PU-|NG`y~#i|T?$_z%R znk#bW*LJKfXZn!B^k8f4H^p`LI1fzbY`FgIX8VpRbq0SmhPd15@{eB4Ww4pa08?z> zHJS|Qk{=ihJb;Cn0AFaA4j1DR-sJY;>jO0ZVTNPgg&ebxsLQ02i2Y AmH+?% literal 0 HcmV?d00001 diff --git a/test/fixtures/controller.bar/borderWidth/value.png b/test/fixtures/controller.bar/borderWidth/value.png index 3c81aff7f2dfb863751bc423a7c8db002e959871..af89232e9723602b3b02106ba12e409b2a81ac8b 100644 GIT binary patch literal 2064 zcmeAS@N?(olHy`uVBq!ia0y~yU;;83893O0)X@p&(is@ouY0;UhE&XXdwuW9m~e*H zz@zI9oRyfen$vuNoLW_Sz$&&oEk%v93t9c%y70^K_ZYtUdGlWOLC^I0GZWsG+t)Pw zpQs#yh8?>_y`{`U{V2MHzz1_px{rVQsS>+fWLp7VJ% z149G55YQnFj^|| zfq{(^==uW+j0y}45-OvL@q~u|`#Hsx-;dXBFFyD4$1^#3`HFt;Pv)Y)aDVU;sBHo; z1W)Z@bNHqBLx1*tR)=W}3=ixD8JH9t85pKiUSqI$UbFwnVK zFYkU|x%u~<&#Zh642%j4lYTQXENB7d(v!7}3;|9I91TzQF)(N-GO#!l#RC(O05CD_ z)MsGu;sB<$JK_ussw@o*9`Cq;)`HU0cUA@m&QW8AL)dI%dJyN%z+htuj7}C%>0EG| zq2f#P_sDmQ2POl{6bEoYzMYeSp&|>!F&Hh%sZp^lNat;^mIqdcVvGvF>hOyc1HTop gdY5wqW<~ek(#vKsK5(4q;K=|4p00i_>zopr0OWP4(f|Me literal 5109 zcmeHLX;c$g7JgM$>?DXLCZK{q2nq-;(8{JlN(IrDCCb_^ENY8(qoAN{0+kU36bBd^ zp^+tXP*6}on^q8k46?aE?1K!*8f6Cw$Sx2vuh7#!=kJ^|hx|#-SMPoA-tR8CucG;O z){1l$Isia%?;gto0HE+E0;r4OS4CW{7XYm1do6c32D%Nt-+$(K(F4lcl8&Nb;uAA) zyP(SJyQLntS(2T%ZH?}HeAT1>rS~0oU@*1Ty>vEGw21b+q{KotJwD*B=H4fh$@;pw zx&}w34s97P{*{VRTQr9o)N=ba(8@78TAM?gR%SOFL0J?w5$zBz8JbmkuA!mfBNmI( z#DHc>Dto30BkC#lzO5`x+3@epQTdXxzVmpCF6$-9?fE*oeRFyU}@3Q!yf4zAhKqxq3>Y-SS z3iL5HpEm3mqNqgqQJa_iStx%xbMlI!M>3MDg^$YQn%4s zbcLYB)M#inmfR=l*+JkoI1*)Mc0QV1bNF$#+YP*kK;K%RoA~R5Ko=y;bpD8`{|G5N z`auwC*&wt;d=qAI_kRRAGy0y5Q0k z^A9MPsh|@-B39{FL_-1 zVT2FV&pirVhpIx474NFQ*CDM6ZSFaN20-qBn%B)6F*cxxVk3Yc32NbYu75enQ*hj= zSX*!bp$J1UH{k&s66sC8{GGb}F|@6^PHn0c?(&c{&Ah@&)(XMJYc(3FdL(z2G;@;c zn@pg-O_tBau_U>~TA?$RwgkGoSTUr`#R+;$3z2SHewP7B+2$Q9=L2(MRWViY0w%|c z?>fiNks8D`ugs#ofeLlk&S{nwLWj3^9-TRW8AA5r8DtEU< zN6a0rhFPFxA0P>Efbt4{8PaD>-GrgqU>xqBO>(C^yVZ70bPBFI#rkZ)gMzUMjSX=; zZG$~HvU1BOn!6M9So>4q&>rDVNXj3JIW~V_(obVtY$+^{r5bS!uSpH?GsjZ24P0;} zY1>TXY&V&$an}a-F&jBT;Ej)0TaZ@uHd~mm3ZR??zqqwsfa>Uiur(O99Ohxt4TnTB zP@=Ut{YJEWDASr2eY1&{N>f{phj3fK#Ii3nN-$eBS2QPb^LF4!BcT%7>Edc7c1a5f4-XFx z3JRLJ*u8FO|7RJDsPJg)_DdoRR>;{`S=bPS4e4Yw8nwh61GxKS%E&#>ah<(k^2DgC zA@)YHsj0w_u&}V~slUtRF%1n3;_#PWW(MSLr=NACn%@9|Fp_F<)|LA7HTRb0IM^x? zyh0}_7w2$=!17n)RHpN|{BTWvn}6MO$a(~=fmzW3BAh~94Ih}Co{cU@27>D_yt`ZK zGcJEcj|@H%(-;K~q%pVhvX|*+j%QxyF zS&`qC|5@kb&nRXLG1ap!vC3aj|39iMba06sY3rG?Y>WFz|Gs5BXL;C`5bZ`N9$M1= zKfj?5CmMU;e*kQQTWW1x>m!xwg6k^NqjG4BM%YHZ%a52J{D#)6&!hmsHW>8ELR!vr z-qC|TO;fD1MhL13vFimn)(T(IXHCb_SsdRi*kMggJe@S0Z3b*r!?_Q&agPiF&0_|P zpZtcpgnaTtF)5o5RC=`U_rJJ$s`ylgeR(}b^@f=7$A{`0zM_3U$A;PwqEu4MmIW~v z#9YWsviScOWv*^Z073Y2zVpCqy}Fcps6@_Va&X}e+_kGr?|W{hnFEFM*r}EQG1qyl zOuWS_8(2PhwF-sPq5?JNu&dS7Z{0RZM76WoE=YD1@k~(2q4G#J#(O<4Ducsc&$)lj zQ|W_O@@6N%J=C=XL9wfp2&awrxwAHYnRNVkPKqW@;E#yBSC^e4(ofYoB2nH6n=vXw zSDE2ln0Iw}bIoshApXI*D*!<&&@#Oh&djg{;}(=#$hrUjf{@?Kj1S1&IyyHuH#Rad zq8yZlGWLJ#1aNYzs;X)p9UXP}+#nNoxVgLYF7*xNdU=)UjaXzQ?`|vZx2beQ?jD$2 z4I9=P%uX@KS{Fnc{;DQ34=g=#Xkxhh5}UwoX253C3#b1HnLSV&bp%`AweE%8OaTr< z)2QzUmHd*&a+)xqy$hq-%-0vU3=LfW7@j=iKk)4j<4E*B{s@k%-LUUp%=zQ(pJ#vmy8JyOIQI@n3LjpD1ce14 ft&4bu1OFfB`%joSi?^xoA;>aMS3j3^P6W2i)R*M+Z%ZfIVWtvURlqH5)vJneuEL*RSWJs{p z_k3(9wsaJu)6J|Bl+bm$E{hbEh}Bv&%T~=wd%gE|MxqjaxPQ)j&wJi;&hwm)1DP4i z<3>&%2>@|2sYDJCg^n1+MkDd4vVAka2%}80I7@Z*)9a*zQ?`s-_F_#%=bNJ<@8yXd zQPVHgvJa(RdwwBZXnD}VX`eOzgZy5}+t0Ns%B;#1jzUiU<5$|?30yRGIdQ1!>>bE0 z(Y1+TnyY{CYR^UhPE(OX#~c)5NismJom<60@Osxh9SVh3 z;wT5T!PQr%ZeOLQ8uS>L#3DFcgXC)t`3}*(Z63un^@#xPI?kKZ&H7LX_S?CY34ugU@TwWy6oJa9z z`4Tk)-#Cz0&#ltBP$DbJ_6yR)ELiPM7xWIsgGr80lUc0*Cjva#uJUNOVXu(d`b9tv zyJ&KJYSHGU8lsmaVTQm}&o9wU`2 zA}Kl?4u?=C(s{YR5%*sdM4_^KA`_37dM=}FhNS^j<3m61tkqSc*+erjFdqy>rN`&$ z0wG9b5!JaU?MjSqxrPj%KxNM=WRnll5|lD88MH-DY|FiF%cA~elRO(^zErb&KkSc1rk0xRm^hZ5W1RSXv9(?5^1Y+nii zxJjVhfSPAm_E8`;w}(q7P2EW+`Nv8R_kySSzO*{0OdX*TJ;rre-Pb+Y zv