From: Evert Timberg Date: Sun, 1 May 2016 16:40:14 +0000 (-0400) Subject: Improve time scale for zoom and pan X-Git-Tag: 2.1.0~3 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=38373300eef9fe31204e8421cae1263a5f3b94d0;p=thirdparty%2FChart.js.git Improve time scale for zoom and pan * Improve time scale for zoom and pan Improve category scale when zoomed * Fix CI test --- diff --git a/src/scales/scale.category.js b/src/scales/scale.category.js index 6cde7200f..cca238ed9 100644 --- a/src/scales/scale.category.js +++ b/src/scales/scale.category.js @@ -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) { @@ -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) { @@ -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); @@ -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(); diff --git a/src/scales/scale.time.js b/src/scales/scale.time.js index af3a624a6..2feeda863 100644 --- a/src/scales/scale.time.js +++ b/src/scales/scale.time.js @@ -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]; } } @@ -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; @@ -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 @@ -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 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 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 @@ -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); @@ -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) { diff --git a/test/scale.time.tests.js b/test/scale.time.tests.js index 97afbe54a..5267a2b86 100644 --- a/test/scale.time.tests.js +++ b/test/scale.time.tests.js @@ -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 @@ -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() { @@ -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() { @@ -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() { @@ -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' });