Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 5 additions & 14 deletions docs/01-Chart-Configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -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,

Expand All @@ -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`.
Expand Down
26 changes: 13 additions & 13 deletions samples/animation/progress-bar.html
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,12 @@
borderColor: window.chartColors.red,
backgroundColor: window.chartColors.red,
data: [
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor()
]
}, {
Expand All @@ -47,12 +47,12 @@
borderColor: window.chartColors.blue,
backgroundColor: window.chartColors.blue,
data: [
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor()
]
}]
Expand All @@ -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() {
Expand Down
134 changes: 81 additions & 53 deletions src/core/core.animation.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand All @@ -29,49 +30,47 @@ 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) {
this.animations.splice(index, 1);
chart.animating = false;
}
},

requestAnimationFrame: function() {
var me = this;
if (me.request === null) {
Expand All @@ -84,9 +83,12 @@ module.exports = function(Chart) {
});
}
},

/**
* @private
*/
startDigest: function() {
var me = this;

var startTime = Date.now();
var framesToDrop = 0;

Expand All @@ -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;
}
};
});

};
36 changes: 17 additions & 19 deletions src/core/core.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
12 changes: 10 additions & 2 deletions src/core/core.helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -955,9 +955,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) {
Expand All @@ -966,4 +966,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;
};
Loading