diff --git a/docs/axes/styling.md b/docs/axes/styling.md index 23b212348da..b058498b652 100644 --- a/docs/axes/styling.md +++ b/docs/axes/styling.md @@ -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). | `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. +| `z` | `number` | `0` | z-index of gridline layer. Values <= 0 are drawn under datasets, > 0 on top. ## 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. @@ -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. | `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 +| `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 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. diff --git a/src/core/core.controller.js b/src/core/core.controller.js index 8abe3978e1a..168d40b4940 100644 --- a/src/core/core.controller.js +++ b/src/core/core.controller.js @@ -169,6 +169,7 @@ helpers.extend(Chart.prototype, /** @lends Chart */ { me.aspectRatio = height ? width / height : null; me.options = config.options; me._bufferedRender = false; + me._layers = []; /** * 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 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) { me._bufferedRequest = { duration: config.duration, @@ -520,6 +527,15 @@ helpers.extend(Chart.prototype, /** @lends Chart */ { 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. * @method IPlugin#afterScaleUpdate @@ -626,6 +642,7 @@ helpers.extend(Chart.prototype, /** @lends Chart */ { draw: function(easingValue) { var me = this; + var i, layers; me.clear(); @@ -643,12 +660,21 @@ helpers.extend(Chart.prototype, /** @lends Chart */ { return; } - // Draw all the scales - helpers.each(me.boxes, function(box) { - box.draw(me.chartArea); - }, me); + // Because of plugin hooks (before/afterDatasetsDraw), datasets can't + // currently be part of layers. Instead, we draw + // layers <= 0 before(default, backward compat), and the rest after + layers = me._layers; + for (i = 0; i < layers.length && layers[i].z <= 0; ++i) { + layers[i].draw(me.chartArea); + } me.drawDatasets(easingValue); + + // Rest of layers + for (; i < layers.length; ++i) { + layers[i].draw(me.chartArea); + } + me._drawTooltip(easingValue); plugins.notify(me, 'afterDraw', [easingValue]); diff --git a/src/core/core.layouts.js b/src/core/core.layouts.js index fbaf96a952d..4a0969f62ba 100644 --- a/src/core/core.layouts.js +++ b/src/core/core.layouts.js @@ -103,6 +103,14 @@ module.exports = { item.fullWidth = item.fullWidth || false; item.position = item.position || 'top'; item.weight = item.weight || 0; + item._layers = item._layers || function() { + return [{ + z: 0, + draw: function() { + item.draw.apply(item, arguments); + } + }]; + }; chart.boxes.push(item); }, diff --git a/src/core/core.scale.js b/src/core/core.scale.js index a4cd62317cc..cf1fb281e88 100644 --- a/src/core/core.scale.js +++ b/src/core/core.scale.js @@ -189,7 +189,7 @@ function parseTickFontOptions(options) { return {minor: minor, major: major}; } -module.exports = Element.extend({ +var Scale = Element.extend({ /** * Get the padding needed for the scale * @method getPadding @@ -252,6 +252,7 @@ module.exports = Element.extend({ me._maxLabelLines = 0; me.longestLabelWidth = 0; me.longestTextCache = me.longestTextCache || {}; + me._itemsToDraw = null; // Dimensions me.beforeSetDimensions(); @@ -780,22 +781,14 @@ module.exports = Element.extend({ }, /** - * Actually draw the scale on the canvas - * @param {object} chartArea - the area of the chart to draw full grid lines on + * @private */ - draw: function(chartArea) { + _computeItemsToDraw: function(chartArea) { var me = this; - var options = me.options; - - if (!me._isVisible()) { - return; - } - var chart = me.chart; - var context = me.ctx; + var options = me.options; var optionTicks = options.ticks; var gridLines = options.gridLines; - var scaleLabel = options.scaleLabel; var position = options.position; var isRotated = me.labelRotation !== 0; @@ -809,12 +802,11 @@ module.exports = Element.extend({ 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 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 alignPixel = helpers._alignPixel; @@ -838,8 +830,6 @@ module.exports = Element.extend({ tickEnd = me.left + tl; } - var epsilon = 0.0000001; // 0.0000001 is margin in pixels for Accumulated error. - helpers.each(ticks, function(tick, index) { // autoskipper skipped this tick (#4635) if (helpers.isNullOrUndef(tick.label)) { @@ -919,7 +909,7 @@ module.exports = Element.extend({ } } - itemsToDraw.push({ + items.push({ tx1: tx1, ty1: ty1, tx2: tx2, @@ -937,107 +927,74 @@ module.exports = Element.extend({ rotation: -1 * labelRotationRadians, label: label, major: tick.major, + font: tick.major ? tickFonts.major : tickFonts.minor, textOffset: textOffset, textAlign: textAlign }); }); - // Draw all of the tick labels, tick marks, and grid lines at the correct places - helpers.each(itemsToDraw, function(itemToDraw) { - var glWidth = itemToDraw.glWidth; - var glColor = itemToDraw.glColor; - - if (gridLines.display && glWidth && glColor) { - context.save(); - context.lineWidth = glWidth; - context.strokeStyle = glColor; - if (context.setLineDash) { - context.setLineDash(itemToDraw.glBorderDash); - context.lineDashOffset = itemToDraw.glBorderDashOffset; + items.ticksLength = ticks.length; + items.borderValue = borderValue; + + return items; + }, + + /** + * @private + */ + _drawGrid: function(chartArea) { + var me = this; + var ctx = me.ctx; + var chart = me.chart; + var gridLines = me.options.gridLines; + + if (!gridLines.display) { + return; + } + + 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; } - context.beginPath(); + ctx.beginPath(); if (gridLines.drawTicks) { - context.moveTo(itemToDraw.tx1, itemToDraw.ty1); - context.lineTo(itemToDraw.tx2, itemToDraw.ty2); + ctx.moveTo(item.tx1, item.ty1); + ctx.lineTo(item.tx2, item.ty2); } if (gridLines.drawOnChartArea) { - context.moveTo(itemToDraw.x1, itemToDraw.y1); - context.lineTo(itemToDraw.x2, itemToDraw.y2); + ctx.moveTo(item.x1, item.y1); + ctx.lineTo(item.x2, item.y2); } - context.stroke(); - context.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(); + ctx.stroke(); + ctx.restore(); } }); - if (scaleLabel.display) { - // Draw the scale label - var scaleLabelX; - var scaleLabelY; - var rotation = 0; - var halfLineHeight = scaleLabelFont.lineHeight / 2; - - if (isHorizontal) { - scaleLabelX = me.left + ((me.right - me.left) / 2); // midpoint of the width - scaleLabelY = position === 'bottom' - ? me.bottom - halfLineHeight - scaleLabelPadding.bottom - : me.top + halfLineHeight + scaleLabelPadding.top; - } else { - var isLeft = position === 'left'; - scaleLabelX = isLeft - ? me.left + halfLineHeight + scaleLabelPadding.top - : me.right - halfLineHeight - scaleLabelPadding.top; - scaleLabelY = me.top + ((me.bottom - me.top) / 2); - rotation = isLeft ? -0.5 * Math.PI : 0.5 * Math.PI; - } - - context.save(); - context.translate(scaleLabelX, scaleLabelY); - context.rotate(rotation); - context.textAlign = 'center'; - context.textBaseline = 'middle'; - context.fillStyle = scaleLabelFontColor; // render in correct colour - context.font = scaleLabelFont.string; - context.fillText(scaleLabel.labelString, 0, 0); - context.restore(); - } - if (axisWidth) { // Draw the line at the edge of the axis var firstLineWidth = axisWidth; - var lastLineWidth = valueAtIndexOrDefault(gridLines.lineWidth, ticks.length - 1, 0); + var lastLineWidth = valueAtIndexOrDefault(gridLines.lineWidth, items.ticksLength - 1, 0); + var borderValue = items.borderValue; 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; @@ -1047,12 +1004,149 @@ module.exports = Element.extend({ x1 = x2 = borderValue; } - context.lineWidth = axisWidth; - context.strokeStyle = valueAtIndexOrDefault(gridLines.color, 0); - context.beginPath(); - context.moveTo(x1, y1); - context.lineTo(x2, y2); - context.stroke(); + 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 + scaleLabelY = position === 'bottom' + ? me.bottom - halfLineHeight - scaleLabelPadding.bottom + : me.top + halfLineHeight + scaleLabelPadding.top; + } else { + var isLeft = position === 'left'; + scaleLabelX = isLeft + ? me.left + halfLineHeight + scaleLabelPadding.top + : me.right - halfLineHeight - scaleLabelPadding.top; + scaleLabelY = me.top + ((me.bottom - me.top) / 2); + rotation = isLeft ? -0.5 * Math.PI : 0.5 * Math.PI; + } + + ctx.save(); + ctx.translate(scaleLabelX, scaleLabelY); + ctx.rotate(rotation); + ctx.textAlign = 'center'; + ctx.textBaseline = 'middle'; + ctx.fillStyle = scaleLabelFontColor; // render in correct colour + ctx.font = scaleLabelFont.string; + ctx.fillText(scaleLabel.labelString, 0, 0); + ctx.restore(); + }, + + draw: function(chartArea) { + var me = this; + + if (!me._isVisible()) { + return; + } + + me._drawGrid(chartArea); + me._drawTitle(chartArea); + me._drawLabels(chartArea); + }, + + /** + * @private + */ + _layers: function() { + var me = this; + var opts = me.options; + var tz = opts.ticks && opts.ticks.z || 0; + 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); + } + }]; + } + + return [{ + z: gz, + draw: function() { + me._drawGrid.apply(me, arguments); + me._drawTitle.apply(me, arguments); + } + }, { + z: tz, + draw: function() { + me._drawLabels.apply(me, arguments); + } + }]; } }); + +Scale.prototype._draw = Scale.prototype.draw; + +module.exports = Scale; diff --git a/src/scales/scale.radialLinear.js b/src/scales/scale.radialLinear.js index f75e594ad7f..47b1089d93d 100644 --- a/src/scales/scale.radialLinear.js +++ b/src/scales/scale.radialLinear.js @@ -224,53 +224,30 @@ function adjustPointPositionForLabelHeight(angle, textSize, position) { function drawPointLabels(scale) { var ctx = scale.ctx; var opts = scale.options; - var angleLineOpts = opts.angleLines; - var gridLineOpts = opts.gridLines; var pointLabelOpts = opts.pointLabels; - var lineWidth = valueOrDefault(angleLineOpts.lineWidth, gridLineOpts.lineWidth); - var lineColor = valueOrDefault(angleLineOpts.color, gridLineOpts.color); var tickBackdropHeight = getTickBackdropHeight(opts); - - 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.save(); + ctx.font = plFont.string; ctx.textBaseline = 'middle'; 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 - var extra = (i === 0 ? tickBackdropHeight / 2 : 0); - var pointLabelPosition = scale.getPointPosition(i, outerDistance + extra + 5); + // Extra pixels out for some label spacing + var extra = (i === 0 ? tickBackdropHeight / 2 : 0); + var pointLabelPosition = scale.getPointPosition(i, outerDistance + extra + 5); - // Keep this in loop since we may support array properties here - var pointLabelFontColor = valueAtIndexOrDefault(pointLabelOpts.fontColor, i, defaults.global.defaultFontColor); - ctx.fillStyle = pointLabelFontColor; + // Keep this in loop since we may support array properties here + var pointLabelFontColor = valueAtIndexOrDefault(pointLabelOpts.fontColor, i, defaults.global.defaultFontColor); + ctx.fillStyle = pointLabelFontColor; - var angleRadians = scale.getIndexAngle(i); - var angle = helpers.toDegrees(angleRadians); - ctx.textAlign = getTextAlignForAngle(angle); - adjustPointPositionForLabelHeight(angle, scale._pointLabelSizes[i], pointLabelPosition); - fillText(ctx, scale.pointLabels[i] || '', pointLabelPosition, plFont.lineHeight); - } + var angleRadians = scale.getIndexAngle(i); + var angle = helpers.toDegrees(angleRadians); + ctx.textAlign = getTextAlignForAngle(angle); + adjustPointPositionForLabelHeight(angle, scale._pointLabelSizes[i], pointLabelPosition); + fillText(ctx, scale.pointLabels[i] || '', pointLabelPosition, plFont.lineHeight); } ctx.restore(); } @@ -473,60 +450,109 @@ module.exports = LinearScaleBase.extend({ 0); }, - draw: function() { + /** + * @private + */ + _drawGrid: function() { var me = this; + var ctx = me.ctx; var opts = me.options; 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.pointLabels.display) { + drawPointLabels(me); + } - if (opts.display) { - var ctx = me.ctx; - var startAngle = this.getIndexAngle(0); - var tickFont = helpers.options._parseFont(tickOpts); + if (gridLineOpts.display) { + helpers.each(me.ticks, function(label, index) { + if (index !== 0) { + offset = me.getDistanceFromCenterForValue(me.ticksAsNumbers[index]); + drawRadiusLine(me, gridLineOpts, offset, index); + } + }); + } - if (opts.angleLines.display || opts.pointLabels.display) { - drawPointLabels(me); + 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]); } - helpers.each(me.ticks, function(label, index) { - // Don't draw a centre value (if it is minimum) - if (index > 0 || tickOpts.reverse) { - var yCenterOffset = me.getDistanceFromCenterForValue(me.ticksAsNumbers[index]); + 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(); + } - // Draw circular lines around the scale - if (gridLineOpts.display && index !== 0) { - drawRadiusLine(me, gridLineOpts, yCenterOffset, index); - } + ctx.restore(); + } + }, - if (tickOpts.display) { - var tickFontColor = valueOrDefault(tickOpts.fontColor, defaults.global.defaultFontColor); - ctx.font = tickFont.string; - - ctx.save(); - ctx.translate(me.xCenter, me.yCenter); - ctx.rotate(startAngle); - - if (tickOpts.showLabelBackdrop) { - var labelWidth = ctx.measureText(label).width; - ctx.fillStyle = tickOpts.backdropColor; - ctx.fillRect( - -labelWidth / 2 - tickOpts.backdropPaddingX, - -yCenterOffset - tickFont.size / 2 - tickOpts.backdropPaddingY, - labelWidth + tickOpts.backdropPaddingX * 2, - tickFont.size + tickOpts.backdropPaddingY * 2 - ); - } - - ctx.textAlign = 'center'; - ctx.textBaseline = 'middle'; - ctx.fillStyle = tickFontColor; - ctx.fillText(label, 0, -yCenterOffset); - 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 offset, width; + + ctx.save(); + ctx.font = tickFont.string; + ctx.translate(me.xCenter, me.yCenter); + 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) { + width = ctx.measureText(label).width; + ctx.fillStyle = tickOpts.backdropColor; + + ctx.fillRect( + -width / 2 - tickOpts.backdropPaddingX, + -offset - tickFont.size / 2 - tickOpts.backdropPaddingY, + width + tickOpts.backdropPaddingX * 2, + tickFont.size + tickOpts.backdropPaddingY * 2 + ); + } + + ctx.fillStyle = tickFontColor; + ctx.fillText(label, 0, -offset); + }); + + ctx.restore(); + }, + + /** + * @private + */ + _drawTitle: helpers.noop }); // INTERNAL: static default options, registered in src/index.js diff --git a/test/fixtures/scale.radialLinear/gridlines-no-z.json b/test/fixtures/scale.radialLinear/gridlines-no-z.json new file mode 100644 index 00000000000..362c519048d --- /dev/null +++ b/test/fixtures/scale.radialLinear/gridlines-no-z.json @@ -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 + } + } + } + } +} diff --git a/test/fixtures/scale.radialLinear/gridlines-no-z.png b/test/fixtures/scale.radialLinear/gridlines-no-z.png new file mode 100644 index 00000000000..c545c564d53 Binary files /dev/null and b/test/fixtures/scale.radialLinear/gridlines-no-z.png differ diff --git a/test/fixtures/scale.radialLinear/gridlines-z.json b/test/fixtures/scale.radialLinear/gridlines-z.json new file mode 100644 index 00000000000..add497b9a13 --- /dev/null +++ b/test/fixtures/scale.radialLinear/gridlines-z.json @@ -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 + } + } + } + } +} diff --git a/test/fixtures/scale.radialLinear/gridlines-z.png b/test/fixtures/scale.radialLinear/gridlines-z.png new file mode 100644 index 00000000000..61485f315e1 Binary files /dev/null and b/test/fixtures/scale.radialLinear/gridlines-z.png differ diff --git a/test/specs/core.scale.tests.js b/test/specs/core.scale.tests.js index 2f2c3b1dece..605707eb3c1 100644 --- a/test/specs/core.scale.tests.js +++ b/test/specs/core.scale.tests.js @@ -473,4 +473,137 @@ describe('Core.scale', function() { 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); + + }); + + }); });