Skip to content

Commit

Permalink
Improve time scale for zoom and pan
Browse files Browse the repository at this point in the history
* Improve time scale for zoom and pan
Improve category scale when zoomed

* Fix CI test
  • Loading branch information
etimberg authored and tannerlinsley committed May 1, 2016
1 parent 81a5b4e commit 3837330
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 26 deletions.
9 changes: 4 additions & 5 deletions src/scales/scale.category.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ module.exports = function(Chart) {
// Implement this so that
determineDataLimits: function() {
this.minIndex = 0;
this.maxIndex = this.chart.data.labels.length;
this.maxIndex = this.chart.data.labels.length - 1;
var findIndex;

if (this.options.ticks.min !== undefined) {
Expand All @@ -33,7 +33,7 @@ module.exports = function(Chart) {

buildTicks: function(index) {
// If we are viewing some subset of labels, slice the original array
this.ticks = (this.minIndex === 0 && this.maxIndex === this.chart.data.labels.length) ? this.chart.data.labels : this.chart.data.labels.slice(this.minIndex, this.maxIndex + 1);
this.ticks = (this.minIndex === 0 && this.maxIndex === this.chart.data.labels.length - 1) ? this.chart.data.labels : this.chart.data.labels.slice(this.minIndex, this.maxIndex + 1);
},

getLabelForIndex: function(index, datasetIndex) {
Expand All @@ -43,7 +43,7 @@ module.exports = function(Chart) {
// Used to get data value locations. Value can either be an index or a numerical value
getPixelForValue: function(value, index, datasetIndex, includeOffset) {
// 1 is added because we need the length but we have the indexes
var offsetAmt = Math.max((this.ticks.length - ((this.options.gridLines.offsetGridLines) ? 0 : 1)), 1);
var offsetAmt = Math.max((this.maxIndex + 1 - this.minIndex - ((this.options.gridLines.offsetGridLines) ? 0 : 1)), 1);

if (this.isHorizontal()) {
var innerWidth = this.width - (this.paddingLeft + this.paddingRight);
Expand All @@ -70,8 +70,7 @@ module.exports = function(Chart) {
getPixelForTick: function(index, includeOffset) {
return this.getPixelForValue(this.ticks[index], index + this.minIndex, null, includeOffset);
},
getValueForPixel: function(pixel)
{
getValueForPixel: function(pixel) {
var value
; var offsetAmt = Math.max((this.ticks.length - ((this.options.gridLines.offsetGridLines) ? 0 : 1)), 1);
var horz = this.isHorizontal();
Expand Down
32 changes: 19 additions & 13 deletions src/scales/scale.time.js
Original file line number Diff line number Diff line change
Expand Up @@ -208,8 +208,9 @@ module.exports = function(Chart) {
unitDefinition = time.units[unitDefinitionIndex];

this.tickUnit = unitDefinition.name;
this.leadingUnitBuffer = this.firstTick.diff(this.firstTick.clone().startOf(this.tickUnit), this.tickUnit, true);
this.scaleSizeInUnits = this.lastTick.diff(this.firstTick, this.tickUnit, true) + (this.leadingUnitBuffer > 0 ? 2 : 0);
var leadingUnitBuffer = this.firstTick.diff(this.firstTick.clone().startOf(this.tickUnit), this.tickUnit, true);
var trailingUnitBuffer = this.lastTick.clone().add(1, this.tickUnit).startOf(this.tickUnit).diff(this.lastTick, this.tickUnit, true);
this.scaleSizeInUnits = this.lastTick.diff(this.firstTick, this.tickUnit, true) + leadingUnitBuffer + trailingUnitBuffer;
this.displayFormat = this.options.time.displayFormats[unitDefinition.name];
}
}
Expand All @@ -227,7 +228,11 @@ module.exports = function(Chart) {

// Only round the last tick if we have no hard maximum
if (!this.options.time.max) {
this.lastTick.endOf(this.tickUnit);
var roundedEnd = this.lastTick.clone().startOf(this.tickUnit);
if (roundedEnd.diff(this.lastTick, this.tickUnit, true) !== 0) {
// Do not use end of because we need this to be in the next time unit
this.lastTick.add(1, this.tickUnit).startOf(this.tickUnit);
}
}

this.smallestLabelSeparation = this.width;
Expand All @@ -247,7 +252,7 @@ module.exports = function(Chart) {
this.ticks.push(this.firstTick.clone());

// For every unit in between the first and last moment, create a moment and add it to the ticks tick
for (var i = 1; i < this.scaleSizeInUnits; ++i) {
for (var i = 1; i <= this.scaleSizeInUnits; ++i) {
var newTick = roundedStart.clone().add(i, this.tickUnit);

// Are we greater than the max time
Expand All @@ -261,18 +266,19 @@ module.exports = function(Chart) {
}

// Always show the right tick
if (this.ticks[this.ticks.length - 1].diff(this.lastTick, this.tickUnit) !== 0 || this.scaleSizeInUnits === 0) {
// this is a weird case. If the <max> option is the same as the end option, we can't just diff the times because the tick was created from the roundedStart
// but the last tick was not rounded.
var diff = this.ticks[this.ticks.length - 1].diff(this.lastTick, this.tickUnit);
if (diff !== 0 || this.scaleSizeInUnits === 0) {
// this is a weird case. If the <max> option is the same as the end option, we can't just diff the times because the tick was created from the roundedStart
// but the last tick was not rounded.
if (this.options.time.max) {
this.ticks.push(this.lastTick.clone());
this.scaleSizeInUnits = this.lastTick.diff(this.ticks[0], this.tickUnit, true);
} else {
this.scaleSizeInUnits = Math.ceil(this.scaleSizeInUnits / this.unitScale) * this.unitScale;
this.ticks.push(this.firstTick.clone().add(this.scaleSizeInUnits, this.tickUnit));
this.lastTick = this.ticks[this.ticks.length - 1].clone();
this.ticks.push(this.lastTick.clone());
this.scaleSizeInUnits = this.lastTick.diff(this.firstTick, this.tickUnit, true);
}
}

this.ctx.restore();
},
// Get tooltip label
Expand Down Expand Up @@ -304,12 +310,12 @@ module.exports = function(Chart) {
this.ticks = this.ticks.map(this.tickFormatFunction, this);
},
getPixelForValue: function(value, index, datasetIndex, includeOffset) {
var labelMoment = this.getLabelMoment(datasetIndex, index);
var labelMoment = value && value.isValid && value.isValid() ? value : this.getLabelMoment(datasetIndex, index);

if (labelMoment) {
var offset = labelMoment.diff(this.firstTick, this.tickUnit, true);

var decimal = offset / (this.scaleSizeInUnits - (this.leadingUnitBuffer > 0 ? 1 : 0));
var decimal = offset / this.scaleSizeInUnits;

if (this.isHorizontal()) {
var innerWidth = this.width - (this.paddingLeft + this.paddingRight);
Expand All @@ -329,7 +335,7 @@ module.exports = function(Chart) {
getValueForPixel: function(pixel) {
var innerDimension = this.isHorizontal() ? this.width - (this.paddingLeft + this.paddingRight) : this.height - (this.paddingTop + this.paddingBottom);
var offset = (pixel - (this.isHorizontal() ? this.left + this.paddingLeft : this.top + this.paddingTop)) / innerDimension;
offset *= (this.scaleSizeInUnits - (this.leadingUnitBuffer > 0 ? 1 : 0));
offset *= this.scaleSizeInUnits;
return this.firstTick.clone().add(moment.duration(offset, this.tickUnit).asSeconds(), 'seconds');
},
parseTime: function(label) {
Expand Down
17 changes: 9 additions & 8 deletions test/scale.time.tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ describe('Time scale tests', function() {
var result = false;

var diff = actual.diff(expected.value, expected.unit, true);
result = Math.abs(diff) < 0.5;
result = Math.abs(diff) < (expected.threshold !== undefined ? expected.threshold : 0.5);

return {
pass: result
Expand Down Expand Up @@ -118,7 +118,7 @@ describe('Time scale tests', function() {
scale.update(400, 50);

// Counts down because the lines are drawn top to bottom
expect(scale.ticks).toEqual([ 'Dec 28, 2014', 'Jan 11, 2015' ]);
expect(scale.ticks).toEqual([ 'Dec 28, 2014', 'Jan 4, 2015', 'Jan 11, 2015' ]);
});

it('should build ticks using date objects', function() {
Expand Down Expand Up @@ -146,7 +146,7 @@ describe('Time scale tests', function() {
scale.update(400, 50);

// Counts down because the lines are drawn top to bottom
expect(scale.ticks).toEqual([ 'Dec 28, 2014', 'Jan 11, 2015' ]);
expect(scale.ticks).toEqual([ 'Dec 28, 2014', 'Jan 4, 2015', 'Jan 11, 2015' ]);
});

it('should build ticks when the data is xy points', function() {
Expand Down Expand Up @@ -244,8 +244,8 @@ describe('Time scale tests', function() {
var xScale = chartInstance.scales.xScale0;

// Counts down because the lines are drawn top to bottom
expect(xScale.ticks[0]).toEqualOneOf(['Nov 19, 1981', 'Nov 20, 1981']); // handle time zone changes
expect(xScale.ticks[1]).toEqualOneOf(['Nov 19, 1981', 'Nov 20, 1981']); // handle time zone changes
expect(xScale.ticks[0]).toEqualOneOf(['Nov 19, 1981', 'Nov 20, 1981', 'Nov 21, 1981']); // handle time zone changes
expect(xScale.ticks[1]).toEqualOneOf(['Nov 19, 1981', 'Nov 20, 1981', 'Nov 21, 1981']); // handle time zone changes
});

it('should build ticks using the config unit', function() {
Expand Down Expand Up @@ -356,13 +356,14 @@ describe('Time scale tests', function() {
var xScale = chartInstance.scales.xScale0;

expect(xScale.getPixelForValue('', 0, 0)).toBeCloseToPixel(78);
expect(xScale.getPixelForValue('', 6, 0)).toBeCloseToPixel(466);
expect(xScale.getPixelForValue('', 6, 0)).toBeCloseToPixel(452);

expect(xScale.getValueForPixel(78)).toBeCloseToTime({
value: moment(chartInstance.data.labels[0]),
unit: 'hour'
unit: 'hour',
threshold: 0.75
});
expect(xScale.getValueForPixel(466)).toBeCloseToTime({
expect(xScale.getValueForPixel(452)).toBeCloseToTime({
value: moment(chartInstance.data.labels[6]),
unit: 'hour'
});
Expand Down

0 comments on commit 3837330

Please sign in to comment.