Skip to content

Commit 572b8c4

Browse files
committed
Remove ticksToTimestamps and change ticks to numbers
1 parent b1421c6 commit 572b8c4

18 files changed

+187
-178
lines changed

docs/getting-started/v3-migration.md

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

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

176175
##### Time Scale

src/core/core.scale.js

Lines changed: 57 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -192,18 +192,18 @@ function calculateSpacing(majorIndices, ticks, axisLength, ticksLimit) {
192192
return Math.max(spacing, 1);
193193
}
194194

195-
function getMajorIndices(ticks) {
195+
function getMajorIndices(meta) {
196196
const result = [];
197197
let i, ilen;
198-
for (i = 0, ilen = ticks.length; i < ilen; i++) {
199-
if (ticks[i].major) {
198+
for (i = 0, ilen = meta.length; i < ilen; i++) {
199+
if (meta[i] && meta[i].major) {
200200
result.push(i);
201201
}
202202
}
203203
return result;
204204
}
205205

206-
function skipMajors(ticks, newTicks, majorIndices, spacing) {
206+
function skipMajors(ticks, meta, newTicks, newMeta, majorIndices, spacing) {
207207
let count = 0;
208208
let next = majorIndices[0];
209209
let i;
@@ -212,13 +212,14 @@ function skipMajors(ticks, newTicks, majorIndices, spacing) {
212212
for (i = 0; i < ticks.length; i++) {
213213
if (i === next) {
214214
newTicks.push(ticks[i]);
215+
newMeta.push(meta[i]);
215216
count++;
216217
next = majorIndices[count * spacing];
217218
}
218219
}
219220
}
220221

221-
function skip(ticks, newTicks, spacing, majorStart, majorEnd) {
222+
function skip(ticks, meta, newTicks, newMeta, spacing, majorStart, majorEnd) {
222223
const start = valueOrDefault(majorStart, 0);
223224
const end = Math.min(valueOrDefault(majorEnd, ticks.length), ticks.length);
224225
let count = 0;
@@ -240,6 +241,7 @@ function skip(ticks, newTicks, spacing, majorStart, majorEnd) {
240241
for (i = Math.max(start, 0); i < end; i++) {
241242
if (i === next) {
242243
newTicks.push(ticks[i]);
244+
newMeta.push(meta[i]);
243245
count++;
244246
next = Math.round(start + count * spacing);
245247
}
@@ -373,7 +375,7 @@ class Scale extends Element {
373375
const me = this;
374376
const tickOpts = me.options.ticks;
375377
const sampleSize = tickOpts.sampleSize;
376-
let samplingEnabled;
378+
const autoSkipEnabled = tickOpts.autoSkip || tickOpts.source === 'auto';
377379

378380
// Update Lifecycle - Probably don't want to ever extend or overwrite this function ;)
379381
me.beforeUpdate();
@@ -390,6 +392,7 @@ class Scale extends Element {
390392
}, margins);
391393

392394
me.ticks = null;
395+
me._tickMeta = [];
393396
me._labelSizes = null;
394397
me._maxLabelLines = 0;
395398
me._longestTextCache = me._longestTextCache || {};
@@ -415,8 +418,8 @@ class Scale extends Element {
415418

416419
// Compute tick rotation and fit using a sampled subset of labels
417420
// We generally don't need to compute the size of every single label for determining scale size
418-
samplingEnabled = sampleSize < me.ticks.length;
419-
me._convertTicksToLabels(samplingEnabled ? sample(me.ticks, sampleSize) : me.ticks);
421+
const samplingEnabled = sampleSize < me.ticks.length;
422+
me.labels = me._convertTicksToLabels(samplingEnabled ? sample(me.ticks, sampleSize) : me.ticks);
420423

421424
// _configure is called twice, once here, once from core.controller.updateLayout.
422425
// Here we haven't been positioned yet, but dimensions are correct.
@@ -434,11 +437,13 @@ class Scale extends Element {
434437
me.afterFit();
435438

436439
// Auto-skip
437-
me.ticks = tickOpts.display && (tickOpts.autoSkip || tickOpts.source === 'auto') ? me._autoSkip(me.ticks) : me.ticks;
440+
if (tickOpts.display && autoSkipEnabled) {
441+
me._autoSkip();
442+
}
438443

439-
if (samplingEnabled) {
444+
if (tickOpts.display && (samplingEnabled || autoSkipEnabled)) {
440445
// Generate labels using all non-skipped ticks
441-
me._convertTicksToLabels(me.ticks);
446+
me.labels = me._convertTicksToLabels(me.ticks);
442447
}
443448

444449
// IMPORTANT: after this point, we consider that `this.ticks` will NEVER change!
@@ -528,14 +533,15 @@ class Scale extends Element {
528533
/**
529534
* Convert ticks to label strings
530535
*/
531-
generateTickLabels(ticks) {
536+
convertTicksToLabels(ticks) {
532537
const me = this;
533538
const tickOpts = me.options.ticks;
534-
let i, ilen, tick;
539+
const labels = [];
540+
let i, ilen;
535541
for (i = 0, ilen = ticks.length; i < ilen; i++) {
536-
tick = ticks[i];
537-
tick.label = helpers.callback(tickOpts.callback, [tick.value, i, ticks], me);
542+
labels[i] = helpers.callback(tickOpts.callback, [ticks[i], i, ticks], me);
538543
}
544+
return labels;
539545
}
540546
afterTickToLabelConversion() {
541547
helpers.callback(this.options.afterTickToLabelConversion, [this]);
@@ -729,9 +735,11 @@ class Scale extends Element {
729735

730736
me.beforeTickToLabelConversion();
731737

732-
me.generateTickLabels(ticks);
738+
const labels = me.convertTicksToLabels(ticks);
733739

734740
me.afterTickToLabelConversion();
741+
742+
return labels;
735743
}
736744

737745
/**
@@ -762,16 +770,17 @@ class Scale extends Element {
762770
const widths = [];
763771
const heights = [];
764772
const offsets = [];
765-
let ticks = me.ticks;
766-
if (sampleSize < ticks.length) {
767-
ticks = sample(ticks, sampleSize);
773+
let labels = me.labels;
774+
if (sampleSize < labels.length) {
775+
labels = sample(labels, sampleSize);
768776
}
769-
const length = ticks.length;
770-
let i, j, jlen, label, tickFont, fontString, cache, lineHeight, width, height, nestedLabel, widest, highest;
777+
const length = labels.length;
778+
let i, j, jlen, label, meta, tickFont, fontString, cache, lineHeight, width, height, nestedLabel, widest, highest;
771779

772780
for (i = 0; i < length; ++i) {
773-
label = ticks[i].label;
774-
tickFont = ticks[i].major ? tickFonts.major : tickFonts.minor;
781+
label = labels[i];
782+
meta = me._tickMeta[i];
783+
tickFont = meta && meta.major ? tickFonts.major : tickFonts.minor;
775784
ctx.font = fontString = tickFont.string;
776785
cache = caches[fontString] = caches[fontString] || {data: {}, gc: []};
777786
lineHeight = tickFont.lineHeight;
@@ -894,37 +903,46 @@ class Scale extends Element {
894903
* Returns a subset of ticks to be plotted to avoid overlapping labels.
895904
* @private
896905
*/
897-
_autoSkip(ticks) {
906+
_autoSkip() {
898907
const me = this;
899908
const tickOpts = me.options.ticks;
909+
const ticks = me.ticks;
910+
const meta = me._tickMeta;
900911
const axisLength = me._length;
901912
const ticksLimit = tickOpts.maxTicksLimit || axisLength / me._tickSize();
902-
const majorIndices = tickOpts.major.enabled ? getMajorIndices(ticks) : [];
913+
const majorIndices = tickOpts.major.enabled ? getMajorIndices(meta) : [];
903914
const numMajorIndices = majorIndices.length;
904915
const first = majorIndices[0];
905916
const last = majorIndices[numMajorIndices - 1];
906917
const newTicks = [];
918+
const newMeta = [];
907919

908920
// If there are too many major ticks to display them all
909921
if (numMajorIndices > ticksLimit) {
910-
skipMajors(ticks, newTicks, majorIndices, numMajorIndices / ticksLimit);
911-
return newTicks;
922+
skipMajors(ticks, meta, newTicks, newMeta, majorIndices, numMajorIndices / ticksLimit);
923+
me._tickMeta = newMeta;
924+
me.ticks = newTicks;
925+
return;
912926
}
913927

914928
const spacing = calculateSpacing(majorIndices, ticks, axisLength, ticksLimit);
915929

916930
if (numMajorIndices > 0) {
917931
let i, ilen;
918932
const avgMajorSpacing = numMajorIndices > 1 ? Math.round((last - first) / (numMajorIndices - 1)) : null;
919-
skip(ticks, newTicks, spacing, helpers.isNullOrUndef(avgMajorSpacing) ? 0 : first - avgMajorSpacing, first);
933+
skip(ticks, meta, newTicks, newMeta, spacing, helpers.isNullOrUndef(avgMajorSpacing) ? 0 : first - avgMajorSpacing, first);
920934
for (i = 0, ilen = numMajorIndices - 1; i < ilen; i++) {
921-
skip(ticks, newTicks, spacing, majorIndices[i], majorIndices[i + 1]);
935+
skip(ticks, meta, newTicks, newMeta, spacing, majorIndices[i], majorIndices[i + 1]);
922936
}
923-
skip(ticks, newTicks, spacing, last, helpers.isNullOrUndef(avgMajorSpacing) ? ticks.length : last + avgMajorSpacing);
924-
return newTicks;
937+
skip(ticks, meta, newTicks, newMeta, spacing, last, helpers.isNullOrUndef(avgMajorSpacing) ? ticks.length : last + avgMajorSpacing);
938+
me._tickMeta = newMeta;
939+
me.ticks = newTicks;
940+
return;
925941
}
926-
skip(ticks, newTicks, spacing);
927-
return newTicks;
942+
skip(ticks, meta, newTicks, newMeta, spacing);
943+
me._tickMeta = newMeta;
944+
me.ticks = newTicks;
945+
return;
928946
}
929947

930948
/**
@@ -946,8 +964,8 @@ class Scale extends Element {
946964

947965
// Calculate space needed for 1 tick in axis direction.
948966
return me.isHorizontal()
949-
? h * cos > w * sin ? w / cos : h / sin
950-
: h * sin < w * cos ? h / cos : w / sin;
967+
? (h * cos > w * sin ? w / cos : h / sin)
968+
: (h * sin < w * cos ? h / cos : w / sin);
951969
}
952970

953971
/**
@@ -1109,7 +1127,7 @@ class Scale extends Element {
11091127
const tl = getTickMarkLength(options.gridLines);
11101128
const rotation = -helpers.math.toRadians(me.labelRotation);
11111129
const items = [];
1112-
let i, ilen, tick, label, x, y, textAlign, pixel, font, lineHeight, lineCount, textOffset;
1130+
let i, ilen, meta, label, x, y, textAlign, pixel, font, lineHeight, lineCount, textOffset;
11131131

11141132
if (position === 'top') {
11151133
y = me.bottom - tl - tickPadding;
@@ -1144,11 +1162,11 @@ class Scale extends Element {
11441162
}
11451163

11461164
for (i = 0, ilen = ticks.length; i < ilen; ++i) {
1147-
tick = ticks[i];
1148-
label = tick.label;
1165+
meta = me._tickMeta[i];
1166+
label = me.labels[i];
11491167

11501168
pixel = me.getPixelForTick(i) + optionTicks.labelOffset;
1151-
font = tick.major ? fonts.major : fonts.minor;
1169+
font = meta && meta.major ? fonts.major : fonts.minor;
11521170
lineHeight = font.lineHeight;
11531171
lineCount = isArray(label) ? label.length : 1;
11541172

src/core/core.ticks.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ export default {
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 @@ export default {
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
@@ -58,6 +58,18 @@ export function almostWhole(x, epsilon) {
5858
return ((rounded - epsilon) <= x) && ((rounded + epsilon) >= x);
5959
}
6060

61+
export function _setMinAndMax(array, target) {
62+
var i, ilen, value;
63+
64+
for (i = 0, ilen = array.length; i < ilen; i++) {
65+
value = array[i];
66+
if (!isNaN(value)) {
67+
target.min = Math.min(target.min, value);
68+
target.max = Math.max(target.max, value);
69+
}
70+
}
71+
}
72+
6173
export function _setMinAndMaxByKey(array, target, property) {
6274
var i, ilen, value;
6375

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.linear.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ class LinearScale extends LinearScaleBase {
6767
if (index < 0 || index > ticks.length - 1) {
6868
return null;
6969
}
70-
return this.getPixelForValue(ticks[index].value);
70+
return this.getPixelForValue(ticks[index]);
7171
}
7272
}
7373

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)