width: 500px;
margin-left: 40px;
margin-right: 40px;
+ box-sizing: content-box;
+ border: 1px solid rgb(240, 240, 240);
+ border-radius: 4px;
+ margin: 4px;
}
.container {
display: flex;
<div class="chart-container">
<canvas id="chart-title-left-end"></canvas>
</div>
+ <div class="chart-container">
+ <canvas id="chart-title-bottom-start"></canvas>
+ </div>
+ <div class="chart-container">
+ <canvas id="chart-title-bottom-center"></canvas>
+ </div>
+ <div class="chart-container">
+ <canvas id="chart-title-bottom-end"></canvas>
+ </div>
+ <div class="chart-container">
+ <canvas id="chart-title-right-start"></canvas>
+ </div>
+ <div class="chart-container">
+ <canvas id="chart-title-right-center"></canvas>
+ </div>
+ <div class="chart-container">
+ <canvas id="chart-title-right-end"></canvas>
+ </div>
</div>
<script>
var color = Chart.helpers.color;
legend: {
display: false
},
+ title: {
+ align: titleAlignment,
+ display: true,
+ position: titlePosition,
+ text: 'Title Position: ' + titlePosition + ', Align: ' + titleAlignment
+ }
},
scales: {
x: {
labelString: 'Value'
}
}
- },
- title: {
- align: titleAlignment,
- display: true,
- position: titlePosition,
- text: 'Title Position: ' + titlePosition + ', Align: ' + titleAlignment
}
}
};
titleAlignment: 'end',
titlePosition: 'left',
color: 'purple'
+ }, {
+ id: 'chart-title-bottom-start',
+ titleAlignment: 'start',
+ titlePosition: 'bottom',
+ color: 'red'
+ }, {
+ id: 'chart-title-bottom-center',
+ titleAlignment: 'center',
+ titlePosition: 'bottom',
+ color: 'orange'
+ }, {
+ id: 'chart-title-bottom-end',
+ titleAlignment: 'end',
+ titlePosition: 'bottom',
+ color: 'yellow'
+ }, {
+ id: 'chart-title-right-start',
+ titleAlignment: 'start',
+ titlePosition: 'right',
+ color: 'green'
+ }, {
+ id: 'chart-title-right-center',
+ titleAlignment: 'center',
+ titlePosition: 'right',
+ color: 'blue'
+ }, {
+ id: 'chart-title-right-end',
+ titleAlignment: 'end',
+ titlePosition: 'right',
+ color: 'purple'
}].forEach(function(details) {
var ctx = document.getElementById(details.id).getContext('2d');
var config = createConfig(details.titlePosition, details.titleAlignment, details.color);
-import defaults from '../core/core.defaults';
import Element from '../core/core.element';
import layouts from '../core/core.layouts';
-import {PI, isArray, mergeIf, toPadding, toFont} from '../helpers';
+import {PI, isArray, toPadding, toFont} from '../helpers';
+
+const toLeftRightCenter = (align) => align === 'start' ? 'left' : align === 'end' ? 'right' : 'center';
+const alignStartEnd = (align, start, end) => align === 'start' ? start : align === 'end' ? end : (start + end) / 2;
export class Title extends Element {
constructor(config) {
this.fullWidth = undefined;
}
- // These methods are ordered by lifecycle. Utilities then follow.
-
-
- beforeUpdate() {}
-
update(maxWidth, maxHeight, margins) {
const me = this;
- // Update Lifecycle - Probably don't want to ever extend or overwrite this function ;)
- me.beforeUpdate();
-
- // Absorb the master measurements
me.maxWidth = maxWidth;
me.maxHeight = maxHeight;
me._margins = margins;
- // Dimensions
- me.beforeSetDimensions();
me.setDimensions();
- me.afterSetDimensions();
- // Labels
- me.beforeBuildLabels();
- me.buildLabels();
- me.afterBuildLabels();
-
- // Fit
- me.beforeFit();
- me.fit();
- me.afterFit();
- //
- me.afterUpdate();
+ me.fit();
}
- afterUpdate() {}
-
-
- beforeSetDimensions() {}
-
setDimensions() {
const me = this;
// Set the unconstrained dimension before label rotation
}
}
- afterSetDimensions() {}
-
- beforeBuildLabels() {}
-
- buildLabels() {}
-
- afterBuildLabels() {}
-
- beforeFit() {}
-
fit() {
const me = this;
const opts = me.options;
me.height = minSize.height = isHorizontal ? textSize : me.maxHeight;
}
- afterFit() {}
-
- // Shared Methods
isHorizontal() {
const pos = this.options.position;
return pos === 'top' || pos === 'bottom';
}
- // Actually draw the title block on the canvas
+ _drawArgs(offset) {
+ const {top, left, bottom, right, options} = this;
+ const align = options.align;
+ let rotation = 0;
+ let maxWidth, titleX, titleY;
+
+ if (this.isHorizontal()) {
+ titleX = alignStartEnd(align, left, right);
+ titleY = top + offset;
+ maxWidth = right - left;
+ } else {
+ if (options.position === 'left') {
+ titleX = left + offset;
+ titleY = alignStartEnd(align, bottom, top);
+ rotation = PI * -0.5;
+ } else {
+ titleX = right - offset;
+ titleY = alignStartEnd(align, top, bottom);
+ rotation = PI * 0.5;
+ }
+ maxWidth = bottom - top;
+ }
+ return {titleX, titleY, maxWidth, rotation};
+ }
+
draw() {
const me = this;
const ctx = me.ctx;
const fontOpts = toFont(opts.font, me.chart.options.font);
const lineHeight = fontOpts.lineHeight;
const offset = lineHeight / 2 + me._padding.top;
- let rotation = 0;
- const top = me.top;
- const left = me.left;
- const bottom = me.bottom;
- const right = me.right;
- let maxWidth, titleX, titleY;
- let align;
-
- // Horizontal
- if (me.isHorizontal()) {
- switch (opts.align) {
- case 'start':
- titleX = left;
- align = 'left';
- break;
- case 'end':
- titleX = right;
- align = 'right';
- break;
- default:
- titleX = left + ((right - left) / 2);
- align = 'center';
- break;
- }
-
- titleY = top + offset;
- maxWidth = right - left;
- } else {
- titleX = opts.position === 'left' ? left + offset : right - offset;
-
- switch (opts.align) {
- case 'start':
- titleY = opts.position === 'left' ? bottom : top;
- align = 'left';
- break;
- case 'end':
- titleY = opts.position === 'left' ? top : bottom;
- align = 'right';
- break;
- default:
- titleY = top + ((bottom - top) / 2);
- align = 'center';
- break;
- }
- maxWidth = bottom - top;
- rotation = PI * (opts.position === 'left' ? -0.5 : 0.5);
- }
+ const {titleX, titleY, maxWidth, rotation} = me._drawArgs(offset);
ctx.save();
ctx.translate(titleX, titleY);
ctx.rotate(rotation);
- ctx.textAlign = align;
+ ctx.textAlign = toLeftRightCenter(opts.align);
ctx.textBaseline = 'middle';
const text = opts.text;
}
}
-function createNewTitleBlockAndAttach(chart, titleOpts) {
+function createTitle(chart, titleOpts) {
const title = new Title({
ctx: chart.ctx,
options: titleOpts,
chart.titleBlock = title;
}
+function removeTitle(chart) {
+ const title = chart.titleBlock;
+ if (title) {
+ layouts.removeBox(chart, title);
+ delete chart.titleBlock;
+ }
+}
+
+function createOrUpdateTitle(chart, options) {
+ const title = chart.titleBlock;
+ if (title) {
+ layouts.configure(chart, title, options);
+ title.options = options;
+ } else {
+ createTitle(chart, options);
+ }
+}
+
export default {
id: 'title',
/**
- * Backward compatibility: since 2.1.5, the title is registered as a plugin, making
- * Chart.Title obsolete. To avoid a breaking change, we export the Title as part of
- * the plugin, which one will be re-exposed in the chart.js file.
- * https://github.com/chartjs/Chart.js/pull/2640
+ * For tests
* @private
*/
_element: Title,
- beforeInit(chart) {
- const titleOpts = chart.options.plugins.title;
-
- if (titleOpts) {
- createNewTitleBlockAndAttach(chart, titleOpts);
- }
+ beforeInit(chart, options) {
+ createTitle(chart, options);
},
- beforeUpdate(chart) {
- const titleOpts = chart.options.plugins.title;
- const titleBlock = chart.titleBlock;
-
- if (titleOpts) {
- mergeIf(titleOpts, defaults.plugins.title);
-
- if (titleBlock) {
- layouts.configure(chart, titleBlock, titleOpts);
- titleBlock.options = titleOpts;
- } else {
- createNewTitleBlockAndAttach(chart, titleOpts);
- }
- } else if (titleBlock) {
- layouts.removeBox(chart, titleBlock);
- delete chart.titleBlock;
+ beforeUpdate(chart, args, options) {
+ if (options === false) {
+ removeTitle(chart);
+ } else {
+ createOrUpdateTitle(chart, options);
}
},