mirror of
https://github.com/chartjs/Chart.js.git
synced 2026-03-04 15:34:04 +01:00
Split legend and title block functionality into 2 separate blocks. This allows the title to be on the top of the chart while the legend is on the bottom
This commit is contained in:
@@ -72,11 +72,11 @@
|
||||
responsive: true,
|
||||
legend: {
|
||||
position: 'top',
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Our 3 Favorite Datasets'
|
||||
}
|
||||
},
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Our 3 Favorite Datasets'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -107,11 +107,11 @@
|
||||
responsive: true,
|
||||
legend: {
|
||||
position: 'top',
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Our Favorite Datasets'
|
||||
}
|
||||
},
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Our Favorite Datasets'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -61,11 +61,7 @@
|
||||
options: {
|
||||
responsive: true,
|
||||
legend: {
|
||||
position: 'top',
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Our 4 Favorite Datasets'
|
||||
}
|
||||
position: 'bottom',
|
||||
},
|
||||
hover: {
|
||||
mode: 'label'
|
||||
@@ -85,6 +81,10 @@
|
||||
labelString: 'Value'
|
||||
}
|
||||
}]
|
||||
},
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Our 4 Favorite Datasets'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -62,10 +62,10 @@
|
||||
responsive: true,
|
||||
legend: {
|
||||
position: 'top',
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Our Favorite Dataset'
|
||||
}
|
||||
},
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Our Favorite Dataset'
|
||||
},
|
||||
scale: {
|
||||
ticks: {
|
||||
|
||||
@@ -58,13 +58,13 @@
|
||||
options: {
|
||||
legend: {
|
||||
position: 'top',
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Our 3 Favorite Datasets'
|
||||
}
|
||||
},
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Our 3 Favorite Datasets'
|
||||
},
|
||||
scale: {
|
||||
reverse: true,
|
||||
reverse: false,
|
||||
ticks: {
|
||||
beginAtZero: true
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
this.ensureScalesHaveIDs();
|
||||
this.buildOrUpdateControllers();
|
||||
this.buildScales();
|
||||
this.buildLegends();
|
||||
this.buildSurroundingItems();
|
||||
this.updateLayout();
|
||||
this.resetElements();
|
||||
this.initToolTip();
|
||||
@@ -170,18 +170,26 @@
|
||||
Chart.scaleService.addScalesToLayout(this);
|
||||
},
|
||||
|
||||
buildLegends: function() {
|
||||
if (!this.options.legend) {
|
||||
return;
|
||||
buildSurroundingItems: function() {
|
||||
if (this.options.title) {
|
||||
this.titleBlock = new Chart.Title({
|
||||
ctx: this.chart.ctx,
|
||||
options: this.options.title,
|
||||
chart: this
|
||||
});
|
||||
|
||||
Chart.layoutService.addBox(this, this.titleBlock);
|
||||
}
|
||||
|
||||
this.legend = new Chart.Legend({
|
||||
ctx: this.chart.ctx,
|
||||
options: this.options.legend,
|
||||
chart: this,
|
||||
});
|
||||
if (this.options.legend) {
|
||||
this.legend = new Chart.Legend({
|
||||
ctx: this.chart.ctx,
|
||||
options: this.options.legend,
|
||||
chart: this,
|
||||
});
|
||||
|
||||
Chart.layoutService.addBox(this, this.legend);
|
||||
Chart.layoutService.addBox(this, this.legend);
|
||||
}
|
||||
},
|
||||
|
||||
updateLayout: function() {
|
||||
|
||||
@@ -12,21 +12,6 @@
|
||||
fullWidth: true, // marks that this box should take the full width of the canvas (pushing down other boxes)
|
||||
onClick: false, // a callback will override the default behavior of toggling the datasets
|
||||
|
||||
title: {
|
||||
position: 'top',
|
||||
fontColor: '#666',
|
||||
fontFamily: 'Helvetica Neue',
|
||||
fontSize: 12,
|
||||
fontStyle: 'bold',
|
||||
padding: 10,
|
||||
|
||||
// actual title
|
||||
text: '',
|
||||
|
||||
// display property
|
||||
display: false,
|
||||
},
|
||||
|
||||
labels: {
|
||||
boxWidth: 40,
|
||||
fontSize: 12,
|
||||
@@ -136,7 +121,6 @@
|
||||
fit: function() {
|
||||
|
||||
var ctx = this.ctx;
|
||||
var titleFont = helpers.fontString(this.options.title.fontSize, this.options.title.fontStyle, this.options.title.fontFamily);
|
||||
var labelFont = helpers.fontString(this.options.labels.fontSize, this.options.labels.fontStyle, this.options.labels.fontFamily);
|
||||
|
||||
// Reset hit boxes
|
||||
@@ -158,12 +142,6 @@
|
||||
|
||||
// Increase sizes here
|
||||
if (this.isHorizontal()) {
|
||||
|
||||
// Title
|
||||
if (this.options.title.display) {
|
||||
this.minSize.height += this.options.title.fontSize + (this.options.title.padding * 2);
|
||||
}
|
||||
|
||||
// Labels
|
||||
|
||||
// Width of each line of legend boxes. Labels wrap onto multiple lines when there are too many to fit on one
|
||||
@@ -215,7 +193,7 @@
|
||||
var ctx = this.ctx;
|
||||
var cursor = {
|
||||
x: this.left + ((this.width - this.lineWidths[0]) / 2),
|
||||
y: this.top,
|
||||
y: this.top + this.options.labels.padding,
|
||||
line: 0,
|
||||
};
|
||||
|
||||
@@ -223,17 +201,6 @@
|
||||
|
||||
// Horizontal
|
||||
if (this.isHorizontal()) {
|
||||
|
||||
// Title Spacing if on top
|
||||
if (this.options.title.display) {
|
||||
if (this.options.title.position === 'top') {
|
||||
cursor.y += this.options.title.fontSize + (this.options.title.padding * 2);
|
||||
} else {
|
||||
// bottom
|
||||
cursor.y += this.options.labels.padding;
|
||||
}
|
||||
}
|
||||
|
||||
// Labels
|
||||
ctx.textAlign = "left";
|
||||
ctx.textBaseline = 'top';
|
||||
@@ -299,49 +266,8 @@
|
||||
|
||||
cursor.x += width + (this.options.labels.padding);
|
||||
}, this);
|
||||
|
||||
// Title Spacing if on bottom
|
||||
if (this.options.title.display && this.options.title.position === 'bottom') {
|
||||
cursor.y += this.options.title.fontSize + (this.options.title.padding * 2);
|
||||
}
|
||||
|
||||
// Title
|
||||
if (this.options.title.display) {
|
||||
|
||||
ctx.textAlign = "center";
|
||||
ctx.textBaseline = 'middle';
|
||||
ctx.fillStyle = this.options.title.fontColor; // render in correct colour
|
||||
ctx.font = helpers.fontString(this.options.title.fontSize, this.options.title.fontStyle, this.options.title.fontFamily);
|
||||
|
||||
var titleX = this.left + ((this.right - this.left) / 2); // midpoint of the width
|
||||
var titleY = this.options.title.position == 'bottom' ? this.bottom - (this.options.title.fontSize / 2) - this.options.title.padding : this.top + (this.options.title.fontSize / 2) + this.options.title.padding;
|
||||
|
||||
ctx.fillText(this.options.title.text, titleX, titleY);
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
|
||||
// Title
|
||||
if (this.options.title.display) {
|
||||
|
||||
// Draw the legend label
|
||||
titleX = this.options.position == 'left' ? this.left + (this.options.title.fontSize / 2) : this.right - (this.options.title.fontSize / 2);
|
||||
titleY = this.top + ((this.bottom - this.top) / 2);
|
||||
var rotation = this.options.position == 'left' ? -0.5 * Math.PI : 0.5 * Math.PI;
|
||||
|
||||
ctx.save();
|
||||
ctx.translate(titleX, titleY);
|
||||
ctx.rotate(rotation);
|
||||
ctx.textAlign = "center";
|
||||
ctx.fillStyle = this.options.title.fontColor; // render in correct colour
|
||||
ctx.font = helpers.fontString(this.options.title.fontSize, this.options.title.fontStyle, this.options.title.fontFamily);
|
||||
ctx.textBaseline = 'middle';
|
||||
ctx.fillText(this.options.title.text, 0, 0);
|
||||
ctx.restore();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -363,7 +289,6 @@
|
||||
this.chart.data.datasets[i].hidden = !this.chart.data.datasets[i].hidden;
|
||||
|
||||
// We hid a dataset ... rerender the chart
|
||||
//this.chart.render();
|
||||
this.chart.update();
|
||||
break;
|
||||
}
|
||||
|
||||
196
src/core/core.title.js
Normal file
196
src/core/core.title.js
Normal file
@@ -0,0 +1,196 @@
|
||||
(function() {
|
||||
"use strict";
|
||||
|
||||
var root = this,
|
||||
Chart = root.Chart,
|
||||
helpers = Chart.helpers;
|
||||
|
||||
Chart.defaults.title = {
|
||||
|
||||
display: true,
|
||||
position: 'top',
|
||||
fullWidth: true, // marks that this box should take the full width of the canvas (pushing down other boxes)
|
||||
|
||||
fontColor: '#666',
|
||||
fontFamily: 'Helvetica Neue',
|
||||
fontSize: 12,
|
||||
fontStyle: 'bold',
|
||||
padding: 10,
|
||||
|
||||
// actual title
|
||||
text: '',
|
||||
};
|
||||
|
||||
Chart.Title = Chart.Element.extend({
|
||||
|
||||
initialize: function(config) {
|
||||
helpers.extend(this, config);
|
||||
this.options = helpers.configMerge(Chart.defaults.title, config.options);
|
||||
|
||||
// Contains hit boxes for each dataset (in dataset order)
|
||||
this.legendHitBoxes = [];
|
||||
},
|
||||
|
||||
// These methods are ordered by lifecyle. Utilities then follow.
|
||||
|
||||
beforeUpdate: helpers.noop,
|
||||
update: function(maxWidth, maxHeight, margins) {
|
||||
|
||||
// Update Lifecycle - Probably don't want to ever extend or overwrite this function ;)
|
||||
this.beforeUpdate();
|
||||
|
||||
// Absorb the master measurements
|
||||
this.maxWidth = maxWidth;
|
||||
this.maxHeight = maxHeight;
|
||||
this.margins = margins;
|
||||
|
||||
// Dimensions
|
||||
this.beforeSetDimensions();
|
||||
this.setDimensions();
|
||||
this.afterSetDimensions();
|
||||
// Labels
|
||||
this.beforeBuildLabels();
|
||||
this.buildLabels();
|
||||
this.afterBuildLabels();
|
||||
|
||||
// Fit
|
||||
this.beforeFit();
|
||||
this.fit();
|
||||
this.afterFit();
|
||||
//
|
||||
this.afterUpdate();
|
||||
|
||||
return this.minSize;
|
||||
|
||||
},
|
||||
afterUpdate: helpers.noop,
|
||||
|
||||
//
|
||||
|
||||
beforeSetDimensions: helpers.noop,
|
||||
setDimensions: function() {
|
||||
// Set the unconstrained dimension before label rotation
|
||||
if (this.isHorizontal()) {
|
||||
// Reset position before calculating rotation
|
||||
this.width = this.maxWidth;
|
||||
this.left = 0;
|
||||
this.right = this.width;
|
||||
} else {
|
||||
this.height = this.maxHeight;
|
||||
|
||||
// Reset position before calculating rotation
|
||||
this.top = 0;
|
||||
this.bottom = this.height;
|
||||
}
|
||||
|
||||
// Reset padding
|
||||
this.paddingLeft = 0;
|
||||
this.paddingTop = 0;
|
||||
this.paddingRight = 0;
|
||||
this.paddingBottom = 0;
|
||||
|
||||
// Reset minSize
|
||||
this.minSize = {
|
||||
width: 0,
|
||||
height: 0,
|
||||
};
|
||||
},
|
||||
afterSetDimensions: helpers.noop,
|
||||
|
||||
//
|
||||
|
||||
beforeBuildLabels: helpers.noop,
|
||||
buildLabels: helpers.noop,
|
||||
afterBuildLabels: helpers.noop,
|
||||
|
||||
//
|
||||
|
||||
beforeFit: helpers.noop,
|
||||
fit: function() {
|
||||
|
||||
var ctx = this.ctx;
|
||||
var titleFont = helpers.fontString(this.options.fontSize, this.options.fontStyle, this.options.fontFamily);
|
||||
|
||||
// Width
|
||||
if (this.isHorizontal()) {
|
||||
this.minSize.width = this.maxWidth; // fill all the width
|
||||
} else {
|
||||
this.minSize.width = 0;
|
||||
}
|
||||
|
||||
// height
|
||||
if (this.isHorizontal()) {
|
||||
this.minSize.height = 0;
|
||||
} else {
|
||||
this.minSize.height = this.maxHeight; // fill all the height
|
||||
}
|
||||
|
||||
// Increase sizes here
|
||||
if (this.isHorizontal()) {
|
||||
|
||||
// Title
|
||||
if (this.options.display) {
|
||||
this.minSize.height += this.options.fontSize + (this.options.padding * 2);
|
||||
}
|
||||
} else {
|
||||
// TODO vertical
|
||||
}
|
||||
|
||||
this.width = this.minSize.width;
|
||||
this.height = this.minSize.height;
|
||||
|
||||
},
|
||||
afterFit: helpers.noop,
|
||||
|
||||
// Shared Methods
|
||||
isHorizontal: function() {
|
||||
return this.options.position == "top" || this.options.position == "bottom";
|
||||
},
|
||||
|
||||
// Actualy draw the title block on the canvas
|
||||
draw: function() {
|
||||
if (this.options.display) {
|
||||
var ctx = this.ctx;
|
||||
var titleX, titleY;
|
||||
|
||||
// Horizontal
|
||||
if (this.isHorizontal()) {
|
||||
// Title
|
||||
if (this.options.display) {
|
||||
|
||||
ctx.textAlign = "center";
|
||||
ctx.textBaseline = 'middle';
|
||||
ctx.fillStyle = this.options.fontColor; // render in correct colour
|
||||
ctx.font = helpers.fontString(this.options.fontSize, this.options.fontStyle, this.options.fontFamily);
|
||||
|
||||
titleX = this.left + ((this.right - this.left) / 2); // midpoint of the width
|
||||
titleY = this.top + ((this.bottom - this.top) / 2); // midpoint of the height
|
||||
|
||||
ctx.fillText(this.options.text, titleX, titleY);
|
||||
}
|
||||
} else {
|
||||
|
||||
// Title
|
||||
if (this.options.display) {
|
||||
titleX = this.options.position == 'left' ? this.left + (this.options.fontSize / 2) : this.right - (this.options.fontSize / 2);
|
||||
titleY = this.top + ((this.bottom - this.top) / 2);
|
||||
var rotation = this.options.position == 'left' ? -0.5 * Math.PI : 0.5 * Math.PI;
|
||||
|
||||
ctx.save();
|
||||
ctx.translate(titleX, titleY);
|
||||
ctx.rotate(rotation);
|
||||
ctx.textAlign = "center";
|
||||
ctx.fillStyle = this.options.fontColor; // render in correct colour
|
||||
ctx.font = helpers.fontString(this.options.fontSize, this.options.fontStyle, this.options.fontFamily);
|
||||
ctx.textBaseline = 'middle';
|
||||
ctx.fillText(this.options.text, 0, 0);
|
||||
ctx.restore();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}).call(this);
|
||||
Reference in New Issue
Block a user