-
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
Conversation
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.
Overall I think this is looking pretty good. Is it possible to see a test fiddle of it in action to play with? I think it might be a good idea to add a comment that explains what an "undefined" border is. It will help when others look at the code.
context.stroke(); | ||
context.restore(); | ||
} | ||
|
||
if (optionTicks.display) { |
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 with null
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
or undefined
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 filtering
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.
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
src/plugins/plugin.gridLines.js
Outdated
// Draws all the borders. Skips undefined orders which would be overlapped by a defined border | ||
helpers.each(bordersToDraw, function(borderToDraw) { | ||
// When the border is undefined, check if there isn't a defined border on the same place, if there is one dont draw this border | ||
if (!borderToDraw.undefinedBorder || bordersToDraw.findIndex(bordersOverlap, borderToDraw) === -1) { |
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.
I realize this might not have good performance, but I think it's fine given that the number of axes is usually small
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.
there will always be numberOfAxes+4
number of items in that array, so as you said it should not really have any impact on performance
Made a jsfiddle. |
src/plugins/plugin.gridLines.js
Outdated
} | ||
|
||
return { | ||
id: 'gridLines', |
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.
I still need to fully review that PR but a first feedback according the plugin guidelines: the id should be lowercase (id: 'gridlines'
). Can you also rename the file to: src/plugins/plugin.gridlines.js
?
@simonbrunel have you had a chance to review this further? |
@Zamaroth could you rebase this agains the latest changes in master? |
I´ll take a look at it |
@etimberg not yet, will try as soon as I can |
I rebased the branch against latest master and updated the jsFiddle |
@Zamaroth here's one test case for this pr: http://jsfiddle.net/andig2/vLgosao7/ ressources copied from your fiddle https://jsfiddle.net/y0whvnvk/4/ One issue I'm seeing is that it doesn't respect min/max settings on the x axis (recently fixed in #4522), maybe the fiddle is not yet using your recent rebased version? |
I refactored the code, rebased the branch against latest master and updated the jsFiddle. |
min/max is now honored as well- thanks! |
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.
Thanks @Zamaroth, can you rebase and resolve conflicts. We still need to figure out if that PR introduces behavior/breaking changes before merging.
src/plugins/plugin.gridlines.js
Outdated
'use strict'; | ||
|
||
module.exports = function(Chart) { | ||
var helpers = Chart.helpers; |
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.
We changed the way how to require dependencies (see this code)
src/plugins/plugin.gridlines.js
Outdated
|
||
var lineWidth, lineColor, borderDash, borderDashOffset; | ||
|
||
if (tickIndex === (typeof scale.zeroLineIndex !== 'undefined' ? scale.zeroLineIndex : 0)) { |
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.
I think we should honor the zero line only if explicitly defined by the scale:
if (tickIndex === scale.zeroLineIndex) {
// ...
}
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.
im gonna change this statement to tickIndex !== undefined && tickIndex === scale.zeroLineIndex
, because i send undefined to tickIndex when getting the last extra gridLine when offsetGridLines is enabled...
link
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.
zeroLineColor
? do you mean zeroLineIndex
?
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.
yes, i meant zeroLineIndex... already corrected the comment
src/plugins/plugin.gridlines.js
Outdated
var globalDefaults = Chart.defaults.global; | ||
|
||
// Stores all gridLines that should be displayed | ||
var 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.
A plugin is potentially called for many charts, so it's not safe to assume that the update
and draw
will be called synchronously per chart (especially if there is animations). This lines
array would contain lines for different charts.
src/plugins/plugin.gridlines.js
Outdated
} | ||
|
||
function collectVisibleGridLines(chart) { | ||
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.
This method should build a local lines
array and return it.
src/plugins/plugin.gridlines.js
Outdated
var glHashByOrientantion = gridLinesHash[scale.isHorizontal() ? 'horizontal' : 'vertical']; | ||
var position; | ||
|
||
for (var tickIndex = 0; tickIndex < scale.ticks.length; tickIndex++) { |
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.
Can you gather all var declarations at the top of the current scope (which is the each callback)? But also cache the loop end condition: for (i = 0, ilen = scale.ticks.length; i < ilen; ++i)
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.
you mean where the scaleOptions is declared?
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.
Yes, you should try to gather vars in the same scope this way to help minification.
helpers.each(chart.scales, function(scale) {
var scaleOptions = scale.options;
var glHashByOrientantion, position, i, ilen, tick;
//...
});
src/plugins/plugin.gridlines.js
Outdated
id: 'gridlines', | ||
|
||
afterUpdate: function(chart) { | ||
collectVisibleGridLines(chart); |
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.
You need to store the lines
per chart and a good way to do that is to add an plugin specific private model under chart
(see this example).
var MODEL_KEY = '$gridlines';
module.exports = function(Chart) {
// ...
return {
afterUpdate: function(chart) {
chart[MODEL_KEY] = {
lines: collectVisibleGridLines(chart);
};
}
beforeDraw: function(chart) {
var model = chart[MODEL_KEY];
if (model) {
drawGridLines(chart.ctx, model.lines);
}
}
};
}
src/core/core.scale.js
Outdated
context.stroke(); | ||
context.restore(); | ||
} | ||
|
||
if (optionTicks.display) { | ||
if (optionTicks.display && itemToDraw.label !== undefined && itemToDraw.label !== '') { |
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.
&& itemToDraw.label
will test against all empty values.
src/core/core.scale.js
Outdated
tmWidth: lineWidth, | ||
tmColor: lineColor, | ||
tmBorderDash: borderDash, | ||
tmBorderDashOffset: borderDashOffset, | ||
rotation: -1 * labelRotationRadians, | ||
label: label, |
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.
I would force a conversion of label to string: label: '' + label
, then we can test if (label)
for empty value.
src/core/core.scale.js
Outdated
var y1 = me.top; | ||
var y2 = me.bottom; | ||
// gridLines.drawBorder is deprecated | ||
if (gridLines.drawBorder && options.borderColor !== null && options.borderWidth !== 0 && options.borderWidth !== null) { |
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.
if (gridLines.drawBorder && options.borderColor && options.borderWidth) {
should be enough, right?
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.
we discussed that borderWidth === 0
should disable the border and it should not be drawn. Thats why I check for it here. Its also checked while collecting the gridLines to determine if it should be added to the hash
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.
if (options.borderWidth)
is the same as options.borderWidth !== 0 && options.borderWidth !== null
but also includes undefined
, false
and ''
, which I think are also values that are supposed to remove the grid 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.
oh I see... then what you suggested should be enough
src/core/core.scale.js
Outdated
tx2: !isHorizontal ? xTickEnd : chartArea.right + aliasPixel, | ||
ty2: !isHorizontal ? chartArea.bottom + aliasPixel : yTickEnd, | ||
tmWidth: gridLines.lineWidth, | ||
tmColor: gridLines.color, |
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.
This will not work if gridLines.lineWidth
or gridLines.color
is an array
@Zamaroth would you be able to rebase and address Simon's comments? |
I can. I actually adressed the comments(only locally), but i had a problem with issue #4545, which has been already merged few days after the comment. It removed logic of offsetting gridLines when offsetGridLines is enabled from getPixelForTick and moved it to getLineValue on core.scale.js, which if I remember correctly cant use from inside the plugin. |
I adressed the comments, rebased the branch against latest master and updated the jsFiddle |
src/plugins/plugin.gridlines.js
Outdated
|
||
// Stores all gridLines that should be displayed | ||
var lines = []; | ||
// 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 comment
The 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
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.
Looked through the code. I haven't run this yet
src/core/core.scale.js
Outdated
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 comment
The reason will be displayed to describe this comment to others. Learn more.
I would put the var aliasPixel = helpers.aliasPixel(context.lineWidth)
back in since it minifies better
src/plugins/plugin.gridlines.js
Outdated
// 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 comment
The reason will be displayed to describe this comment to others. Learn more.
[nit] orientation
has a typo in it
src/plugins/plugin.gridlines.js
Outdated
// 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 comment
The reason will be displayed to describe this comment to others. Learn more.
[nit] same here
|
||
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 comment
The 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 comment
The reason will be displayed to describe this comment to others. Learn more.
I think so
@simonbrunel have you had a chance to look at this since it was rebased? |
@Zamaroth sorry that this PR has been open for so long :-( I'm afraid it will need to be rebased again. @simonbrunel thoughts on this one? |
@Zamaroth just a quick reminder to rebase this PR when you get a chance |
Did the rebase for this here #5480 |
I'm going to close this PR as inactive in favor of the rebased PR that @okcoker opened |
@benmccann need anything from me here? |
This pull request is an extended version of pull request #4058, discussed directly with @etimberg .
Summary:
Created a separate plugin for drawing of gridLines and moved them outside of core.scale.js to allow better control over them and interaction between them, solving multiple problems regarding the gridLines.
Also added the border functionality to allow separate configuration of axisLine discussed in issue #4041
Solved problems:
However the axisLine of an adjacement axis could never be completely hidden because of the overlapping of gridLines.
Also the axisLine color was the same as gridLines of that axis, but it has the opposite direction. For better explanation check Coloring an axis line separately from gridLines #4041
Added functionality:
I havent updated the documentation yet, but I want to start the pull request already as there is quite a lot to review and update the documentation while it is being reviewed.
Also please ignore the first two commits by Cizmarik as his pull request was already merged yesterday with those changes. Also all the merge commits as they were just the result of updating our fork, caused by us being new and a bit confused with this whole git thing :)