-
Notifications
You must be signed in to change notification settings - Fork 11.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Move drawing of gridLines into separate plugin and add border functionality #4117
Changes from 3 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,6 +9,10 @@ defaults._set('scale', { | |
display: true, | ||
position: 'left', | ||
offset: false, | ||
borderColor: 'rgba(0, 0, 0, 0.4)', | ||
borderWidth: 0, | ||
borderDash: [], | ||
borderDashOffset: 0.0, | ||
|
||
// grid line settings | ||
gridLines: { | ||
|
@@ -672,7 +676,7 @@ module.exports = function(Chart) { | |
}, | ||
|
||
// Actually draw the scale on the canvas | ||
// @param {rectangle} chartArea : the area of the chart to draw full grid lines on | ||
// @param {rectangle} chartArea : the area of the chart to draw all ticks and labels on | ||
draw: function(chartArea) { | ||
var me = this; | ||
var options = me.options; | ||
|
@@ -732,7 +736,7 @@ module.exports = function(Chart) { | |
} | ||
|
||
// Common properties | ||
var tx1, ty1, tx2, ty2, x1, y1, x2, y2, labelX, labelY; | ||
var tx1, ty1, tx2, ty2, labelX, labelY; | ||
var textAlign = 'middle'; | ||
var textBaseline = 'middle'; | ||
var tickPadding = optionTicks.padding; | ||
|
@@ -760,11 +764,9 @@ module.exports = function(Chart) { | |
|
||
labelX = me.getPixelForTick(index) + optionTicks.labelOffset; // x values for optionTicks (need to consider offsetLabel option) | ||
|
||
tx1 = tx2 = x1 = x2 = xLineValue; | ||
tx1 = tx2 = xLineValue; | ||
ty1 = yTickStart; | ||
ty2 = yTickEnd; | ||
y1 = chartArea.top; | ||
y2 = chartArea.bottom; | ||
} else { | ||
var isLeft = options.position === 'left'; | ||
var labelXOffset; | ||
|
@@ -789,43 +791,53 @@ module.exports = function(Chart) { | |
|
||
tx1 = xTickStart; | ||
tx2 = xTickEnd; | ||
x1 = chartArea.left; | ||
x2 = chartArea.right; | ||
ty1 = ty2 = y1 = y2 = yLineValue; | ||
ty1 = ty2 = yLineValue; | ||
} | ||
|
||
itemsToDraw.push({ | ||
tx1: tx1, | ||
ty1: ty1, | ||
tx2: tx2, | ||
ty2: ty2, | ||
x1: x1, | ||
y1: y1, | ||
x2: x2, | ||
y2: y2, | ||
labelX: labelX, | ||
labelY: labelY, | ||
glWidth: lineWidth, | ||
glColor: lineColor, | ||
glBorderDash: borderDash, | ||
glBorderDashOffset: borderDashOffset, | ||
tmWidth: lineWidth, | ||
tmColor: lineColor, | ||
tmBorderDash: borderDash, | ||
tmBorderDashOffset: borderDashOffset, | ||
rotation: -1 * labelRotationRadians, | ||
label: label, | ||
label: '' + label, | ||
major: tick.major, | ||
textBaseline: textBaseline, | ||
textAlign: textAlign | ||
}); | ||
}); | ||
|
||
// Draw all of the tick labels, tick marks, and grid lines at the correct places | ||
// When offsetGridLines is enabled, there should be one more tick mark than there | ||
// are ticks in scale.ticks array, therefore the missing gridLine has to be manually added | ||
if (gridLines.offsetGridLines) { | ||
var aliasPixel = helpers.aliasPixel(gridLines.lineWidth); | ||
itemsToDraw.push({ | ||
tx1: !isHorizontal ? xTickStart : chartArea.right + aliasPixel, | ||
ty1: !isHorizontal ? chartArea.bottom + aliasPixel : yTickStart, | ||
tx2: !isHorizontal ? xTickEnd : chartArea.right + aliasPixel, | ||
ty2: !isHorizontal ? chartArea.bottom + aliasPixel : yTickEnd, | ||
tmWidth: helpers.valueAtIndexOrDefault(gridLines.lineWidth, ticks.length), | ||
tmColor: helpers.valueAtIndexOrDefault(gridLines.color, ticks.length), | ||
tmBorderDash: gridLines.borderDash, | ||
tmBorderDashOffset: gridLines.borderDashOffset | ||
}); | ||
} | ||
|
||
// Draw all of the tick labels and tick marks at the correct places | ||
helpers.each(itemsToDraw, function(itemToDraw) { | ||
if (gridLines.display) { | ||
context.save(); | ||
context.lineWidth = itemToDraw.glWidth; | ||
context.strokeStyle = itemToDraw.glColor; | ||
context.lineWidth = itemToDraw.tmWidth; | ||
context.strokeStyle = itemToDraw.tmColor; | ||
if (context.setLineDash) { | ||
context.setLineDash(itemToDraw.glBorderDash); | ||
context.lineDashOffset = itemToDraw.glBorderDashOffset; | ||
context.setLineDash(itemToDraw.tmBorderDash); | ||
context.lineDashOffset = itemToDraw.tmBorderDashOffset; | ||
} | ||
|
||
context.beginPath(); | ||
|
@@ -835,16 +847,11 @@ module.exports = function(Chart) { | |
context.lineTo(itemToDraw.tx2, itemToDraw.ty2); | ||
} | ||
|
||
if (gridLines.drawOnChartArea) { | ||
context.moveTo(itemToDraw.x1, itemToDraw.y1); | ||
context.lineTo(itemToDraw.x2, itemToDraw.y2); | ||
} | ||
|
||
context.stroke(); | ||
context.restore(); | ||
} | ||
|
||
if (optionTicks.display) { | ||
if (optionTicks.display && itemToDraw.label) { | ||
// Make sure we draw text in the correct color and font | ||
context.save(); | ||
context.translate(itemToDraw.labelX, itemToDraw.labelY); | ||
|
@@ -901,30 +908,38 @@ module.exports = function(Chart) { | |
context.restore(); | ||
} | ||
|
||
if (gridLines.drawBorder) { | ||
// Draw the line at the edge of the axis | ||
context.lineWidth = helpers.valueAtIndexOrDefault(gridLines.lineWidth, 0); | ||
context.strokeStyle = helpers.valueAtIndexOrDefault(gridLines.color, 0); | ||
var x1 = me.left; | ||
var x2 = me.right; | ||
var y1 = me.top; | ||
var y2 = me.bottom; | ||
// gridLines.drawBorder is deprecated | ||
if (gridLines.drawBorder && options.borderColor && options.borderWidth) { | ||
// Draw the scale border | ||
context.lineWidth = options.borderWidth; | ||
context.strokeStyle = options.borderColor; | ||
if (context.setLineDash) { | ||
context.setLineDash(helpers.getValueOrDefault(options.borderDash, globalDefaults.borderDash)); | ||
context.lineDashOffset = helpers.getValueOrDefault(options.borderDashOffset, globalDefaults.borderDashOffset); | ||
} | ||
|
||
var x1 = Math.round(me.left); | ||
var x2 = Math.round(me.right); | ||
var y1 = Math.round(me.top); | ||
var y2 = Math.round(me.bottom); | ||
|
||
var aliasPixel = helpers.aliasPixel(context.lineWidth); | ||
if (isHorizontal) { | ||
y1 = y2 = options.position === 'top' ? me.bottom : me.top; | ||
y1 += aliasPixel; | ||
y2 += aliasPixel; | ||
y1 += helpers.aliasPixel(context.lineWidth); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would put the |
||
y2 += helpers.aliasPixel(context.lineWidth); | ||
} else { | ||
x1 = x2 = options.position === 'left' ? me.right : me.left; | ||
x1 += aliasPixel; | ||
x2 += aliasPixel; | ||
x1 += helpers.aliasPixel(context.lineWidth); | ||
x2 += helpers.aliasPixel(context.lineWidth); | ||
} | ||
|
||
context.beginPath(); | ||
|
||
context.moveTo(x1, y1); | ||
context.lineTo(x2, y2); | ||
|
||
context.stroke(); | ||
context.restore(); | ||
} | ||
} | ||
}); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,182 @@ | ||
'use strict'; | ||
|
||
var defaults = require('../core/core.defaults'); | ||
var helpers = require('../helpers/index'); | ||
var MODEL_KEY = '$gridlines'; | ||
|
||
// This is copied from core.scale.js, which is also required here and im not sure where it should be placed for both of them to access it | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should not be here, but i dont know where to move it for both core.scale.js and this plugin to access it |
||
function getLineValue(scale, index, offsetGridLines) { | ||
var lineValue = scale.getPixelForTick(index); | ||
|
||
if (offsetGridLines) { | ||
if (index === 0) { | ||
lineValue -= (scale.getPixelForTick(1) - lineValue) / 2; | ||
} else { | ||
lineValue -= (lineValue - scale.getPixelForTick(index - 1)) / 2; | ||
} | ||
} | ||
return lineValue; | ||
} | ||
|
||
module.exports = function() { | ||
function getGridLine(chart, scale, position, tickIndex) { | ||
var chartArea = chart.chartArea; | ||
var scaleOptions = scale.options; | ||
var gridLines = scaleOptions.gridLines; | ||
|
||
var lineWidth, lineColor, borderDash, borderDashOffset; | ||
|
||
if (tickIndex !== undefined && tickIndex === scale.zeroLineIndex) { | ||
lineWidth = gridLines.zeroLineWidth; | ||
lineColor = gridLines.zeroLineColor; | ||
borderDash = gridLines.zeroLineBorderDash; | ||
borderDashOffset = gridLines.zeroLineBorderDashOffset; | ||
} else { | ||
lineWidth = helpers.getValueAtIndexOrDefault(gridLines.lineWidth, tickIndex); | ||
lineColor = helpers.getValueAtIndexOrDefault(gridLines.color, tickIndex); | ||
borderDash = helpers.getValueOrDefault(gridLines.borderDash, defaults.global.borderDash); | ||
borderDashOffset = helpers.getValueOrDefault(gridLines.borderDashOffset, defaults.global.borderDashOffset); | ||
} | ||
|
||
var x1, x2, y1, y2; | ||
|
||
if (scale.isHorizontal()) { | ||
x1 = x2 = position; | ||
y1 = chartArea.top; | ||
y2 = chartArea.bottom; | ||
} else { | ||
x1 = chartArea.left; | ||
x2 = chartArea.right; | ||
y1 = y2 = position; | ||
} | ||
|
||
return { | ||
x1: x1, | ||
x2: x2, | ||
y1: y1, | ||
y2: y2, | ||
width: lineWidth, | ||
color: lineColor, | ||
borderDash: borderDash, | ||
borderDashOffset: borderDashOffset | ||
}; | ||
} | ||
|
||
function collectVisibleGridLines(chart) { | ||
var lines = []; | ||
|
||
// Temporary object that stores already collected gridLine positions to prevent gridLines from overlapping | ||
var gridLinesHash = { | ||
horizontal: {}, | ||
vertical: {} | ||
}; | ||
|
||
// Marks scale border positions to prevent overlapping of gridLines and scale borders | ||
helpers.each(chart.scales, function(scale) { | ||
var scaleOptions = scale.options; | ||
var glHashByOrientantion = gridLinesHash[!scale.isHorizontal() ? 'horizontal' : 'vertical']; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [nit] |
||
var borderPosition; | ||
|
||
// gridLines.drawBorder is deprecated | ||
if (scaleOptions.gridLines.drawBorder && scaleOptions.borderColor !== null && scaleOptions.borderWidth !== 0 && scaleOptions.borderWidth !== null) { | ||
if (scale.isHorizontal()) { | ||
borderPosition = scale.position === 'top' ? scale.bottom : scale.top; | ||
} else { | ||
borderPosition = scale.position === 'left' ? scale.right : scale.left; | ||
} | ||
|
||
glHashByOrientantion[Math.round(borderPosition)] = true; | ||
} | ||
}); | ||
|
||
// Collects gridLines | ||
helpers.each(chart.scales, function(scale) { | ||
var scaleOptions = scale.options; | ||
var glHashByOrientantion = gridLinesHash[scale.isHorizontal() ? 'horizontal' : 'vertical']; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [nit] same here |
||
var position; | ||
|
||
if (scaleOptions.display && scaleOptions.gridLines.display && scaleOptions.gridLines.drawOnChartArea) { | ||
for (var tickIndex = 0, ticksCount = scale.ticks.length; tickIndex < ticksCount; tickIndex++) { | ||
if (helpers.isNullOrUndef(scale.ticks[tickIndex])) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @simonbrunel @benmccann does this check make sense in the context of how ticks changed in 2.7.0? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think so |
||
continue; | ||
} | ||
|
||
position = getLineValue(scale, tickIndex, scaleOptions.gridLines.offsetGridLines && ticksCount > 1); | ||
|
||
if (glHashByOrientantion[position] === undefined) { | ||
glHashByOrientantion[position] = true; | ||
lines.push(getGridLine(chart, scale, position, tickIndex)); | ||
} | ||
} | ||
|
||
// When offsetGridLines is enabled, there should be one more gridLine than there | ||
// are ticks in scale.ticks array, therefore the missing gridLine has to be manually added | ||
if (scaleOptions.gridLines.offsetGridLines) { | ||
position = Math.round(!scale.isHorizontal() ? scale.bottom : scale.right); | ||
|
||
if (glHashByOrientantion[position] === undefined) { | ||
glHashByOrientantion[position] = true; | ||
lines.push(getGridLine(chart, scale, position, undefined)); | ||
} | ||
} | ||
} | ||
}); | ||
|
||
return lines; | ||
} | ||
|
||
function drawGridLines(ctx, lines) { | ||
var aliasPixel; | ||
var x1, x2, y1, y2; | ||
|
||
for (var i = 0, ilen = lines.length; i < ilen; i++) { | ||
var line = lines[i]; | ||
|
||
ctx.lineWidth = line.width; | ||
ctx.strokeStyle = line.color; | ||
if (ctx.setLineDash) { | ||
ctx.setLineDash(line.borderDash); | ||
ctx.lineDashOffset = line.borderDashOffset; | ||
} | ||
|
||
aliasPixel = helpers.aliasPixel(ctx.lineWidth); | ||
x1 = line.x1; | ||
x2 = line.x2; | ||
y1 = line.y1; | ||
y2 = line.y2; | ||
|
||
if (y1 === y2) { | ||
y1 += aliasPixel; | ||
y2 += aliasPixel; | ||
} else { | ||
x1 += aliasPixel; | ||
x2 += aliasPixel; | ||
} | ||
|
||
ctx.beginPath(); | ||
|
||
ctx.moveTo(x1, y1); | ||
ctx.lineTo(x2, y2); | ||
|
||
ctx.stroke(); | ||
ctx.restore(); | ||
} | ||
} | ||
|
||
return { | ||
id: 'gridlines', | ||
|
||
afterUpdate: function(chart) { | ||
chart[MODEL_KEY] = { | ||
lines: collectVisibleGridLines(chart) | ||
}; | ||
}, | ||
|
||
beforeDraw: function(chart) { | ||
var model = chart[MODEL_KEY]; | ||
if (model) { | ||
drawGridLines(chart.ctx, model.lines); | ||
} | ||
} | ||
}; | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might make sense to do this for any value of
itemToDraw.label
that isn't truthy. Blank labels don't need to be drawn and same withnull
labels. What do you think?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
items with null or undefined label aren't pushed into the array at all, thanks to this statement.
The blank labels don't need to be drawn tho. The question is if they should be skipped the same way as when it's null(its ticks and gridLines wont be drawn) or in the statement you linked so there will be a blank tick, but the label itself wont be drawn.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmmm, for grid lines we have the following logic:
if label is a string, draw label, tick and grid line
if label is
null
orundefined
hide the label, tick and gridline.This allows users to filter out grid lines by return
null
from the tick callback. here is an example of filteringThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i understand, i'll add just add the statement to skip the drawing of label itself if it's empty string then
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good