Skip to content

Commit 48a49e0

Browse files
committed
Remove ticksToTimestamps and change ticks to numbers
1 parent 3427479 commit 48a49e0

17 files changed

+188
-178
lines changed

docs/getting-started/v3-migration.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -151,9 +151,8 @@ Animation system was completely rewritten in Chart.js v3. Each property can now
151151
##### Ticks
152152

153153
* `Scale.afterBuildTicks` now has no parameters like the other callbacks
154-
* `Scale.buildTicks` is now expected to return tick objects
155-
* `Scale.convertTicksToLabels` was renamed to `generateTickLabels`. It is now expected to set the label property on the ticks given as input
156-
* `Scale.ticks` now contains objects instead of strings
154+
* `Scale.ticks` now contains the tick values instead of labels, which are now available in `Scale.labels`
155+
* `TimeScale.buildTicks` is now expected to return `number`s like the other scales
157156
* When the `autoSkip` option is enabled, `Scale.ticks` now contains only the non-skipped ticks instead of all ticks.
158157

159158
##### Time Scale

src/core/core.scale.js

Lines changed: 60 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -122,16 +122,17 @@ function garbageCollect(caches, length) {
122122
* Returns {width, height, offset} objects for the first, last, widest, highest tick
123123
* labels where offset indicates the anchor point offset from the top in pixels.
124124
*/
125-
function computeLabelSizes(ctx, tickFonts, ticks, caches) {
126-
var length = ticks.length;
125+
function computeLabelSizes(ctx, tickFonts, labels, tickMeta, caches) {
126+
var length = labels.length;
127127
var widths = [];
128128
var heights = [];
129129
var offsets = [];
130-
var i, j, jlen, label, tickFont, fontString, cache, lineHeight, width, height, nestedLabel, widest, highest;
130+
var i, j, jlen, label, meta, tickFont, fontString, cache, lineHeight, width, height, nestedLabel, widest, highest;
131131

132132
for (i = 0; i < length; ++i) {
133-
label = ticks[i].label;
134-
tickFont = ticks[i].major ? tickFonts.major : tickFonts.minor;
133+
label = labels[i];
134+
meta = tickMeta[i];
135+
tickFont = meta && meta.major ? tickFonts.major : tickFonts.minor;
135136
ctx.font = fontString = tickFont.string;
136137
cache = caches[fontString] = caches[fontString] || {data: {}, gc: []};
137138
lineHeight = tickFont.lineHeight;
@@ -248,18 +249,18 @@ function calculateSpacing(majorIndices, ticks, axisLength, ticksLimit) {
248249
return Math.max(spacing, 1);
249250
}
250251

251-
function getMajorIndices(ticks) {
252+
function getMajorIndices(meta) {
252253
var result = [];
253254
var i, ilen;
254-
for (i = 0, ilen = ticks.length; i < ilen; i++) {
255-
if (ticks[i].major) {
255+
for (i = 0, ilen = meta.length; i < ilen; i++) {
256+
if (meta[i] && meta[i].major) {
256257
result.push(i);
257258
}
258259
}
259260
return result;
260261
}
261262

262-
function skipMajors(ticks, newTicks, majorIndices, spacing) {
263+
function skipMajors(ticks, meta, newTicks, newMeta, majorIndices, spacing) {
263264
let count = 0;
264265
let next = majorIndices[0];
265266
let i;
@@ -268,13 +269,14 @@ function skipMajors(ticks, newTicks, majorIndices, spacing) {
268269
for (i = 0; i < ticks.length; i++) {
269270
if (i === next) {
270271
newTicks.push(ticks[i]);
272+
newMeta.push(meta[i]);
271273
count++;
272274
next = majorIndices[count * spacing];
273275
}
274276
}
275277
}
276278

277-
function skip(ticks, newTicks, spacing, majorStart, majorEnd) {
279+
function skip(ticks, meta, newTicks, newMeta, spacing, majorStart, majorEnd) {
278280
const start = valueOrDefault(majorStart, 0);
279281
const end = Math.min(valueOrDefault(majorEnd, ticks.length), ticks.length);
280282
let count = 0;
@@ -296,6 +298,7 @@ function skip(ticks, newTicks, spacing, majorStart, majorEnd) {
296298
for (i = Math.max(start, 0); i < end; i++) {
297299
if (i === next) {
298300
newTicks.push(ticks[i]);
301+
newMeta.push(meta[i]);
299302
count++;
300303
next = Math.round(start + count * spacing);
301304
}
@@ -426,10 +429,10 @@ class Scale extends Element {
426429
* - thickness of scales or legends in another orientation
427430
*/
428431
update(maxWidth, maxHeight, margins) {
429-
var me = this;
430-
var tickOpts = me.options.ticks;
431-
var sampleSize = tickOpts.sampleSize;
432-
var samplingEnabled;
432+
const me = this;
433+
const tickOpts = me.options.ticks;
434+
const sampleSize = tickOpts.sampleSize;
435+
const autoSkipEnabled = tickOpts.autoSkip || tickOpts.source === 'auto';
433436

434437
// Update Lifecycle - Probably don't want to ever extend or overwrite this function ;)
435438
me.beforeUpdate();
@@ -445,6 +448,7 @@ class Scale extends Element {
445448
}, margins);
446449

447450
me.ticks = null;
451+
me._tickMeta = [];
448452
me._labelSizes = null;
449453
me._maxLabelLines = 0;
450454
me.longestLabelWidth = 0;
@@ -471,8 +475,8 @@ class Scale extends Element {
471475

472476
// Compute tick rotation and fit using a sampled subset of labels
473477
// We generally don't need to compute the size of every single label for determining scale size
474-
samplingEnabled = sampleSize < me.ticks.length;
475-
me._convertTicksToLabels(samplingEnabled ? sample(me.ticks, sampleSize) : me.ticks);
478+
const samplingEnabled = sampleSize < me.ticks.length;
479+
me.labels = me._convertTicksToLabels(samplingEnabled ? sample(me.ticks, sampleSize) : me.ticks);
476480

477481
// _configure is called twice, once here, once from core.controller.updateLayout.
478482
// Here we haven't been positioned yet, but dimensions are correct.
@@ -490,11 +494,13 @@ class Scale extends Element {
490494
me.afterFit();
491495

492496
// Auto-skip
493-
me.ticks = tickOpts.display && (tickOpts.autoSkip || tickOpts.source === 'auto') ? me._autoSkip(me.ticks) : me.ticks;
497+
if (tickOpts.display && autoSkipEnabled) {
498+
me._autoSkip();
499+
}
494500

495-
if (samplingEnabled) {
501+
if (tickOpts.display && (samplingEnabled || autoSkipEnabled)) {
496502
// Generate labels using all non-skipped ticks
497-
me._convertTicksToLabels(me.ticks);
503+
me.labels = me._convertTicksToLabels(me.ticks);
498504
}
499505

500506
// IMPORTANT: after this point, we consider that `this.ticks` will NEVER change!
@@ -588,14 +594,15 @@ class Scale extends Element {
588594
/**
589595
* Convert ticks to label strings
590596
*/
591-
generateTickLabels(ticks) {
597+
convertTicksToLabels(ticks) {
592598
var me = this;
593599
var tickOpts = me.options.ticks;
594-
var i, ilen, tick;
600+
var labels = [];
601+
var i, ilen;
595602
for (i = 0, ilen = ticks.length; i < ilen; i++) {
596-
tick = ticks[i];
597-
tick.label = helpers.callback(tickOpts.callback, [tick.value, i, ticks], me);
603+
labels[i] = helpers.callback(tickOpts.callback, [ticks[i], i, ticks], me);
598604
}
605+
return labels;
599606
}
600607
afterTickToLabelConversion() {
601608
helpers.callback(this.options.afterTickToLabelConversion, [this]);
@@ -786,12 +793,15 @@ class Scale extends Element {
786793

787794
_convertTicksToLabels(ticks) {
788795
var me = this;
796+
var labels;
789797

790798
me.beforeTickToLabelConversion();
791799

792-
me.generateTickLabels(ticks);
800+
labels = me.convertTicksToLabels(ticks);
793801

794802
me.afterTickToLabelConversion();
803+
804+
return labels;
795805
}
796806

797807
/**
@@ -802,7 +812,7 @@ class Scale extends Element {
802812
var labelSizes = me._labelSizes;
803813

804814
if (!labelSizes) {
805-
me._labelSizes = labelSizes = computeLabelSizes(me.ctx, parseTickFontOptions(me.options.ticks), me.ticks, me.longestTextCache);
815+
me._labelSizes = labelSizes = computeLabelSizes(me.ctx, parseTickFontOptions(me.options.ticks), me.labels, me._tickMeta, me.longestTextCache);
806816
me.longestLabelWidth = labelSizes.widest.width;
807817
}
808818

@@ -890,37 +900,46 @@ class Scale extends Element {
890900
* Returns a subset of ticks to be plotted to avoid overlapping labels.
891901
* @private
892902
*/
893-
_autoSkip(ticks) {
903+
_autoSkip() {
894904
const me = this;
895905
const tickOpts = me.options.ticks;
906+
const ticks = me.ticks;
907+
const meta = me._tickMeta;
896908
const axisLength = me._length;
897909
const ticksLimit = tickOpts.maxTicksLimit || axisLength / me._tickSize() + 1;
898-
const majorIndices = tickOpts.major.enabled ? getMajorIndices(ticks) : [];
910+
const majorIndices = tickOpts.major.enabled ? getMajorIndices(meta) : [];
899911
const numMajorIndices = majorIndices.length;
900912
const first = majorIndices[0];
901913
const last = majorIndices[numMajorIndices - 1];
902914
const newTicks = [];
915+
const newMeta = [];
903916

904917
// If there are too many major ticks to display them all
905918
if (numMajorIndices > ticksLimit) {
906-
skipMajors(ticks, newTicks, majorIndices, numMajorIndices / ticksLimit);
907-
return newTicks;
919+
skipMajors(ticks, meta, newTicks, newMeta, majorIndices, numMajorIndices / ticksLimit);
920+
me._tickMeta = newMeta;
921+
me.ticks = newTicks;
922+
return;
908923
}
909924

910925
const spacing = calculateSpacing(majorIndices, ticks, axisLength, ticksLimit);
911926

912927
if (numMajorIndices > 0) {
913928
let i, ilen;
914929
const avgMajorSpacing = numMajorIndices > 1 ? Math.round((last - first) / (numMajorIndices - 1)) : null;
915-
skip(ticks, newTicks, spacing, helpers.isNullOrUndef(avgMajorSpacing) ? 0 : first - avgMajorSpacing, first);
930+
skip(ticks, meta, newTicks, newMeta, spacing, helpers.isNullOrUndef(avgMajorSpacing) ? 0 : first - avgMajorSpacing, first);
916931
for (i = 0, ilen = numMajorIndices - 1; i < ilen; i++) {
917-
skip(ticks, newTicks, spacing, majorIndices[i], majorIndices[i + 1]);
932+
skip(ticks, meta, newTicks, newMeta, spacing, majorIndices[i], majorIndices[i + 1]);
918933
}
919-
skip(ticks, newTicks, spacing, last, helpers.isNullOrUndef(avgMajorSpacing) ? ticks.length : last + avgMajorSpacing);
920-
return newTicks;
934+
skip(ticks, meta, newTicks, newMeta, spacing, last, helpers.isNullOrUndef(avgMajorSpacing) ? ticks.length : last + avgMajorSpacing);
935+
me._tickMeta = newMeta;
936+
me.ticks = newTicks;
937+
return;
921938
}
922-
skip(ticks, newTicks, spacing);
923-
return newTicks;
939+
skip(ticks, meta, newTicks, newMeta, spacing);
940+
me._tickMeta = newMeta;
941+
me.ticks = newTicks;
942+
return;
924943
}
925944

926945
/**
@@ -942,8 +961,8 @@ class Scale extends Element {
942961

943962
// Calculate space needed for 1 tick in axis direction.
944963
return me.isHorizontal()
945-
? h * cos > w * sin ? w / cos : h / sin
946-
: h * sin < w * cos ? h / cos : w / sin;
964+
? (h * cos > w * sin ? w / cos : h / sin)
965+
: (h * sin < w * cos ? h / cos : w / sin);
947966
}
948967

949968
/**
@@ -1105,7 +1124,7 @@ class Scale extends Element {
11051124
const tl = getTickMarkLength(options.gridLines);
11061125
const rotation = -helpers.math.toRadians(me.labelRotation);
11071126
const items = [];
1108-
let i, ilen, tick, label, x, y, textAlign, pixel, font, lineHeight, lineCount, textOffset;
1127+
let i, ilen, meta, label, x, y, textAlign, pixel, font, lineHeight, lineCount, textOffset;
11091128

11101129
if (position === 'top') {
11111130
y = me.bottom - tl - tickPadding;
@@ -1140,11 +1159,11 @@ class Scale extends Element {
11401159
}
11411160

11421161
for (i = 0, ilen = ticks.length; i < ilen; ++i) {
1143-
tick = ticks[i];
1144-
label = tick.label;
1162+
meta = me._tickMeta[i];
1163+
label = me.labels[i];
11451164

11461165
pixel = me.getPixelForTick(i) + optionTicks.labelOffset;
1147-
font = tick.major ? fonts.major : fonts.minor;
1166+
font = meta && meta.major ? fonts.major : fonts.minor;
11481167
lineHeight = font.lineHeight;
11491168
lineCount = isArray(label) ? label.length : 1;
11501169

src/core/core.ticks.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ module.exports = {
3333
*/
3434
linear: function(tickValue, index, ticks) {
3535
// If we have lots of ticks, don't use the ones
36-
var delta = ticks.length > 3 ? ticks[2].value - ticks[1].value : ticks[1].value - ticks[0].value;
36+
var delta = ticks.length > 3 ? ticks[2] - ticks[1] : ticks[1] - ticks[0];
3737

3838
// If we have a number like 2.5 as the delta, figure out how many decimal places we need
3939
if (Math.abs(delta) > 1) {
@@ -47,7 +47,7 @@ module.exports = {
4747
var tickString = '';
4848

4949
if (tickValue !== 0) {
50-
var maxTick = Math.max(Math.abs(ticks[0].value), Math.abs(ticks[ticks.length - 1].value));
50+
var maxTick = Math.max(Math.abs(ticks[0]), Math.abs(ticks[ticks.length - 1]));
5151
if (maxTick < 1e-4) { // all ticks are small numbers; use scientific notation
5252
var logTick = math.log10(Math.abs(tickValue));
5353
var numExponential = Math.floor(logTick) - Math.floor(logDelta);

src/helpers/helpers.math.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,18 @@ export function almostWhole(x, epsilon) {
5555
return ((rounded - epsilon) <= x) && ((rounded + epsilon) >= x);
5656
}
5757

58+
export function _setMinAndMax(array, target) {
59+
var i, ilen, value;
60+
61+
for (i = 0, ilen = array.length; i < ilen; i++) {
62+
value = array[i];
63+
if (!isNaN(value)) {
64+
target.min = Math.min(target.min, value);
65+
target.max = Math.max(target.max, value);
66+
}
67+
}
68+
}
69+
5870
export function _setMinAndMaxByKey(array, target, property) {
5971
var i, ilen, value;
6072

src/scales/scale.category.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,7 @@ class CategoryScale extends Scale {
3535
me._valueRange = Math.max(labels.length - (offset ? 0 : 1), 1);
3636
me._startValue = me.min - (offset ? 0.5 : 0);
3737

38-
return labels.map(function(l) {
39-
return {value: l};
40-
});
38+
return labels;
4139
}
4240

4341
getLabelForValue(value) {

src/scales/scale.linearbase.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
'use strict';
22

33
import helpers from '../helpers/index';
4-
import {almostEquals, almostWhole, _decimalPlaces, _setMinAndMaxByKey, sign} from '../helpers/helpers.math';
4+
import {almostEquals, almostWhole, _decimalPlaces, _setMinAndMax, sign} from '../helpers/helpers.math';
55
import Scale from '../core/core.scale';
66

77
const isNullOrUndef = helpers.isNullOrUndef;
@@ -33,7 +33,7 @@ function generateTicks(generationOptions, dataRange) {
3333
// Beyond MIN_SPACING floating point numbers being to lose precision
3434
// such that we can't do the math necessary to generate ticks
3535
if (spacing < MIN_SPACING && isNullOrUndef(min) && isNullOrUndef(max)) {
36-
return [{value: rmin}, {value: rmax}];
36+
return [rmin, rmax];
3737
}
3838

3939
numSpaces = Math.ceil(rmax / spacing) - Math.floor(rmin / spacing);
@@ -75,11 +75,11 @@ function generateTicks(generationOptions, dataRange) {
7575

7676
niceMin = Math.round(niceMin * factor) / factor;
7777
niceMax = Math.round(niceMax * factor) / factor;
78-
ticks.push({value: isNullOrUndef(min) ? niceMin : min});
78+
ticks.push(isNullOrUndef(min) ? niceMin : min);
7979
for (var j = 1; j < numSpaces; ++j) {
80-
ticks.push({value: Math.round((niceMin + j * spacing) * factor) / factor});
80+
ticks.push(Math.round((niceMin + j * spacing) * factor) / factor);
8181
}
82-
ticks.push({value: isNullOrUndef(max) ? niceMax : max});
82+
ticks.push(isNullOrUndef(max) ? niceMax : max);
8383

8484
return ticks;
8585
}
@@ -216,7 +216,7 @@ class LinearScaleBase extends Scale {
216216

217217
// At this point, we need to update our max and min given the tick values since we have expanded the
218218
// range of the scale
219-
_setMinAndMaxByKey(ticks, me, 'value');
219+
_setMinAndMax(ticks, me);
220220

221221
if (opts.reverse) {
222222
ticks.reverse();

0 commit comments

Comments
 (0)