]> git.ipfire.org Git - thirdparty/Chart.js.git/commitdiff
Flatten animation object and fix callbacks
authorSimon Brunel <simonbrunel@users.noreply.github.com>
Sat, 25 Feb 2017 10:51:47 +0000 (11:51 +0100)
committerEvert Timberg <evert.timberg+github@gmail.com>
Fri, 3 Mar 2017 11:49:47 +0000 (06:49 -0500)
Animation callbacks now receives `animationObject` directly with a reference on the associated chart (`animation.chart`), which deprecates `animation.animationObject` and `animation.chartInstance`. Also fix missing `onComplete` animation argument and make sure that an animation object is passed even when animations are disabled.

docs/01-Chart-Configuration.md
samples/animation/progress-bar.html
src/core/core.animation.js
src/core/core.controller.js
src/core/core.helpers.js
src/core/core.scale.js
test/global.deprecations.tests.js

index b01af7fb9c39b09dd8bacaf177eb7a78f76aed62..104c6953ad8f1854e8b4ddfbc449856685bef2da 100644 (file)
@@ -339,29 +339,18 @@ Name | Type | Default | Description
 --- |:---:| --- | ---
 duration | Number | 1000 | The number of milliseconds an animation takes.
 easing | String | "easeOutQuart" | Easing function to use. Available options are: `'linear'`, `'easeInQuad'`, `'easeOutQuad'`, `'easeInOutQuad'`, `'easeInCubic'`, `'easeOutCubic'`, `'easeInOutCubic'`, `'easeInQuart'`, `'easeOutQuart'`, `'easeInOutQuart'`, `'easeInQuint'`, `'easeOutQuint'`, `'easeInOutQuint'`, `'easeInSine'`, `'easeOutSine'`, `'easeInOutSine'`, `'easeInExpo'`, `'easeOutExpo'`, `'easeInOutExpo'`, `'easeInCirc'`, `'easeOutCirc'`, `'easeInOutCirc'`, `'easeInElastic'`, `'easeOutElastic'`, `'easeInOutElastic'`, `'easeInBack'`, `'easeOutBack'`, `'easeInOutBack'`, `'easeInBounce'`, `'easeOutBounce'`, `'easeInOutBounce'`. See [Robert Penner's easing equations](http://robertpenner.com/easing/).
-onProgress | Function | none | Callback called on each step of an animation. Passed a single argument, an object, containing the chart instance and an object with details of the animation.
-onComplete | Function | none | Callback called at the end of an animation. Passed the same arguments as `onProgress`
+onProgress | Function | none | Callback called on each step of an animation. Passed a single argument, a `Chart.Animation` instance, see below.
+onComplete | Function | none | Callback called at the end of an animation. Passed a single argument, a `Chart.Animation` instance, see below.
 
 #### Animation Callbacks
 
-The `onProgress` and `onComplete` callbacks are useful for synchronizing an external draw to the chart animation. The callback is passed an object that implements the following interface. An example usage of these callbacks can be found on [Github](https://github.com/chartjs/Chart.js/blob/master/samples/animation/progress-bar.html). This sample displays a progress bar showing how far along the animation is.
+The `onProgress` and `onComplete` callbacks are useful for synchronizing an external draw to the chart animation. The callback is passed a `Chart.Animation` instance:
 
 ```javascript
 {
     // Chart instance
     chart,
 
-    // Contains details of the on-going animation
-    animationObject,
-}
-```
-
-#### Animation Object
-
-The animation object passed to the callbacks is of type `Chart.Animation`. The object has the following parameters.
-
-```javascript
-{
     // Current Animation frame number
     currentStep: Number,
 
@@ -382,6 +371,8 @@ The animation object passed to the callbacks is of type `Chart.Animation`. The o
 }
 ```
 
+An example usage of these callbacks can be found on [Github](https://github.com/chartjs/Chart.js/blob/master/samples/animation/progress-bar.html): this sample displays a progress bar showing how far along the animation is.
+
 ### Element Configuration
 
 The global options for elements are defined in `Chart.defaults.global.elements`.
index b0c495a02db0818a0b994bfdc520b7e078937214..d460bc88e2a1c0b1c2b9f7e562d9a9a3f856d323 100644 (file)
                     borderColor: window.chartColors.red,
                     backgroundColor: window.chartColors.red,
                     data: [
-                        randomScalingFactor(), 
-                        randomScalingFactor(), 
-                        randomScalingFactor(), 
-                        randomScalingFactor(), 
-                        randomScalingFactor(), 
-                        randomScalingFactor(), 
+                        randomScalingFactor(),
+                        randomScalingFactor(),
+                        randomScalingFactor(),
+                        randomScalingFactor(),
+                        randomScalingFactor(),
+                        randomScalingFactor(),
                         randomScalingFactor()
                     ]
                 }, {
                     borderColor: window.chartColors.blue,
                     backgroundColor: window.chartColors.blue,
                     data: [
-                        randomScalingFactor(), 
-                        randomScalingFactor(), 
-                        randomScalingFactor(), 
-                        randomScalingFactor(), 
-                        randomScalingFactor(), 
-                        randomScalingFactor(), 
+                        randomScalingFactor(),
+                        randomScalingFactor(),
+                        randomScalingFactor(),
+                        randomScalingFactor(),
+                        randomScalingFactor(),
+                        randomScalingFactor(),
                         randomScalingFactor()
                     ]
                 }]
@@ -65,7 +65,7 @@
                 animation: {
                     duration: 2000,
                     onProgress: function(animation) {
-                        progress.value = animation.animationObject.currentStep / animation.animationObject.numSteps;
+                        progress.value = animation.currentStep / animation.numSteps;
                     },
                     onComplete: function(animation) {
                         window.setTimeout(function() {
index c0f4a35f70719096dcc19907f4d1e7523f501a47..ad450df8e2a19d157f7a0fbfcb9df8473946c0ee 100644 (file)
@@ -13,13 +13,14 @@ module.exports = function(Chart) {
        };
 
        Chart.Animation = Chart.Element.extend({
-               currentStep: null, // the current animation step
+               chart: null, // the animation associated chart instance
+               currentStep: 0, // the current animation step
                numSteps: 60, // default number of steps
                easing: '', // the easing to use for this animation
                render: null, // render function used by the animation service
 
                onAnimationProgress: null, // user specified callback to fire on each step of the animation
-               onAnimationComplete: null // user specified callback to fire when the animation finishes
+               onAnimationComplete: null, // user specified callback to fire when the animation finishes
        });
 
        Chart.animationService = {
@@ -29,42 +30,39 @@ module.exports = function(Chart) {
                request: null,
 
                /**
-                * @function Chart.animationService.addAnimation
-                * @param chart {ChartController} the chart to animate
-                * @param animationObject {IAnimation} the animation that we will animate
-                * @param duration {Number} length of animation in ms
-                * @param lazy {Boolean} if true, the chart is not marked as animating to enable more responsive interactions
+                * @param {Chart} chart - The chart to animate.
+                * @param {Chart.Animation} animation - The animation that we will animate.
+                * @param {Number} duration - The animation duration in ms.
+                * @param {Boolean} lazy - if true, the chart is not marked as animating to enable more responsive interactions
                 */
-               addAnimation: function(chart, animationObject, duration, lazy) {
-                       var me = this;
+               addAnimation: function(chart, animation, duration, lazy) {
+                       var animations = this.animations;
+                       var i, ilen;
+
+                       animation.chart = chart;
 
                        if (!lazy) {
                                chart.animating = true;
                        }
 
-                       for (var index = 0; index < me.animations.length; ++index) {
-                               if (me.animations[index].chart === chart) {
-                                       // replacing an in progress animation
-                                       me.animations[index].animationObject = animationObject;
+                       for (i=0, ilen=animations.length; i < ilen; ++i) {
+                               if (animations[i].chart === chart) {
+                                       animations[i] = animation;
                                        return;
                                }
                        }
 
-                       me.animations.push({
-                               chart: chart,
-                               chartInstance: chart,               // deprecated, backward compatibility
-                               animationObject: animationObject
-                       });
+                       animations.push(animation);
 
                        // If there are no animations queued, manually kickstart a digest, for lack of a better word
-                       if (me.animations.length === 1) {
-                               me.requestAnimationFrame();
+                       if (animations.length === 1) {
+                               this.requestAnimationFrame();
                        }
                },
-               // Cancel the animation for a given chart instance
+
                cancelAnimation: function(chart) {
-                       var index = helpers.findIndex(this.animations, function(animationWrapper) {
-                               return animationWrapper.chart === chart;
+                       var index = helpers.findIndex(this.animations, function(animation) {
+                               return animation.chart === chart;
                        });
 
                        if (index !== -1) {
@@ -72,6 +70,7 @@ module.exports = function(Chart) {
                                chart.animating = false;
                        }
                },
+
                requestAnimationFrame: function() {
                        var me = this;
                        if (me.request === null) {
@@ -84,9 +83,12 @@ module.exports = function(Chart) {
                                });
                        }
                },
+
+               /**
+                * @private
+                */
                startDigest: function() {
                        var me = this;
-
                        var startTime = Date.now();
                        var framesToDrop = 0;
 
@@ -95,46 +97,72 @@ module.exports = function(Chart) {
                                me.dropFrames = me.dropFrames % 1;
                        }
 
-                       var i = 0;
-                       while (i < me.animations.length) {
-                               if (me.animations[i].animationObject.currentStep === null) {
-                                       me.animations[i].animationObject.currentStep = 0;
-                               }
+                       me.advance(1 + framesToDrop);
 
-                               me.animations[i].animationObject.currentStep += 1 + framesToDrop;
+                       var endTime = Date.now();
 
-                               if (me.animations[i].animationObject.currentStep > me.animations[i].animationObject.numSteps) {
-                                       me.animations[i].animationObject.currentStep = me.animations[i].animationObject.numSteps;
-                               }
+                       me.dropFrames += (endTime - startTime) / me.frameDuration;
 
-                               me.animations[i].animationObject.render(me.animations[i].chart, me.animations[i].animationObject);
-                               if (me.animations[i].animationObject.onAnimationProgress && me.animations[i].animationObject.onAnimationProgress.call) {
-                                       me.animations[i].animationObject.onAnimationProgress.call(me.animations[i].chart, me.animations[i]);
-                               }
+                       // Do we have more stuff to animate?
+                       if (me.animations.length > 0) {
+                               me.requestAnimationFrame();
+                       }
+               },
 
-                               if (me.animations[i].animationObject.currentStep === me.animations[i].animationObject.numSteps) {
-                                       if (me.animations[i].animationObject.onAnimationComplete && me.animations[i].animationObject.onAnimationComplete.call) {
-                                               me.animations[i].animationObject.onAnimationComplete.call(me.animations[i].chart, me.animations[i]);
-                                       }
+               /**
+                * @private
+                */
+               advance: function(count) {
+                       var animations = this.animations;
+                       var animation, chart;
+                       var i = 0;
+
+                       while (i < animations.length) {
+                               animation = animations[i];
+                               chart = animation.chart;
+
+                               animation.currentStep = (animation.currentStep || 0) + count;
+                               animation.currentStep = Math.min(animation.currentStep, animation.numSteps);
 
-                                       // executed the last frame. Remove the animation.
-                                       me.animations[i].chart.animating = false;
+                               helpers.callback(animation.render, [chart, animation], chart);
+                               helpers.callback(animation.onAnimationProgress, [animation], chart);
 
-                                       me.animations.splice(i, 1);
+                               if (animation.currentStep >= animation.numSteps) {
+                                       helpers.callback(animation.onAnimationComplete, [animation], chart);
+                                       chart.animating = false;
+                                       animations.splice(i, 1);
                                } else {
                                        ++i;
                                }
                        }
+               }
+       };
 
-                       var endTime = Date.now();
-                       var dropFrames = (endTime - startTime) / me.frameDuration;
-
-                       me.dropFrames += dropFrames;
+       /**
+        * Provided for backward compatibility, use Chart.Animation instead
+        * @prop Chart.Animation#animationObject
+        * @deprecated since version 2.6.0
+        * @todo remove at version 3
+        */
+       Object.defineProperty(Chart.Animation.prototype, 'animationObject', {
+               get: function() {
+                       return this;
+               }
+       });
 
-                       // Do we have more stuff to animate?
-                       if (me.animations.length > 0) {
-                               me.requestAnimationFrame();
-                       }
+       /**
+        * Provided for backward compatibility, use Chart.Animation#chart instead
+        * @prop Chart.Animation#chartInstance
+        * @deprecated since version 2.6.0
+        * @todo remove at version 3
+        */
+       Object.defineProperty(Chart.Animation.prototype, 'chartInstance', {
+               get: function() {
+                       return this.chart;
+               },
+               set: function(value) {
+                       this.chart = value;
                }
-       };
+       });
+
 };
index ddd02e7afdefd8f50e629df45e53dae359bb2b4f..1090aa9b80d71ad4f127663c0bb0bdaa1b230f7a 100644 (file)
@@ -445,36 +445,34 @@ module.exports = function(Chart) {
                        }
 
                        var animationOptions = me.options.animation;
-                       var onComplete = function() {
+                       var onComplete = function(animation) {
                                plugins.notify(me, 'afterRender');
-                               var callback = animationOptions && animationOptions.onComplete;
-                               if (callback && callback.call) {
-                                       callback.call(me);
-                               }
+                               helpers.callback(animationOptions && animationOptions.onComplete, [animation], me);
                        };
 
                        if (animationOptions && ((typeof duration !== 'undefined' && duration !== 0) || (typeof duration === 'undefined' && animationOptions.duration !== 0))) {
-                               var animation = new Chart.Animation();
-                               animation.numSteps = (duration || animationOptions.duration) / 16.66; // 60 fps
-                               animation.easing = animationOptions.easing;
+                               var animation = new Chart.Animation({
+                                       numSteps: (duration || animationOptions.duration) / 16.66, // 60 fps
+                                       easing: animationOptions.easing,
 
-                               // render function
-                               animation.render = function(chart, animationObject) {
-                                       var easingFunction = helpers.easingEffects[animationObject.easing];
-                                       var stepDecimal = animationObject.currentStep / animationObject.numSteps;
-                                       var easeDecimal = easingFunction(stepDecimal);
+                                       render: function(chart, animationObject) {
+                                               var easingFunction = helpers.easingEffects[animationObject.easing];
+                                               var currentStep = animationObject.currentStep;
+                                               var stepDecimal = currentStep / animationObject.numSteps;
 
-                                       chart.draw(easeDecimal, stepDecimal, animationObject.currentStep);
-                               };
+                                               chart.draw(easingFunction(stepDecimal), stepDecimal, currentStep);
+                                       },
 
-                               // user events
-                               animation.onAnimationProgress = animationOptions.onProgress;
-                               animation.onAnimationComplete = onComplete;
+                                       onAnimationProgress: animationOptions.onProgress,
+                                       onAnimationComplete: onComplete
+                               });
 
                                Chart.animationService.addAnimation(me, animation, duration, lazy);
                        } else {
                                me.draw();
-                               onComplete();
+
+                               // See https://github.com/chartjs/Chart.js/issues/3781
+                               onComplete(new Chart.Animation({numSteps: 0, chart: me}));
                        }
 
                        return me;
index c32e9a76c1c4778483cb65491314683b26a7a65e..2d96ac14b47a8e1c692d0c256f8fb991ee248d30 100644 (file)
@@ -957,9 +957,9 @@ module.exports = function(Chart) {
 
                return true;
        };
-       helpers.callCallback = function(fn, args, _tArg) {
+       helpers.callback = function(fn, args, thisArg) {
                if (fn && typeof fn.call === 'function') {
-                       fn.apply(_tArg, args);
+                       fn.apply(thisArg, args);
                }
        };
        helpers.getHoverColor = function(colorValue) {
@@ -968,4 +968,12 @@ module.exports = function(Chart) {
                        colorValue :
                        helpers.color(colorValue).saturate(0.5).darken(0.1).rgbString();
        };
+
+       /**
+        * Provided for backward compatibility, use Chart.helpers#callback instead.
+        * @function Chart.helpers#callCallback
+        * @deprecated since version 2.6.0
+        * @todo remove at version 3
+        */
+       helpers.callCallback = helpers.callback;
 };
index 4584f0427116f4eec5d8fecaa0a1a8593becb0a9..de453e64a33eceec525932e6d95ee40e85d48cda 100644 (file)
@@ -93,7 +93,7 @@ module.exports = function(Chart) {
                // Any function can be extended by the scale type
 
                beforeUpdate: function() {
-                       helpers.callCallback(this.options.beforeUpdate, [this]);
+                       helpers.callback(this.options.beforeUpdate, [this]);
                },
                update: function(maxWidth, maxHeight, margins) {
                        var me = this;
@@ -146,13 +146,13 @@ module.exports = function(Chart) {
 
                },
                afterUpdate: function() {
-                       helpers.callCallback(this.options.afterUpdate, [this]);
+                       helpers.callback(this.options.afterUpdate, [this]);
                },
 
                //
 
                beforeSetDimensions: function() {
-                       helpers.callCallback(this.options.beforeSetDimensions, [this]);
+                       helpers.callback(this.options.beforeSetDimensions, [this]);
                },
                setDimensions: function() {
                        var me = this;
@@ -177,29 +177,29 @@ module.exports = function(Chart) {
                        me.paddingBottom = 0;
                },
                afterSetDimensions: function() {
-                       helpers.callCallback(this.options.afterSetDimensions, [this]);
+                       helpers.callback(this.options.afterSetDimensions, [this]);
                },
 
                // Data limits
                beforeDataLimits: function() {
-                       helpers.callCallback(this.options.beforeDataLimits, [this]);
+                       helpers.callback(this.options.beforeDataLimits, [this]);
                },
                determineDataLimits: helpers.noop,
                afterDataLimits: function() {
-                       helpers.callCallback(this.options.afterDataLimits, [this]);
+                       helpers.callback(this.options.afterDataLimits, [this]);
                },
 
                //
                beforeBuildTicks: function() {
-                       helpers.callCallback(this.options.beforeBuildTicks, [this]);
+                       helpers.callback(this.options.beforeBuildTicks, [this]);
                },
                buildTicks: helpers.noop,
                afterBuildTicks: function() {
-                       helpers.callCallback(this.options.afterBuildTicks, [this]);
+                       helpers.callback(this.options.afterBuildTicks, [this]);
                },
 
                beforeTickToLabelConversion: function() {
-                       helpers.callCallback(this.options.beforeTickToLabelConversion, [this]);
+                       helpers.callback(this.options.beforeTickToLabelConversion, [this]);
                },
                convertTicksToLabels: function() {
                        var me = this;
@@ -208,13 +208,13 @@ module.exports = function(Chart) {
                        me.ticks = me.ticks.map(tickOpts.userCallback || tickOpts.callback);
                },
                afterTickToLabelConversion: function() {
-                       helpers.callCallback(this.options.afterTickToLabelConversion, [this]);
+                       helpers.callback(this.options.afterTickToLabelConversion, [this]);
                },
 
                //
 
                beforeCalculateTickRotation: function() {
-                       helpers.callCallback(this.options.beforeCalculateTickRotation, [this]);
+                       helpers.callback(this.options.beforeCalculateTickRotation, [this]);
                },
                calculateTickRotation: function() {
                        var me = this;
@@ -257,13 +257,13 @@ module.exports = function(Chart) {
                        me.labelRotation = labelRotation;
                },
                afterCalculateTickRotation: function() {
-                       helpers.callCallback(this.options.afterCalculateTickRotation, [this]);
+                       helpers.callback(this.options.afterCalculateTickRotation, [this]);
                },
 
                //
 
                beforeFit: function() {
-                       helpers.callCallback(this.options.beforeFit, [this]);
+                       helpers.callback(this.options.beforeFit, [this]);
                },
                fit: function() {
                        var me = this;
@@ -381,7 +381,7 @@ module.exports = function(Chart) {
                },
 
                afterFit: function() {
-                       helpers.callCallback(this.options.afterFit, [this]);
+                       helpers.callback(this.options.afterFit, [this]);
                },
 
                // Shared Methods
index a6b4f70f55cad2a338faf40a2956345534761f49..63f383ed6cda904c0aef4e1bcd48f137fcda83b7 100644 (file)
@@ -39,6 +39,53 @@ describe('Deprecations', function() {
                                expect(proxy.width).toBe(140);
                        });
                });
+
+               describe('Chart.Animation.animationObject', function() {
+                       it('should be defined and an alias of Chart.Animation', function(done) {
+                               var animation = null;
+
+                               acquireChart({
+                                       options: {
+                                               animation: {
+                                                       duration: 50,
+                                                       onComplete: function(arg) {
+                                                               animation = arg;
+                                                       }
+                                               }
+                                       }
+                               });
+
+                               setTimeout(function() {
+                                       expect(animation).not.toBeNull();
+                                       expect(animation.animationObject).toBeDefined();
+                                       expect(animation.animationObject).toBe(animation);
+                                       done();
+                               }, 200);
+                       });
+               });
+
+               describe('Chart.Animation.chartInstance', function() {
+                       it('should be defined and an alias of Chart.Animation.chart', function(done) {
+                               var animation = null;
+                               var chart = acquireChart({
+                                       options: {
+                                               animation: {
+                                                       duration: 50,
+                                                       onComplete: function(arg) {
+                                                               animation = arg;
+                                                       }
+                                               }
+                                       }
+                               });
+
+                               setTimeout(function() {
+                                       expect(animation).not.toBeNull();
+                                       expect(animation.chartInstance).toBeDefined();
+                                       expect(animation.chartInstance).toBe(chart);
+                                       done();
+                               }, 200);
+                       });
+               });
        });
 
        describe('Version 2.5.0', function() {