]> git.ipfire.org Git - thirdparty/Chart.js.git/commitdiff
Implemented aligment by senior unit in time axis. (#4267)
authorAndrew <hurskiy.andriy@gmail.com>
Mon, 29 May 2017 22:40:10 +0000 (01:40 +0300)
committerEvert Timberg <evert.timberg+github@gmail.com>
Mon, 29 May 2017 22:40:10 +0000 (18:40 -0400)
Implemented alignment by major unit in the time scale. This allows showing the first tick of a larger unit like days in a special way and is part of the basis of the time series scale.

samples/scales/time/line-point-data.html
samples/scales/time/line.html
src/helpers/helpers.time.js
src/scales/scale.time.js
test/specs/scale.time.tests.js

index 0a221624bf583ece971e9ef246f30444490ee9c9..e28f883f22360a560ed39bf0b1ed9ae20078e1bc 100644 (file)
                        },
                        options: {
                                responsive: true,
-                title:{
-                    display:true,
-                    text:"Chart.js Time Point Data"
-                },
+                               title:{
+                                       display:true,
+                                       text:"Chart.js Time Point Data"
+                               },
                                scales: {
                                        xAxes: [{
                                                type: "time",
index 221354f9ee2f295f71ff0d5d57eb68cbf835fdac..0cca931e0091ca3c6d7cf7c5b398eb326f57d758 100644 (file)
@@ -28,7 +28,7 @@
        <button id="removeData">Remove Data</button>
        <script>
                var timeFormat = 'MM/DD/YYYY HH:mm';
-               
+
                function newDate(days) {
                        return moment().add(days, 'd').toDate();
                }
                        type: 'line',
                        data: {
                                labels: [ // Date Objects
-                                       newDate(0), 
-                                       newDate(1), 
-                                       newDate(2), 
-                                       newDate(3), 
-                                       newDate(4), 
-                                       newDate(5), 
+                                       newDate(0),
+                                       newDate(1),
+                                       newDate(2),
+                                       newDate(3),
+                                       newDate(4),
+                                       newDate(5),
                                        newDate(6)
                                ],
                                datasets: [{
                                        borderColor: window.chartColors.red,
                                        fill: false,
                                        data: [
-                                               randomScalingFactor(), 
-                                               randomScalingFactor(), 
-                                               randomScalingFactor(), 
-                                               randomScalingFactor(), 
-                                               randomScalingFactor(), 
-                                               randomScalingFactor(), 
+                                               randomScalingFactor(),
+                                               randomScalingFactor(),
+                                               randomScalingFactor(),
+                                               randomScalingFactor(),
+                                               randomScalingFactor(),
+                                               randomScalingFactor(),
                                                randomScalingFactor()
                                        ],
                                }, {
                                        borderColor: window.chartColors.blue,
                                        fill: false,
                                        data: [
-                                               randomScalingFactor(), 
-                                               randomScalingFactor(), 
-                                               randomScalingFactor(), 
-                                               randomScalingFactor(), 
-                                               randomScalingFactor(), 
-                                               randomScalingFactor(), 
+                                               randomScalingFactor(),
+                                               randomScalingFactor(),
+                                               randomScalingFactor(),
+                                               randomScalingFactor(),
+                                               randomScalingFactor(),
+                                               randomScalingFactor(),
                                                randomScalingFactor()
                                        ],
                                }, {
index e055fb2b1b8448c84e0f9dc28d2cd703d90aff21..670917d1ab0d064232749470c66bf0141c3dfbed 100644 (file)
@@ -55,15 +55,25 @@ module.exports = function(Chart) {
                var ticks = [];
                if (options.maxTicks) {
                        var stepSize = options.stepSize;
-                       ticks.push(options.min !== undefined ? options.min : niceRange.min);
-                       var cur = moment(niceRange.min);
-                       while (cur.add(stepSize, options.unit).valueOf() < niceRange.max) {
-                               ticks.push(cur.valueOf());
+                       var startTick = options.min !== undefined ? options.min : niceRange.min;
+                       var majorUnit = options.majorUnit;
+                       var majorUnitStart = majorUnit ? moment(startTick).add(1, majorUnit).startOf(majorUnit) : startTick;
+                       var startRange = majorUnitStart.valueOf() - startTick;
+                       var stepValue = interval[options.unit].size * stepSize;
+                       var startFraction = startRange % stepValue;
+                       var alignedTick = startTick;
+                       if (startFraction && majorUnit && !options.timeOpts.round && !options.timeOpts.isoWeekday) {
+                               alignedTick += startFraction - stepValue;
+                               ticks.push(alignedTick);
+                       } else {
+                               ticks.push(startTick);
                        }
+                       var cur = moment(alignedTick);
                        var realMax = options.max || niceRange.max;
-                       if (ticks[ticks.length - 1] !== realMax) {
-                               ticks.push(realMax);
+                       while (cur.add(stepSize, options.unit).valueOf() < realMax) {
+                               ticks.push(cur.valueOf());
                        }
+                       ticks.push(cur.valueOf());
                }
                return ticks;
        }
@@ -128,6 +138,22 @@ module.exports = function(Chart) {
                        return unit;
                },
 
+               /**
+                * Determine major unit accordingly to passed unit
+                * @param unit {String} relative unit
+                * @return {String} major unit
+                */
+               determineMajorUnit: function(unit) {
+                       var units = Object.keys(interval);
+                       var majorUnit = null;
+                       var unitIndex = units.indexOf(unit);
+                       if (unitIndex < units.length - 1) {
+                               majorUnit = units[unitIndex + 1];
+                       }
+
+                       return majorUnit;
+               },
+
                /**
                 * Determines how we scale the unit
                 * @param min {Number} the scale minimum
@@ -169,7 +195,7 @@ module.exports = function(Chart) {
                generateTicks: function(options, dataRange) {
                        var niceMin;
                        var niceMax;
-                       var isoWeekday = options.isoWeekday;
+                       var isoWeekday = options.timeOpts.isoWeekday;
                        if (options.unit === 'week' && isoWeekday !== false) {
                                niceMin = moment(dataRange.min).startOf('isoWeek').isoWeekday(isoWeekday).valueOf();
                                niceMax = moment(dataRange.max).startOf('isoWeek').isoWeekday(isoWeekday);
index f99452eef775858a45bee1ed7f9a5e6a06cd0f33..f9f80386e8980f6c7ca977f2fb3a25df22988a22 100644 (file)
@@ -135,8 +135,14 @@ module.exports = function(Chart) {
                        }
 
                        var maxTicks = me.getLabelCapacity(minTimestamp || dataMin);
+
                        var unit = timeOpts.unit || timeHelpers.determineUnit(timeOpts.minUnit, minTimestamp || dataMin, maxTimestamp || dataMax, maxTicks);
+                       var majorUnit = timeHelpers.determineMajorUnit(unit);
+
                        me.displayFormat = timeOpts.displayFormats[unit];
+                       me.majorDisplayFormat = timeOpts.displayFormats[majorUnit];
+                       me.unit = unit;
+                       me.majorUnit = majorUnit;
 
                        var stepSize = timeOpts.stepSize || timeHelpers.determineStepSize(minTimestamp || dataMin, maxTimestamp || dataMax, unit, maxTicks);
                        me.ticks = timeHelpers.generateTicks({
@@ -144,8 +150,9 @@ module.exports = function(Chart) {
                                min: minTimestamp,
                                max: maxTimestamp,
                                stepSize: stepSize,
+                               majorUnit: majorUnit,
                                unit: unit,
-                               isoWeekday: timeOpts.isoWeekday
+                               timeOpts: timeOpts
                        }, {
                                min: dataMin,
                                max: dataMax
index b1ab8410c4bce3a409ecebc09352d776bac71efc..3843ee8d2f4c95492175a63bb371879802627fd9 100755 (executable)
@@ -122,7 +122,8 @@ describe('Time scale tests', function() {
                                labels: ['2015-01-01T12:00:00', '2015-01-02T21:00:00', '2015-01-03T22:00:00', '2015-01-05T23:00:00', '2015-01-07T03:00', '2015-01-08T10:00', '2015-01-10T12:00'], // days
                        };
 
-                       var scale = createScale(mockData, Chart.scaleService.getScaleDefaults('time'));
+                       var scaleOptions = Chart.scaleService.getScaleDefaults('time');
+                       var scale = createScale(mockData, scaleOptions);
                        scale.update(1000, 200);
                        expect(scale.ticks).toEqual(['Jan 1, 2015', 'Jan 2, 2015', 'Jan 3, 2015', 'Jan 4, 2015', 'Jan 5, 2015', 'Jan 6, 2015', 'Jan 7, 2015', 'Jan 8, 2015', 'Jan 9, 2015', 'Jan 10, 2015', 'Jan 11, 2015']);
                });
@@ -231,6 +232,7 @@ describe('Time scale tests', function() {
 
                var scale = createScale(mockData, config);
                scale.update(2500, 200);
+
                expect(scale.ticks).toEqual(['Jan 1, 8PM', 'Jan 1, 9PM', 'Jan 1, 10PM', 'Jan 1, 11PM', 'Jan 2, 12AM', 'Jan 2, 1AM', 'Jan 2, 2AM', 'Jan 2, 3AM', 'Jan 2, 4AM', 'Jan 2, 5AM', 'Jan 2, 6AM', 'Jan 2, 7AM', 'Jan 2, 8AM', 'Jan 2, 9AM', 'Jan 2, 10AM', 'Jan 2, 11AM', 'Jan 2, 12PM', 'Jan 2, 1PM', 'Jan 2, 2PM', 'Jan 2, 3PM', 'Jan 2, 4PM', 'Jan 2, 5PM', 'Jan 2, 6PM', 'Jan 2, 7PM', 'Jan 2, 8PM', 'Jan 2, 9PM']);
        });
 
@@ -285,7 +287,7 @@ describe('Time scale tests', function() {
                        config.time.max = '2015-01-05T06:00:00';
 
                        var scale = createScale(mockData, config);
-                       expect(scale.ticks[scale.ticks.length - 1]).toEqual('Jan 5, 2015');
+                       expect(scale.ticks[scale.ticks.length - 1]).toEqual('Jan 6, 2015');
                });
        });
 
@@ -329,34 +331,28 @@ describe('Time scale tests', function() {
 
                var xScale = chart.scales.xScale0;
 
-               it('should be bounded by the nearest day beginnings', function() {
-                       expect(xScale.getValueForPixel(xScale.left)).toBeCloseToTime({
-                               value: moment(chart.data.labels[0]).startOf('day'),
-                               unit: 'hour',
-                       });
-                       expect(xScale.getValueForPixel(xScale.right)).toBeCloseToTime({
-                               value: moment(chart.data.labels[chart.data.labels.length - 1]).endOf('day'),
-                               unit: 'hour',
-                       });
+               it('should be bounded by the nearest week beginnings', function() {
+                       expect(xScale.getValueForPixel(xScale.left)).toBeGreaterThan(moment(chart.data.labels[0]).startOf('week'));
+                       expect(xScale.getValueForPixel(xScale.right)).toBeLessThan(moment(chart.data.labels[chart.data.labels.length - 1]).add(1, 'week').endOf('week'));
                });
 
                it('should convert between screen coordinates and times', function() {
-                       var timeRange = moment('2015-01-11').valueOf() - moment('2015-01-01').valueOf();
-                       var msInHour = 3600000;
-                       var firstLabelAlong = 20 * msInHour / timeRange;
-                       var firstLabelPixel = xScale.left + (xScale.width * firstLabelAlong);
-                       var lastLabelAlong = (timeRange - (12 * msInHour)) / timeRange;
-                       var lastLabelPixel = xScale.left + (xScale.width * lastLabelAlong);
-
-                       expect(xScale.getPixelForValue('', 0, 0)).toBeCloseToPixel(firstLabelPixel);
-                       expect(xScale.getPixelForValue(chart.data.labels[0])).toBeCloseToPixel(firstLabelPixel);
-                       expect(xScale.getValueForPixel(firstLabelPixel)).toBeCloseToTime({
+                       var timeRange = moment(xScale.max).valueOf() - moment(xScale.min).valueOf();
+                       var msPerPix = timeRange / xScale.width;
+                       var firstPointOffsetMs = moment(chart.config.data.labels[0]).valueOf() - xScale.min;
+                       var firstPointPixel = xScale.left + firstPointOffsetMs / msPerPix;
+                       var lastPointOffsetMs = moment(chart.config.data.labels[chart.config.data.labels.length - 1]).valueOf() - xScale.min;
+                       var lastPointPixel = xScale.left + lastPointOffsetMs / msPerPix;
+
+                       expect(xScale.getPixelForValue('', 0, 0)).toBeCloseToPixel(firstPointPixel);
+                       expect(xScale.getPixelForValue(chart.data.labels[0])).toBeCloseToPixel(firstPointPixel);
+                       expect(xScale.getValueForPixel(firstPointPixel)).toBeCloseToTime({
                                value: moment(chart.data.labels[0]),
                                unit: 'hour',
                        });
 
-                       expect(xScale.getPixelForValue('', 6, 0)).toBeCloseToPixel(lastLabelPixel);
-                       expect(xScale.getValueForPixel(lastLabelPixel)).toBeCloseToTime({
+                       expect(xScale.getPixelForValue('', 6, 0)).toBeCloseToPixel(lastPointPixel);
+                       expect(xScale.getValueForPixel(lastPointPixel)).toBeCloseToTime({
                                value: moment(chart.data.labels[6]),
                                unit: 'hour'
                        });
@@ -382,26 +378,28 @@ describe('Time scale tests', function() {
 
                var xScale = chart.scales.xScale0;
                xScale.update(800, 200);
+               var step = xScale.ticksAsTimestamps[1] - xScale.ticksAsTimestamps[0];
+               var stepsAmount = Math.floor((xScale.max - xScale.min) / step);
 
-               it('should be bounded by nearest year starts', function() {
+               it('should be bounded by nearest step year starts', function() {
                        expect(xScale.getValueForPixel(xScale.left)).toBeCloseToTime({
-                               value: moment(chart.data.labels[0]).startOf('year'),
+                               value: moment(xScale.min).startOf('year'),
                                unit: 'hour',
                        });
                        expect(xScale.getValueForPixel(xScale.right)).toBeCloseToTime({
-                               value: moment(chart.data.labels[chart.data.labels - 1]).endOf('year'),
+                               value: moment(xScale.min + step * stepsAmount).endOf('year'),
                                unit: 'hour',
                        });
                });
 
                it('should build the correct ticks', function() {
-                       // Where 'correct' is a two year spacing, except the last tick which is the year end of the last point.
-                       expect(xScale.ticks).toEqual(['2005', '2007', '2009', '2011', '2013', '2015', '2017', '2018']);
+                       // Where 'correct' is a two year spacing.
+                       expect(xScale.ticks).toEqual(['2005', '2007', '2009', '2011', '2013', '2015', '2017', '2019']);
                });
 
                it('should have ticks with accurate labels', function() {
                        var ticks = xScale.ticks;
-                       var pixelsPerYear = xScale.width / 13;
+                       var pixelsPerYear = xScale.width / 14;
 
                        for (var i = 0; i < ticks.length - 1; i++) {
                                var offset = 2 * pixelsPerYear * i;
@@ -463,13 +461,9 @@ describe('Time scale tests', function() {
                });
 
                var xScale = chart.scales.xScale0;
+               var pixel = xScale.getPixelForValue('', 0, 0);
 
-               expect(xScale.getPixelForValue('', 0, 0)).toBeCloseToPixel(62);
-
-               expect(xScale.getValueForPixel(62)).toBeCloseToTime({
-                       value: moment(chart.data.labels[0]),
-                       unit: 'day',
-               });
+               expect(xScale.getValueForPixel(pixel).valueOf()).toEqual(moment(chart.data.labels[0]).valueOf());
        });
 
        it('does not create a negative width chart when hidden', function() {