mirror of
https://github.com/chartjs/Chart.js.git
synced 2025-12-08 20:36:08 +00:00
Implement layers (z-index) for layout items (#6241)
This commit is contained in:
parent
95b9953922
commit
2a96d83c2c
@ -23,6 +23,7 @@ The grid line configuration is nested under the scale configuration in the `grid
|
|||||||
| `zeroLineBorderDash` | `number[]` | `[]` | Length and spacing of dashes of the grid line for the first index (index 0). See [MDN](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/setLineDash).
|
| `zeroLineBorderDash` | `number[]` | `[]` | Length and spacing of dashes of the grid line for the first index (index 0). See [MDN](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/setLineDash).
|
||||||
| `zeroLineBorderDashOffset` | `number` | `0.0` | Offset for line dashes of the grid line for the first index (index 0). See [MDN](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/lineDashOffset).
|
| `zeroLineBorderDashOffset` | `number` | `0.0` | Offset for line dashes of the grid line for the first index (index 0). See [MDN](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/lineDashOffset).
|
||||||
| `offsetGridLines` | `boolean` | `false` | If true, grid lines will be shifted to be between labels. This is set to `true` for a category scale in a bar chart by default.
|
| `offsetGridLines` | `boolean` | `false` | If true, grid lines will be shifted to be between labels. This is set to `true` for a category scale in a bar chart by default.
|
||||||
|
| `z` | `number` | `0` | z-index of gridline layer. Values <= 0 are drawn under datasets, > 0 on top.
|
||||||
|
|
||||||
## Tick Configuration
|
## Tick Configuration
|
||||||
The tick configuration is nested under the scale configuration in the `ticks` key. It defines options for the tick marks that are generated by the axis.
|
The tick configuration is nested under the scale configuration in the `ticks` key. It defines options for the tick marks that are generated by the axis.
|
||||||
@ -40,6 +41,7 @@ The tick configuration is nested under the scale configuration in the `ticks` ke
|
|||||||
| `minor` | `object` | `{}` | Minor ticks configuration. Omitted options are inherited from options above.
|
| `minor` | `object` | `{}` | Minor ticks configuration. Omitted options are inherited from options above.
|
||||||
| `major` | `object` | `{}` | Major ticks configuration. Omitted options are inherited from options above.
|
| `major` | `object` | `{}` | Major ticks configuration. Omitted options are inherited from options above.
|
||||||
| `padding` | `number` | `0` | Sets the offset of the tick labels from the axis
|
| `padding` | `number` | `0` | Sets the offset of the tick labels from the axis
|
||||||
|
| `z` | `number` | `0` | z-index of tick layer. Useful when ticks are drawn on chart area. Values <= 0 are drawn under datasets, > 0 on top.
|
||||||
|
|
||||||
## Minor Tick Configuration
|
## Minor Tick Configuration
|
||||||
The minorTick configuration is nested under the ticks configuration in the `minor` key. It defines options for the minor tick marks that are generated by the axis. Omitted options are inherited from `ticks` configuration.
|
The minorTick configuration is nested under the ticks configuration in the `minor` key. It defines options for the minor tick marks that are generated by the axis. Omitted options are inherited from `ticks` configuration.
|
||||||
|
|||||||
@ -169,6 +169,7 @@ helpers.extend(Chart.prototype, /** @lends Chart */ {
|
|||||||
me.aspectRatio = height ? width / height : null;
|
me.aspectRatio = height ? width / height : null;
|
||||||
me.options = config.options;
|
me.options = config.options;
|
||||||
me._bufferedRender = false;
|
me._bufferedRender = false;
|
||||||
|
me._layers = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provided for backward compatibility, Chart and Chart.Controller have been merged,
|
* Provided for backward compatibility, Chart and Chart.Controller have been merged,
|
||||||
@ -495,6 +496,12 @@ helpers.extend(Chart.prototype, /** @lends Chart */ {
|
|||||||
// Do this before render so that any plugins that need final scale updates can use it
|
// Do this before render so that any plugins that need final scale updates can use it
|
||||||
plugins.notify(me, 'afterUpdate');
|
plugins.notify(me, 'afterUpdate');
|
||||||
|
|
||||||
|
me._layers.sort(function(a, b) {
|
||||||
|
return a.z === b.z
|
||||||
|
? a._idx - b._idx
|
||||||
|
: a.z - b.z;
|
||||||
|
});
|
||||||
|
|
||||||
if (me._bufferedRender) {
|
if (me._bufferedRender) {
|
||||||
me._bufferedRequest = {
|
me._bufferedRequest = {
|
||||||
duration: config.duration,
|
duration: config.duration,
|
||||||
@ -520,6 +527,15 @@ helpers.extend(Chart.prototype, /** @lends Chart */ {
|
|||||||
|
|
||||||
layouts.update(this, this.width, this.height);
|
layouts.update(this, this.width, this.height);
|
||||||
|
|
||||||
|
me._layers = [];
|
||||||
|
helpers.each(me.boxes, function(box) {
|
||||||
|
me._layers.push.apply(me._layers, box._layers());
|
||||||
|
}, me);
|
||||||
|
|
||||||
|
me._layers.forEach(function(item, index) {
|
||||||
|
item._idx = index;
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provided for backward compatibility, use `afterLayout` instead.
|
* Provided for backward compatibility, use `afterLayout` instead.
|
||||||
* @method IPlugin#afterScaleUpdate
|
* @method IPlugin#afterScaleUpdate
|
||||||
@ -626,6 +642,7 @@ helpers.extend(Chart.prototype, /** @lends Chart */ {
|
|||||||
|
|
||||||
draw: function(easingValue) {
|
draw: function(easingValue) {
|
||||||
var me = this;
|
var me = this;
|
||||||
|
var i, layers;
|
||||||
|
|
||||||
me.clear();
|
me.clear();
|
||||||
|
|
||||||
@ -643,12 +660,21 @@ helpers.extend(Chart.prototype, /** @lends Chart */ {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw all the scales
|
// Because of plugin hooks (before/afterDatasetsDraw), datasets can't
|
||||||
helpers.each(me.boxes, function(box) {
|
// currently be part of layers. Instead, we draw
|
||||||
box.draw(me.chartArea);
|
// layers <= 0 before(default, backward compat), and the rest after
|
||||||
}, me);
|
layers = me._layers;
|
||||||
|
for (i = 0; i < layers.length && layers[i].z <= 0; ++i) {
|
||||||
|
layers[i].draw(me.chartArea);
|
||||||
|
}
|
||||||
|
|
||||||
me.drawDatasets(easingValue);
|
me.drawDatasets(easingValue);
|
||||||
|
|
||||||
|
// Rest of layers
|
||||||
|
for (; i < layers.length; ++i) {
|
||||||
|
layers[i].draw(me.chartArea);
|
||||||
|
}
|
||||||
|
|
||||||
me._drawTooltip(easingValue);
|
me._drawTooltip(easingValue);
|
||||||
|
|
||||||
plugins.notify(me, 'afterDraw', [easingValue]);
|
plugins.notify(me, 'afterDraw', [easingValue]);
|
||||||
|
|||||||
@ -103,6 +103,14 @@ module.exports = {
|
|||||||
item.fullWidth = item.fullWidth || false;
|
item.fullWidth = item.fullWidth || false;
|
||||||
item.position = item.position || 'top';
|
item.position = item.position || 'top';
|
||||||
item.weight = item.weight || 0;
|
item.weight = item.weight || 0;
|
||||||
|
item._layers = item._layers || function() {
|
||||||
|
return [{
|
||||||
|
z: 0,
|
||||||
|
draw: function() {
|
||||||
|
item.draw.apply(item, arguments);
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
};
|
||||||
|
|
||||||
chart.boxes.push(item);
|
chart.boxes.push(item);
|
||||||
},
|
},
|
||||||
|
|||||||
@ -189,7 +189,7 @@ function parseTickFontOptions(options) {
|
|||||||
return {minor: minor, major: major};
|
return {minor: minor, major: major};
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = Element.extend({
|
var Scale = Element.extend({
|
||||||
/**
|
/**
|
||||||
* Get the padding needed for the scale
|
* Get the padding needed for the scale
|
||||||
* @method getPadding
|
* @method getPadding
|
||||||
@ -252,6 +252,7 @@ module.exports = Element.extend({
|
|||||||
me._maxLabelLines = 0;
|
me._maxLabelLines = 0;
|
||||||
me.longestLabelWidth = 0;
|
me.longestLabelWidth = 0;
|
||||||
me.longestTextCache = me.longestTextCache || {};
|
me.longestTextCache = me.longestTextCache || {};
|
||||||
|
me._itemsToDraw = null;
|
||||||
|
|
||||||
// Dimensions
|
// Dimensions
|
||||||
me.beforeSetDimensions();
|
me.beforeSetDimensions();
|
||||||
@ -780,22 +781,14 @@ module.exports = Element.extend({
|
|||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Actually draw the scale on the canvas
|
* @private
|
||||||
* @param {object} chartArea - the area of the chart to draw full grid lines on
|
|
||||||
*/
|
*/
|
||||||
draw: function(chartArea) {
|
_computeItemsToDraw: function(chartArea) {
|
||||||
var me = this;
|
var me = this;
|
||||||
var options = me.options;
|
|
||||||
|
|
||||||
if (!me._isVisible()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var chart = me.chart;
|
var chart = me.chart;
|
||||||
var context = me.ctx;
|
var options = me.options;
|
||||||
var optionTicks = options.ticks;
|
var optionTicks = options.ticks;
|
||||||
var gridLines = options.gridLines;
|
var gridLines = options.gridLines;
|
||||||
var scaleLabel = options.scaleLabel;
|
|
||||||
var position = options.position;
|
var position = options.position;
|
||||||
|
|
||||||
var isRotated = me.labelRotation !== 0;
|
var isRotated = me.labelRotation !== 0;
|
||||||
@ -809,12 +802,11 @@ module.exports = Element.extend({
|
|||||||
|
|
||||||
var tl = getTickMarkLength(gridLines);
|
var tl = getTickMarkLength(gridLines);
|
||||||
|
|
||||||
var scaleLabelFontColor = valueOrDefault(scaleLabel.fontColor, defaults.global.defaultFontColor);
|
|
||||||
var scaleLabelFont = helpers.options._parseFont(scaleLabel);
|
|
||||||
var scaleLabelPadding = helpers.options.toPadding(scaleLabel.padding);
|
|
||||||
var labelRotationRadians = helpers.toRadians(me.labelRotation);
|
var labelRotationRadians = helpers.toRadians(me.labelRotation);
|
||||||
|
|
||||||
var itemsToDraw = [];
|
var items = [];
|
||||||
|
|
||||||
|
var epsilon = 0.0000001; // 0.0000001 is margin in pixels for Accumulated error.
|
||||||
|
|
||||||
var axisWidth = gridLines.drawBorder ? valueAtIndexOrDefault(gridLines.lineWidth, 0, 0) : 0;
|
var axisWidth = gridLines.drawBorder ? valueAtIndexOrDefault(gridLines.lineWidth, 0, 0) : 0;
|
||||||
var alignPixel = helpers._alignPixel;
|
var alignPixel = helpers._alignPixel;
|
||||||
@ -838,8 +830,6 @@ module.exports = Element.extend({
|
|||||||
tickEnd = me.left + tl;
|
tickEnd = me.left + tl;
|
||||||
}
|
}
|
||||||
|
|
||||||
var epsilon = 0.0000001; // 0.0000001 is margin in pixels for Accumulated error.
|
|
||||||
|
|
||||||
helpers.each(ticks, function(tick, index) {
|
helpers.each(ticks, function(tick, index) {
|
||||||
// autoskipper skipped this tick (#4635)
|
// autoskipper skipped this tick (#4635)
|
||||||
if (helpers.isNullOrUndef(tick.label)) {
|
if (helpers.isNullOrUndef(tick.label)) {
|
||||||
@ -919,7 +909,7 @@ module.exports = Element.extend({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
itemsToDraw.push({
|
items.push({
|
||||||
tx1: tx1,
|
tx1: tx1,
|
||||||
ty1: ty1,
|
ty1: ty1,
|
||||||
tx2: tx2,
|
tx2: tx2,
|
||||||
@ -937,76 +927,156 @@ module.exports = Element.extend({
|
|||||||
rotation: -1 * labelRotationRadians,
|
rotation: -1 * labelRotationRadians,
|
||||||
label: label,
|
label: label,
|
||||||
major: tick.major,
|
major: tick.major,
|
||||||
|
font: tick.major ? tickFonts.major : tickFonts.minor,
|
||||||
textOffset: textOffset,
|
textOffset: textOffset,
|
||||||
textAlign: textAlign
|
textAlign: textAlign
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Draw all of the tick labels, tick marks, and grid lines at the correct places
|
items.ticksLength = ticks.length;
|
||||||
helpers.each(itemsToDraw, function(itemToDraw) {
|
items.borderValue = borderValue;
|
||||||
var glWidth = itemToDraw.glWidth;
|
|
||||||
var glColor = itemToDraw.glColor;
|
|
||||||
|
|
||||||
if (gridLines.display && glWidth && glColor) {
|
return items;
|
||||||
context.save();
|
},
|
||||||
context.lineWidth = glWidth;
|
|
||||||
context.strokeStyle = glColor;
|
/**
|
||||||
if (context.setLineDash) {
|
* @private
|
||||||
context.setLineDash(itemToDraw.glBorderDash);
|
*/
|
||||||
context.lineDashOffset = itemToDraw.glBorderDashOffset;
|
_drawGrid: function(chartArea) {
|
||||||
|
var me = this;
|
||||||
|
var ctx = me.ctx;
|
||||||
|
var chart = me.chart;
|
||||||
|
var gridLines = me.options.gridLines;
|
||||||
|
|
||||||
|
if (!gridLines.display) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
context.beginPath();
|
var alignPixel = helpers._alignPixel;
|
||||||
|
var axisWidth = gridLines.drawBorder ? valueAtIndexOrDefault(gridLines.lineWidth, 0, 0) : 0;
|
||||||
|
var items = me._itemsToDraw || (me._itemsToDraw = me._computeItemsToDraw(chartArea));
|
||||||
|
var glWidth, glColor;
|
||||||
|
|
||||||
|
helpers.each(items, function(item) {
|
||||||
|
glWidth = item.glWidth;
|
||||||
|
glColor = item.glColor;
|
||||||
|
|
||||||
|
if (glWidth && glColor) {
|
||||||
|
ctx.save();
|
||||||
|
ctx.lineWidth = glWidth;
|
||||||
|
ctx.strokeStyle = glColor;
|
||||||
|
if (ctx.setLineDash) {
|
||||||
|
ctx.setLineDash(item.glBorderDash);
|
||||||
|
ctx.lineDashOffset = item.glBorderDashOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.beginPath();
|
||||||
|
|
||||||
if (gridLines.drawTicks) {
|
if (gridLines.drawTicks) {
|
||||||
context.moveTo(itemToDraw.tx1, itemToDraw.ty1);
|
ctx.moveTo(item.tx1, item.ty1);
|
||||||
context.lineTo(itemToDraw.tx2, itemToDraw.ty2);
|
ctx.lineTo(item.tx2, item.ty2);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gridLines.drawOnChartArea) {
|
if (gridLines.drawOnChartArea) {
|
||||||
context.moveTo(itemToDraw.x1, itemToDraw.y1);
|
ctx.moveTo(item.x1, item.y1);
|
||||||
context.lineTo(itemToDraw.x2, itemToDraw.y2);
|
ctx.lineTo(item.x2, item.y2);
|
||||||
}
|
}
|
||||||
|
|
||||||
context.stroke();
|
ctx.stroke();
|
||||||
context.restore();
|
ctx.restore();
|
||||||
}
|
|
||||||
|
|
||||||
if (optionTicks.display) {
|
|
||||||
var tickFont = itemToDraw.major ? tickFonts.major : tickFonts.minor;
|
|
||||||
|
|
||||||
// Make sure we draw text in the correct color and font
|
|
||||||
context.save();
|
|
||||||
context.translate(itemToDraw.labelX, itemToDraw.labelY);
|
|
||||||
context.rotate(itemToDraw.rotation);
|
|
||||||
context.font = tickFont.string;
|
|
||||||
context.fillStyle = tickFont.color;
|
|
||||||
context.textBaseline = 'middle';
|
|
||||||
context.textAlign = itemToDraw.textAlign;
|
|
||||||
|
|
||||||
var label = itemToDraw.label;
|
|
||||||
var y = itemToDraw.textOffset;
|
|
||||||
if (helpers.isArray(label)) {
|
|
||||||
for (var i = 0; i < label.length; ++i) {
|
|
||||||
// We just make sure the multiline element is a string here..
|
|
||||||
context.fillText('' + label[i], 0, y);
|
|
||||||
y += tickFont.lineHeight;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
context.fillText(label, 0, y);
|
|
||||||
}
|
|
||||||
context.restore();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (scaleLabel.display) {
|
if (axisWidth) {
|
||||||
// Draw the scale label
|
// Draw the line at the edge of the axis
|
||||||
var scaleLabelX;
|
var firstLineWidth = axisWidth;
|
||||||
var scaleLabelY;
|
var lastLineWidth = valueAtIndexOrDefault(gridLines.lineWidth, items.ticksLength - 1, 0);
|
||||||
var rotation = 0;
|
var borderValue = items.borderValue;
|
||||||
var halfLineHeight = scaleLabelFont.lineHeight / 2;
|
var x1, x2, y1, y2;
|
||||||
|
|
||||||
if (isHorizontal) {
|
if (me.isHorizontal()) {
|
||||||
|
x1 = alignPixel(chart, me.left, firstLineWidth) - firstLineWidth / 2;
|
||||||
|
x2 = alignPixel(chart, me.right, lastLineWidth) + lastLineWidth / 2;
|
||||||
|
y1 = y2 = borderValue;
|
||||||
|
} else {
|
||||||
|
y1 = alignPixel(chart, me.top, firstLineWidth) - firstLineWidth / 2;
|
||||||
|
y2 = alignPixel(chart, me.bottom, lastLineWidth) + lastLineWidth / 2;
|
||||||
|
x1 = x2 = borderValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.lineWidth = axisWidth;
|
||||||
|
ctx.strokeStyle = valueAtIndexOrDefault(gridLines.color, 0);
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(x1, y1);
|
||||||
|
ctx.lineTo(x2, y2);
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_drawLabels: function(chartArea) {
|
||||||
|
var me = this;
|
||||||
|
var ctx = me.ctx;
|
||||||
|
var optionTicks = me.options.ticks;
|
||||||
|
|
||||||
|
if (!optionTicks.display) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var items = me._itemsToDraw || (me._itemsToDraw = me._computeItemsToDraw(chartArea));
|
||||||
|
var tickFont;
|
||||||
|
|
||||||
|
helpers.each(items, function(item) {
|
||||||
|
tickFont = item.font;
|
||||||
|
|
||||||
|
// Make sure we draw text in the correct color and font
|
||||||
|
ctx.save();
|
||||||
|
ctx.translate(item.labelX, item.labelY);
|
||||||
|
ctx.rotate(item.rotation);
|
||||||
|
ctx.font = tickFont.string;
|
||||||
|
ctx.fillStyle = tickFont.color;
|
||||||
|
ctx.textBaseline = 'middle';
|
||||||
|
ctx.textAlign = item.textAlign;
|
||||||
|
|
||||||
|
var label = item.label;
|
||||||
|
var y = item.textOffset;
|
||||||
|
if (helpers.isArray(label)) {
|
||||||
|
for (var i = 0; i < label.length; ++i) {
|
||||||
|
// We just make sure the multiline element is a string here..
|
||||||
|
ctx.fillText('' + label[i], 0, y);
|
||||||
|
y += tickFont.lineHeight;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ctx.fillText(label, 0, y);
|
||||||
|
}
|
||||||
|
ctx.restore();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_drawTitle: function() {
|
||||||
|
var me = this;
|
||||||
|
var ctx = me.ctx;
|
||||||
|
var options = me.options;
|
||||||
|
var scaleLabel = options.scaleLabel;
|
||||||
|
|
||||||
|
if (!scaleLabel.display) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var scaleLabelFontColor = valueOrDefault(scaleLabel.fontColor, defaults.global.defaultFontColor);
|
||||||
|
var scaleLabelFont = helpers.options._parseFont(scaleLabel);
|
||||||
|
var scaleLabelPadding = helpers.options.toPadding(scaleLabel.padding);
|
||||||
|
var halfLineHeight = scaleLabelFont.lineHeight / 2;
|
||||||
|
var position = options.position;
|
||||||
|
var rotation = 0;
|
||||||
|
var scaleLabelX, scaleLabelY;
|
||||||
|
|
||||||
|
if (me.isHorizontal()) {
|
||||||
scaleLabelX = me.left + ((me.right - me.left) / 2); // midpoint of the width
|
scaleLabelX = me.left + ((me.right - me.left) / 2); // midpoint of the width
|
||||||
scaleLabelY = position === 'bottom'
|
scaleLabelY = position === 'bottom'
|
||||||
? me.bottom - halfLineHeight - scaleLabelPadding.bottom
|
? me.bottom - halfLineHeight - scaleLabelPadding.bottom
|
||||||
@ -1020,39 +1090,63 @@ module.exports = Element.extend({
|
|||||||
rotation = isLeft ? -0.5 * Math.PI : 0.5 * Math.PI;
|
rotation = isLeft ? -0.5 * Math.PI : 0.5 * Math.PI;
|
||||||
}
|
}
|
||||||
|
|
||||||
context.save();
|
ctx.save();
|
||||||
context.translate(scaleLabelX, scaleLabelY);
|
ctx.translate(scaleLabelX, scaleLabelY);
|
||||||
context.rotate(rotation);
|
ctx.rotate(rotation);
|
||||||
context.textAlign = 'center';
|
ctx.textAlign = 'center';
|
||||||
context.textBaseline = 'middle';
|
ctx.textBaseline = 'middle';
|
||||||
context.fillStyle = scaleLabelFontColor; // render in correct colour
|
ctx.fillStyle = scaleLabelFontColor; // render in correct colour
|
||||||
context.font = scaleLabelFont.string;
|
ctx.font = scaleLabelFont.string;
|
||||||
context.fillText(scaleLabel.labelString, 0, 0);
|
ctx.fillText(scaleLabel.labelString, 0, 0);
|
||||||
context.restore();
|
ctx.restore();
|
||||||
|
},
|
||||||
|
|
||||||
|
draw: function(chartArea) {
|
||||||
|
var me = this;
|
||||||
|
|
||||||
|
if (!me._isVisible()) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (axisWidth) {
|
me._drawGrid(chartArea);
|
||||||
// Draw the line at the edge of the axis
|
me._drawTitle(chartArea);
|
||||||
var firstLineWidth = axisWidth;
|
me._drawLabels(chartArea);
|
||||||
var lastLineWidth = valueAtIndexOrDefault(gridLines.lineWidth, ticks.length - 1, 0);
|
},
|
||||||
var x1, x2, y1, y2;
|
|
||||||
|
|
||||||
if (isHorizontal) {
|
/**
|
||||||
x1 = alignPixel(chart, me.left, firstLineWidth) - firstLineWidth / 2;
|
* @private
|
||||||
x2 = alignPixel(chart, me.right, lastLineWidth) + lastLineWidth / 2;
|
*/
|
||||||
y1 = y2 = borderValue;
|
_layers: function() {
|
||||||
} else {
|
var me = this;
|
||||||
y1 = alignPixel(chart, me.top, firstLineWidth) - firstLineWidth / 2;
|
var opts = me.options;
|
||||||
y2 = alignPixel(chart, me.bottom, lastLineWidth) + lastLineWidth / 2;
|
var tz = opts.ticks && opts.ticks.z || 0;
|
||||||
x1 = x2 = borderValue;
|
var gz = opts.gridLines && opts.gridLines.z || 0;
|
||||||
|
|
||||||
|
if (!me._isVisible() || tz === gz || me.draw !== me._draw) {
|
||||||
|
// backward compatibility: draw has been overridden by custom scale
|
||||||
|
return [{
|
||||||
|
z: tz,
|
||||||
|
draw: function() {
|
||||||
|
me.draw.apply(me, arguments);
|
||||||
|
}
|
||||||
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
context.lineWidth = axisWidth;
|
return [{
|
||||||
context.strokeStyle = valueAtIndexOrDefault(gridLines.color, 0);
|
z: gz,
|
||||||
context.beginPath();
|
draw: function() {
|
||||||
context.moveTo(x1, y1);
|
me._drawGrid.apply(me, arguments);
|
||||||
context.lineTo(x2, y2);
|
me._drawTitle.apply(me, arguments);
|
||||||
context.stroke();
|
|
||||||
}
|
}
|
||||||
|
}, {
|
||||||
|
z: tz,
|
||||||
|
draw: function() {
|
||||||
|
me._drawLabels.apply(me, arguments);
|
||||||
|
}
|
||||||
|
}];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Scale.prototype._draw = Scale.prototype.draw;
|
||||||
|
|
||||||
|
module.exports = Scale;
|
||||||
|
|||||||
@ -224,39 +224,17 @@ function adjustPointPositionForLabelHeight(angle, textSize, position) {
|
|||||||
function drawPointLabels(scale) {
|
function drawPointLabels(scale) {
|
||||||
var ctx = scale.ctx;
|
var ctx = scale.ctx;
|
||||||
var opts = scale.options;
|
var opts = scale.options;
|
||||||
var angleLineOpts = opts.angleLines;
|
|
||||||
var gridLineOpts = opts.gridLines;
|
|
||||||
var pointLabelOpts = opts.pointLabels;
|
var pointLabelOpts = opts.pointLabels;
|
||||||
var lineWidth = valueOrDefault(angleLineOpts.lineWidth, gridLineOpts.lineWidth);
|
|
||||||
var lineColor = valueOrDefault(angleLineOpts.color, gridLineOpts.color);
|
|
||||||
var tickBackdropHeight = getTickBackdropHeight(opts);
|
var tickBackdropHeight = getTickBackdropHeight(opts);
|
||||||
|
var outerDistance = scale.getDistanceFromCenterForValue(opts.ticks.reverse ? scale.min : scale.max);
|
||||||
|
var plFont = helpers.options._parseFont(pointLabelOpts);
|
||||||
|
|
||||||
ctx.save();
|
ctx.save();
|
||||||
ctx.lineWidth = lineWidth;
|
|
||||||
ctx.strokeStyle = lineColor;
|
|
||||||
if (ctx.setLineDash) {
|
|
||||||
ctx.setLineDash(resolve([angleLineOpts.borderDash, gridLineOpts.borderDash, []]));
|
|
||||||
ctx.lineDashOffset = resolve([angleLineOpts.borderDashOffset, gridLineOpts.borderDashOffset, 0.0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
var outerDistance = scale.getDistanceFromCenterForValue(opts.ticks.reverse ? scale.min : scale.max);
|
|
||||||
|
|
||||||
// Point Label Font
|
|
||||||
var plFont = helpers.options._parseFont(pointLabelOpts);
|
|
||||||
|
|
||||||
ctx.font = plFont.string;
|
ctx.font = plFont.string;
|
||||||
ctx.textBaseline = 'middle';
|
ctx.textBaseline = 'middle';
|
||||||
|
|
||||||
for (var i = getValueCount(scale) - 1; i >= 0; i--) {
|
for (var i = getValueCount(scale) - 1; i >= 0; i--) {
|
||||||
if (angleLineOpts.display && lineWidth && lineColor) {
|
|
||||||
var outerPosition = scale.getPointPosition(i, outerDistance);
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.moveTo(scale.xCenter, scale.yCenter);
|
|
||||||
ctx.lineTo(outerPosition.x, outerPosition.y);
|
|
||||||
ctx.stroke();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pointLabelOpts.display) {
|
|
||||||
// Extra pixels out for some label spacing
|
// Extra pixels out for some label spacing
|
||||||
var extra = (i === 0 ? tickBackdropHeight / 2 : 0);
|
var extra = (i === 0 ? tickBackdropHeight / 2 : 0);
|
||||||
var pointLabelPosition = scale.getPointPosition(i, outerDistance + extra + 5);
|
var pointLabelPosition = scale.getPointPosition(i, outerDistance + extra + 5);
|
||||||
@ -271,7 +249,6 @@ function drawPointLabels(scale) {
|
|||||||
adjustPointPositionForLabelHeight(angle, scale._pointLabelSizes[i], pointLabelPosition);
|
adjustPointPositionForLabelHeight(angle, scale._pointLabelSizes[i], pointLabelPosition);
|
||||||
fillText(ctx, scale.pointLabels[i] || '', pointLabelPosition, plFont.lineHeight);
|
fillText(ctx, scale.pointLabels[i] || '', pointLabelPosition, plFont.lineHeight);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
ctx.restore();
|
ctx.restore();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -473,60 +450,109 @@ module.exports = LinearScaleBase.extend({
|
|||||||
0);
|
0);
|
||||||
},
|
},
|
||||||
|
|
||||||
draw: function() {
|
/**
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_drawGrid: function() {
|
||||||
var me = this;
|
var me = this;
|
||||||
|
var ctx = me.ctx;
|
||||||
var opts = me.options;
|
var opts = me.options;
|
||||||
var gridLineOpts = opts.gridLines;
|
var gridLineOpts = opts.gridLines;
|
||||||
var tickOpts = opts.ticks;
|
var angleLineOpts = opts.angleLines;
|
||||||
|
var lineWidth = valueOrDefault(angleLineOpts.lineWidth, gridLineOpts.lineWidth);
|
||||||
|
var lineColor = valueOrDefault(angleLineOpts.color, gridLineOpts.color);
|
||||||
|
var i, offset, position;
|
||||||
|
|
||||||
if (opts.display) {
|
if (opts.pointLabels.display) {
|
||||||
var ctx = me.ctx;
|
|
||||||
var startAngle = this.getIndexAngle(0);
|
|
||||||
var tickFont = helpers.options._parseFont(tickOpts);
|
|
||||||
|
|
||||||
if (opts.angleLines.display || opts.pointLabels.display) {
|
|
||||||
drawPointLabels(me);
|
drawPointLabels(me);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (gridLineOpts.display) {
|
||||||
helpers.each(me.ticks, function(label, index) {
|
helpers.each(me.ticks, function(label, index) {
|
||||||
// Don't draw a centre value (if it is minimum)
|
if (index !== 0) {
|
||||||
if (index > 0 || tickOpts.reverse) {
|
offset = me.getDistanceFromCenterForValue(me.ticksAsNumbers[index]);
|
||||||
var yCenterOffset = me.getDistanceFromCenterForValue(me.ticksAsNumbers[index]);
|
drawRadiusLine(me, gridLineOpts, offset, index);
|
||||||
|
}
|
||||||
// Draw circular lines around the scale
|
});
|
||||||
if (gridLineOpts.display && index !== 0) {
|
|
||||||
drawRadiusLine(me, gridLineOpts, yCenterOffset, index);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tickOpts.display) {
|
if (angleLineOpts.display && lineWidth && lineColor) {
|
||||||
|
ctx.save();
|
||||||
|
ctx.lineWidth = lineWidth;
|
||||||
|
ctx.strokeStyle = lineColor;
|
||||||
|
if (ctx.setLineDash) {
|
||||||
|
ctx.setLineDash(resolve([angleLineOpts.borderDash, gridLineOpts.borderDash, []]));
|
||||||
|
ctx.lineDashOffset = resolve([angleLineOpts.borderDashOffset, gridLineOpts.borderDashOffset, 0.0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = getValueCount(me) - 1; i >= 0; i--) {
|
||||||
|
offset = me.getDistanceFromCenterForValue(opts.ticks.reverse ? me.min : me.max);
|
||||||
|
position = me.getPointPosition(i, offset);
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(me.xCenter, me.yCenter);
|
||||||
|
ctx.lineTo(position.x, position.y);
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.restore();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_drawLabels: function() {
|
||||||
|
var me = this;
|
||||||
|
var ctx = me.ctx;
|
||||||
|
var opts = me.options;
|
||||||
|
var tickOpts = opts.ticks;
|
||||||
|
|
||||||
|
if (!tickOpts.display) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var startAngle = me.getIndexAngle(0);
|
||||||
|
var tickFont = helpers.options._parseFont(tickOpts);
|
||||||
var tickFontColor = valueOrDefault(tickOpts.fontColor, defaults.global.defaultFontColor);
|
var tickFontColor = valueOrDefault(tickOpts.fontColor, defaults.global.defaultFontColor);
|
||||||
ctx.font = tickFont.string;
|
var offset, width;
|
||||||
|
|
||||||
ctx.save();
|
ctx.save();
|
||||||
|
ctx.font = tickFont.string;
|
||||||
ctx.translate(me.xCenter, me.yCenter);
|
ctx.translate(me.xCenter, me.yCenter);
|
||||||
ctx.rotate(startAngle);
|
ctx.rotate(startAngle);
|
||||||
|
ctx.textAlign = 'center';
|
||||||
|
ctx.textBaseline = 'middle';
|
||||||
|
|
||||||
|
helpers.each(me.ticks, function(label, index) {
|
||||||
|
if (index === 0 && !tickOpts.reverse) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
offset = me.getDistanceFromCenterForValue(me.ticksAsNumbers[index]);
|
||||||
|
|
||||||
if (tickOpts.showLabelBackdrop) {
|
if (tickOpts.showLabelBackdrop) {
|
||||||
var labelWidth = ctx.measureText(label).width;
|
width = ctx.measureText(label).width;
|
||||||
ctx.fillStyle = tickOpts.backdropColor;
|
ctx.fillStyle = tickOpts.backdropColor;
|
||||||
|
|
||||||
ctx.fillRect(
|
ctx.fillRect(
|
||||||
-labelWidth / 2 - tickOpts.backdropPaddingX,
|
-width / 2 - tickOpts.backdropPaddingX,
|
||||||
-yCenterOffset - tickFont.size / 2 - tickOpts.backdropPaddingY,
|
-offset - tickFont.size / 2 - tickOpts.backdropPaddingY,
|
||||||
labelWidth + tickOpts.backdropPaddingX * 2,
|
width + tickOpts.backdropPaddingX * 2,
|
||||||
tickFont.size + tickOpts.backdropPaddingY * 2
|
tickFont.size + tickOpts.backdropPaddingY * 2
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.textAlign = 'center';
|
|
||||||
ctx.textBaseline = 'middle';
|
|
||||||
ctx.fillStyle = tickFontColor;
|
ctx.fillStyle = tickFontColor;
|
||||||
ctx.fillText(label, 0, -yCenterOffset);
|
ctx.fillText(label, 0, -offset);
|
||||||
ctx.restore();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
|
||||||
}
|
ctx.restore();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_drawTitle: helpers.noop
|
||||||
});
|
});
|
||||||
|
|
||||||
// INTERNAL: static default options, registered in src/index.js
|
// INTERNAL: static default options, registered in src/index.js
|
||||||
|
|||||||
33
test/fixtures/scale.radialLinear/gridlines-no-z.json
vendored
Normal file
33
test/fixtures/scale.radialLinear/gridlines-no-z.json
vendored
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"config": {
|
||||||
|
"type": "radar",
|
||||||
|
"data": {
|
||||||
|
"labels": ["A", "B", "C", "D", "E"],
|
||||||
|
"datasets": [{
|
||||||
|
"backgroundColor": "rgba(255, 0, 0, 1)",
|
||||||
|
"data": [1,2,3,3,3]
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"responsive": false,
|
||||||
|
"legend": false,
|
||||||
|
"title": false,
|
||||||
|
"scale": {
|
||||||
|
"gridLines": {
|
||||||
|
"color": "rgba(0, 0, 0, 1)",
|
||||||
|
"lineWidth": 1
|
||||||
|
},
|
||||||
|
"angleLines": {
|
||||||
|
"color": "rgba(0, 0, 255, 1)",
|
||||||
|
"lineWidth": 1
|
||||||
|
},
|
||||||
|
"pointLabels": {
|
||||||
|
"display": false
|
||||||
|
},
|
||||||
|
"ticks": {
|
||||||
|
"display": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
test/fixtures/scale.radialLinear/gridlines-no-z.png
vendored
Normal file
BIN
test/fixtures/scale.radialLinear/gridlines-no-z.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 38 KiB |
34
test/fixtures/scale.radialLinear/gridlines-z.json
vendored
Normal file
34
test/fixtures/scale.radialLinear/gridlines-z.json
vendored
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"config": {
|
||||||
|
"type": "radar",
|
||||||
|
"data": {
|
||||||
|
"labels": ["A", "B", "C", "D", "E"],
|
||||||
|
"datasets": [{
|
||||||
|
"backgroundColor": "rgba(255, 0, 0, 1)",
|
||||||
|
"data": [1,2,3,3,3]
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"responsive": false,
|
||||||
|
"legend": false,
|
||||||
|
"title": false,
|
||||||
|
"scale": {
|
||||||
|
"gridLines": {
|
||||||
|
"color": "rgba(0, 0, 0, 1)",
|
||||||
|
"lineWidth": 1,
|
||||||
|
"z": 1
|
||||||
|
},
|
||||||
|
"angleLines": {
|
||||||
|
"color": "rgba(0, 0, 255, 1)",
|
||||||
|
"lineWidth": 1
|
||||||
|
},
|
||||||
|
"pointLabels": {
|
||||||
|
"display": false
|
||||||
|
},
|
||||||
|
"ticks": {
|
||||||
|
"display": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
test/fixtures/scale.radialLinear/gridlines-z.png
vendored
Normal file
BIN
test/fixtures/scale.radialLinear/gridlines-z.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 51 KiB |
@ -473,4 +473,137 @@ describe('Core.scale', function() {
|
|||||||
expect(scale.ticks.length).toBe(0);
|
expect(scale.ticks.length).toBe(0);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('_layers', function() {
|
||||||
|
it('should default to one layer', function() {
|
||||||
|
var chart = window.acquireChart({
|
||||||
|
type: 'line',
|
||||||
|
options: {
|
||||||
|
scales: {
|
||||||
|
xAxes: [{
|
||||||
|
id: 'x',
|
||||||
|
type: 'linear',
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var scale = chart.scales.x;
|
||||||
|
expect(scale._layers().length).toEqual(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should default to one layer for custom scales', function() {
|
||||||
|
var customScale = Chart.Scale.extend({
|
||||||
|
draw: function() {},
|
||||||
|
convertTicksToLabels: function() {
|
||||||
|
return ['tick'];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Chart.scaleService.registerScaleType('customScale', customScale, {});
|
||||||
|
|
||||||
|
var chart = window.acquireChart({
|
||||||
|
type: 'line',
|
||||||
|
options: {
|
||||||
|
scales: {
|
||||||
|
xAxes: [{
|
||||||
|
id: 'x',
|
||||||
|
type: 'customScale',
|
||||||
|
gridLines: {
|
||||||
|
z: 10
|
||||||
|
},
|
||||||
|
ticks: {
|
||||||
|
z: 20
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var scale = chart.scales.x;
|
||||||
|
expect(scale._layers().length).toEqual(1);
|
||||||
|
expect(scale._layers()[0].z).toEqual(20);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should default to one layer when z is equal between ticks and grid', function() {
|
||||||
|
var chart = window.acquireChart({
|
||||||
|
type: 'line',
|
||||||
|
options: {
|
||||||
|
scales: {
|
||||||
|
xAxes: [{
|
||||||
|
id: 'x',
|
||||||
|
type: 'linear',
|
||||||
|
ticks: {
|
||||||
|
z: 10
|
||||||
|
},
|
||||||
|
gridLines: {
|
||||||
|
z: 10
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var scale = chart.scales.x;
|
||||||
|
expect(scale._layers().length).toEqual(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('should return 2 layers when z is not equal between ticks and grid', function() {
|
||||||
|
var chart = window.acquireChart({
|
||||||
|
type: 'line',
|
||||||
|
options: {
|
||||||
|
scales: {
|
||||||
|
xAxes: [{
|
||||||
|
id: 'x',
|
||||||
|
type: 'linear',
|
||||||
|
ticks: {
|
||||||
|
z: 10
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(chart.scales.x._layers().length).toEqual(2);
|
||||||
|
|
||||||
|
chart = window.acquireChart({
|
||||||
|
type: 'line',
|
||||||
|
options: {
|
||||||
|
scales: {
|
||||||
|
xAxes: [{
|
||||||
|
id: 'x',
|
||||||
|
type: 'linear',
|
||||||
|
gridLines: {
|
||||||
|
z: 11
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(chart.scales.x._layers().length).toEqual(2);
|
||||||
|
|
||||||
|
chart = window.acquireChart({
|
||||||
|
type: 'line',
|
||||||
|
options: {
|
||||||
|
scales: {
|
||||||
|
xAxes: [{
|
||||||
|
id: 'x',
|
||||||
|
type: 'linear',
|
||||||
|
ticks: {
|
||||||
|
z: 10
|
||||||
|
},
|
||||||
|
gridLines: {
|
||||||
|
z: 11
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(chart.scales.x._layers().length).toEqual(2);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user