From 81e1448836857dbfc2bcc01ca2b483f8bcb66da8 Mon Sep 17 00:00:00 2001 From: Ivan Samoylenko Date: Sun, 13 Mar 2016 18:58:36 +0300 Subject: [PATCH] Added 'borderSkipped' parameter to Rectangle. --- docs/00-Getting-Started.md | 1 + samples/bar.html | 3 +- src/controllers/controller.bar.js | 1 + src/elements/element.rectangle.js | 34 ++++++++++++++---- test/element.rectangle.tests.js | 57 +++++++++++++++++++++++++++++++ 5 files changed, 88 insertions(+), 8 deletions(-) diff --git a/docs/00-Getting-Started.md b/docs/00-Getting-Started.md index c9101b828..62f98be2e 100644 --- a/docs/00-Getting-Started.md +++ b/docs/00-Getting-Started.md @@ -236,6 +236,7 @@ rectangle | - | - | - *rectangle*.backgroundColor | Color | `Chart.defaults.global.defaultColor` | Default bar fill color *rectangle*.borderWidth | Number | 0 | Default bar stroke width *rectangle*.borderColor | Color | `Chart.defaults.global.defaultColor` | Default bar stroke color +*rectangle*.borderSkipped | String | 'bottom' | Default skipped (excluded) border for rectangle. Can be one of `bottom`, `left`, `top`, `right` If for example, you wanted all charts created to be responsive, and resize when the browser window does, the following setting can be changed: diff --git a/samples/bar.html b/samples/bar.html index 9bdb366ac..d595bae49 100644 --- a/samples/bar.html +++ b/samples/bar.html @@ -66,7 +66,8 @@ elements: { rectangle: { borderWidth: 2, - borderColor: 'rgb(0, 255, 0)' + borderColor: 'rgb(0, 255, 0)', + borderSkipped: 'bottom' } }, responsive: true, diff --git a/src/controllers/controller.bar.js b/src/controllers/controller.bar.js index 094292b53..9574ed2e3 100644 --- a/src/controllers/controller.bar.js +++ b/src/controllers/controller.bar.js @@ -116,6 +116,7 @@ module.exports = function(Chart) { base: reset ? yScalePoint : this.calculateBarBase(this.index, index), width: this.calculateBarWidth(numBars), backgroundColor: rectangle.custom && rectangle.custom.backgroundColor ? rectangle.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.getDataset().backgroundColor, index, this.chart.options.elements.rectangle.backgroundColor), + borderSkipped: rectangle.custom && rectangle.custom.borderSkipped ? rectangle.custom.borderSkipped : this.chart.options.elements.rectangle.borderSkipped, borderColor: rectangle.custom && rectangle.custom.borderColor ? rectangle.custom.borderColor : helpers.getValueAtIndexOrDefault(this.getDataset().borderColor, index, this.chart.options.elements.rectangle.borderColor), borderWidth: rectangle.custom && rectangle.custom.borderWidth ? rectangle.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.getDataset().borderWidth, index, this.chart.options.elements.rectangle.borderWidth) } diff --git a/src/elements/element.rectangle.js b/src/elements/element.rectangle.js index e3db40fb4..63f426688 100644 --- a/src/elements/element.rectangle.js +++ b/src/elements/element.rectangle.js @@ -7,7 +7,8 @@ module.exports = function(Chart) { Chart.defaults.global.elements.rectangle = { backgroundColor: Chart.defaults.global.defaultColor, borderWidth: 0, - borderColor: Chart.defaults.global.defaultColor + borderColor: Chart.defaults.global.defaultColor, + borderSkipped: 'bottom' }; Chart.elements.Rectangle = Chart.Element.extend({ @@ -36,12 +37,31 @@ module.exports = function(Chart) { ctx.strokeStyle = vm.borderColor; ctx.lineWidth = vm.borderWidth; - // It'd be nice to keep this class totally generic to any rectangle - // and simply specify which border to miss out. - ctx.moveTo(leftX, vm.base); - ctx.lineTo(leftX, top); - ctx.lineTo(rightX, top); - ctx.lineTo(rightX, vm.base); + // Corner points, from bottom-left to bottom-right clockwise + // | 1 2 | + // | 0 3 | + var corners = [ + [leftX, vm.base], + [leftX, top], + [rightX, top], + [rightX, vm.base] + ]; + + // Find first (starting) corner with fallback to 'bottom' + var borders = ['bottom', 'left', 'top', 'right']; + var startCorner = borders.indexOf(vm.borderSkipped, 0); + if (startCorner === -1) + startCorner = 0; + + function cornerAt(index) { + return corners[(startCorner + index) % 4]; + } + + // Draw rectangle from 'startCorner' + ctx.moveTo.apply(ctx, cornerAt(0)); + for (var i = 1; i < 4; i++) + ctx.lineTo.apply(ctx, cornerAt(i)); + ctx.fill(); if (vm.borderWidth) { ctx.stroke(); diff --git a/test/element.rectangle.tests.js b/test/element.rectangle.tests.js index cb1959b04..8c28970b1 100644 --- a/test/element.rectangle.tests.js +++ b/test/element.rectangle.tests.js @@ -242,5 +242,62 @@ describe('Rectangle element tests', function() { }]); }); + function testBorderSkipped (borderSkipped, expectedDrawCalls) { + var mockContext = window.createMockContext(); + var rectangle = new Chart.elements.Rectangle({ + _chart: { ctx: mockContext } + }); + + // Attach a view object as if we were the controller + rectangle._view = { + borderSkipped: borderSkipped, // set tested 'borderSkipped' parameter + ctx: mockContext, + base: 0, + width: 4, + x: 10, + y: 15, + }; + + rectangle.draw(); + + var drawCalls = rectangle._view.ctx.getCalls().splice(4, 4); + expect(drawCalls).toEqual(expectedDrawCalls); + } + + it ('should draw correctly respecting "borderSkipped" == "bottom"', function() { + testBorderSkipped ('bottom', [ + { name: 'moveTo', args: [8, 0] }, + { name: 'lineTo', args: [8, 15] }, + { name: 'lineTo', args: [12, 15] }, + { name: 'lineTo', args: [12, 0] }, + ]); + }); + + it ('should draw correctly respecting "borderSkipped" == "left"', function() { + testBorderSkipped ('left', [ + { name: 'moveTo', args: [8, 15] }, + { name: 'lineTo', args: [12, 15] }, + { name: 'lineTo', args: [12, 0] }, + { name: 'lineTo', args: [8, 0] }, + ]); + }); + + it ('should draw correctly respecting "borderSkipped" == "top"', function() { + testBorderSkipped ('top', [ + { name: 'moveTo', args: [12, 15] }, + { name: 'lineTo', args: [12, 0] }, + { name: 'lineTo', args: [8, 0] }, + { name: 'lineTo', args: [8, 15] }, + ]); + }); + + it ('should draw correctly respecting "borderSkipped" == "right"', function() { + testBorderSkipped ('right', [ + { name: 'moveTo', args: [12, 0] }, + { name: 'lineTo', args: [8, 0] }, + { name: 'lineTo', args: [8, 15] }, + { name: 'lineTo', args: [12, 15] }, + ]); + }); }); \ No newline at end of file