]> git.ipfire.org Git - thirdparty/Chart.js.git/commitdiff
Improve time scale for zoom and pan
authorEvert Timberg <evert.timberg+github@gmail.com>
Sun, 1 May 2016 16:40:14 +0000 (12:40 -0400)
committerTanner Linsley <tannerlinsley@gmail.com>
Sun, 1 May 2016 16:40:14 +0000 (11:40 -0500)
* Improve time scale for zoom and pan
Improve category scale when zoomed

* Fix CI test

src/scales/scale.category.js
src/scales/scale.time.js
test/scale.time.tests.js

index 6cde7200fea10d83da1d32530e00809af94d2b36..cca238ed9f41b4e7fa517b00147c4b13940011a9 100644 (file)
@@ -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();
index af3a624a6f8dcbab07cee18207291412d3f81539..2feeda86392621b315299250ef4139896cbb4121 100644 (file)
@@ -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 <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
@@ -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) {
index 97afbe54ac441b5100a93d55ab0436e011653157..5267a2b86385fb713b227629423b43259d28eff3 100644 (file)
@@ -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'
                });