diff --git a/docs/options.md b/docs/options.md index 33ae04c..9639bb4 100644 --- a/docs/options.md +++ b/docs/options.md @@ -16,6 +16,7 @@ Available options: | `borderColor` | [`Style`](#style-options)/`null` | Yes | Yes | `null` | `borderRadius` | `Number` | Yes | Yes | `0` | `borderWidth` | `Number` | Yes | Yes | `0` +| [`clip`](positioning.md#visibility) | `Boolean` | Yes | Yes | `false` | `color` | [`Style`](#style-options) | Yes | Yes | `0` | [`display`](positioning.md#visibility) | `Boolean` | Yes | Yes | `true` | `font` | `Object` | Yes | Yes | - diff --git a/docs/positioning.md b/docs/positioning.md index b4ca7d9..496b2ca 100644 --- a/docs/positioning.md +++ b/docs/positioning.md @@ -39,3 +39,5 @@ display: function(context) { return context.dataIndex % 2; // display labels with an odd index } ``` + +When the `clip` option is `true`, the part of the label which is outside the chart area will be masked (see [CanvasRenderingContext2D.clip()](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/clip)) diff --git a/gulpfile.js b/gulpfile.js index bc7265e..9d74231 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -86,7 +86,7 @@ gulp.task('lint', function() { gulp.task('docs', function(done) { var script = require.resolve('gitbook-cli/bin/gitbook.js'); var out = path.join(argv.output, argv.docsDir); - var cmd = process.execPath; + var cmd = '"' + process.execPath + '"'; exec([cmd, script, 'install', './'].join(' ')).then(() => { return exec([cmd, script, argv.watch ? 'serve' : 'build', './', out].join(' ')); diff --git a/src/defaults.js b/src/defaults.js index b9f3a72..2bdf445 100644 --- a/src/defaults.js +++ b/src/defaults.js @@ -59,6 +59,13 @@ export default { */ borderWidth: 0, + /** + * Clip the label drawing to the chart area. + * @member {Boolean|Array|Function} + * @default false (no clipping) + */ + clip: false, + /** * The color used to draw the label text. * @member {String|Array|Function} diff --git a/src/label.js b/src/label.js index d0ac9c8..9998b10 100644 --- a/src/label.js +++ b/src/label.js @@ -195,6 +195,7 @@ helpers.extend(Label.prototype, { borderColor: resolve([config.borderColor, null], context, index), borderRadius: resolve([config.borderRadius, 0], context, index), borderWidth: resolve([config.borderWidth, 0], context, index), + clip: resolve([config.clip, false], context, index), color: resolve([config.color, Chart.defaults.global.defaultFontColor], context, index), font: font, lines: lines, @@ -226,10 +227,11 @@ helpers.extend(Label.prototype, { me._model = model; }, - draw: function(ctx) { + draw: function(chart) { var me = this; + var ctx = chart.ctx; var model = me._model; - var rects, center; + var rects, center, area; if (!model || !model.opacity) { return; @@ -240,6 +242,18 @@ helpers.extend(Label.prototype, { me._hitbox.update(center, rects.frame, model.rotation); ctx.save(); + + if (model.clip) { + area = chart.chartArea; + ctx.beginPath(); + ctx.rect( + area.left, + area.top, + area.right - area.left, + area.bottom - area.top); + ctx.clip(); + } + ctx.globalAlpha = utils.bound(0, model.opacity, 1); ctx.translate(Math.round(center.x), Math.round(center.y)); ctx.rotate(model.rotation); diff --git a/src/plugin.js b/src/plugin.js index 5f12d78..46d3e9a 100644 --- a/src/plugin.js +++ b/src/plugin.js @@ -38,7 +38,7 @@ function drawLabels(chart, datasetIndex) { el = elements[i]; label = el[EXPANDO_KEY]; if (label) { - label.draw(chart.ctx); + label.draw(chart); } } } diff --git a/test/fixtures/options.clip/clip-indexable.js b/test/fixtures/options.clip/clip-indexable.js new file mode 100644 index 0000000..041ace3 --- /dev/null +++ b/test/fixtures/options.clip/clip-indexable.js @@ -0,0 +1,75 @@ +var data = [ + {x: 0, y: 0}, + {x: 0, y: 1}, + {x: 0, y: 2}, + {x: 1, y: 0}, + {x: 1, y: 1}, + {x: 1, y: 2}, + {x: 2, y: 0}, + {x: 2, y: 1}, + {x: 2, y: 2} +]; + +export default { + config: { + type: 'bubble', + data: { + datasets: [{ + data: data, + datalabels: { + backgroundColor: '#ff0077', + borderColor: 'white', + padding: 32, + clip: [ + false, + true, + false, + true, + false, + true, + false, + true, + false + ] + } + }, { + data: data, + datalabels: { + backgroundColor: '#00ff77', + borderColor: 'black', + padding: 16, + clip: [ + true, + false, + true, + false, + true, + false, + true, + false, + true, + ] + } + }] + }, + options: { + layout: { + padding: 48 + }, + plugins: { + datalabels: { + borderWidth: 4, + font: { + size: 0 + } + } + } + } + }, + options: { + canvas: { + height: 256, + width: 256 + } + } +}; diff --git a/test/fixtures/options.clip/clip-indexable.png b/test/fixtures/options.clip/clip-indexable.png new file mode 100644 index 0000000..bffd358 Binary files /dev/null and b/test/fixtures/options.clip/clip-indexable.png differ diff --git a/test/fixtures/options.clip/clip-scriptable.js b/test/fixtures/options.clip/clip-scriptable.js new file mode 100644 index 0000000..fbbcaf9 --- /dev/null +++ b/test/fixtures/options.clip/clip-scriptable.js @@ -0,0 +1,56 @@ +var data = [ + {x: 0, y: 0}, + {x: 0, y: 1}, + {x: 0, y: 2}, + {x: 1, y: 0}, + {x: 1, y: 1}, + {x: 1, y: 2}, + {x: 2, y: 0}, + {x: 2, y: 1}, + {x: 2, y: 2} +]; + +export default { + config: { + type: 'bubble', + data: { + datasets: [{ + data: data, + datalabels: { + backgroundColor: '#ff0077', + borderColor: 'white', + padding: 32 + } + }, { + data: data, + datalabels: { + backgroundColor: '#00ff77', + borderColor: 'black', + padding: 16 + } + }] + }, + options: { + layout: { + padding: 48 + }, + plugins: { + datalabels: { + borderWidth: 4, + clip: function(ctx) { + return (ctx.dataIndex + ctx.datasetIndex) % 2 === 0; + }, + font: { + size: 0 + } + } + } + } + }, + options: { + canvas: { + height: 256, + width: 256 + } + } +}; diff --git a/test/fixtures/options.clip/clip-scriptable.png b/test/fixtures/options.clip/clip-scriptable.png new file mode 100644 index 0000000..27a7d3e Binary files /dev/null and b/test/fixtures/options.clip/clip-scriptable.png differ diff --git a/test/fixtures/options.clip/clip-values.js b/test/fixtures/options.clip/clip-values.js new file mode 100644 index 0000000..027f013 --- /dev/null +++ b/test/fixtures/options.clip/clip-values.js @@ -0,0 +1,55 @@ +var data = [ + {x: 0, y: 0}, + {x: 0, y: 1}, + {x: 0, y: 2}, + {x: 1, y: 0}, + {x: 1, y: 1}, + {x: 1, y: 2}, + {x: 2, y: 0}, + {x: 2, y: 1}, + {x: 2, y: 2} +]; + +export default { + config: { + type: 'bubble', + data: { + datasets: [{ + data: data, + datalabels: { + backgroundColor: '#ff0077', + borderColor: 'white', + clip: true, + padding: 32 + } + }, { + data: data, + datalabels: { + backgroundColor: '#00ff77', + borderColor: 'black', + clip: false, + padding: 16 + } + }] + }, + options: { + layout: { + padding: 48 + }, + plugins: { + datalabels: { + borderWidth: 4, + font: { + size: 0 + } + } + } + } + }, + options: { + canvas: { + height: 256, + width: 256 + } + } +}; diff --git a/test/fixtures/options.clip/clip-values.png b/test/fixtures/options.clip/clip-values.png new file mode 100644 index 0000000..74ddc0b Binary files /dev/null and b/test/fixtures/options.clip/clip-values.png differ diff --git a/test/plugins.js b/test/plugins.js new file mode 100644 index 0000000..355f62f --- /dev/null +++ b/test/plugins.js @@ -0,0 +1,20 @@ +export default { + 'jasmine-chart-area': { + afterDraw: function(chart) { + var ctx = chart.ctx; + var area = chart.chartArea; + + ctx.save(); + ctx.beginPath(); + ctx.rect( + area.left, + area.top, + area.right - area.left, + area.bottom - area.top); + ctx.strokeStyle = 'blue'; + ctx.lineWidth = 2; + ctx.stroke(); + ctx.restore(); + } + } +}; diff --git a/test/specs/options.spec.js b/test/specs/options.spec.js index 7ff9efd..486862b 100644 --- a/test/specs/options.spec.js +++ b/test/specs/options.spec.js @@ -2,6 +2,7 @@ describe('options (scriptable)', function() { [ 'align', 'anchor', + 'clip', 'opacity' ].forEach(function(key) { it(key + ' should be called with a valid context', function() { @@ -48,6 +49,10 @@ describe('option.align', function() { describe('auto', jasmine.fixture.specs('options.align')); }); +describe('option.clip', function() { + describe('auto', jasmine.fixture.specs('options.clip')); +}); + describe('option.anchor', function() { describe('auto', jasmine.fixture.specs('options.anchor')); });