From 0b8fd966fff8a4b5ef7d7bd1c2f1ecefa02149b5 Mon Sep 17 00:00:00 2001 From: Evert Timberg Date: Sat, 11 Apr 2015 19:02:50 -0400 Subject: [PATCH 01/15] Chart animations will now use the animation service provided in Chart.animationService. --- src/Chart.Core.js | 39 +++++++++++++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/src/Chart.Core.js b/src/Chart.Core.js index 296844ea2..2b8aa78f8 100755 --- a/src/Chart.Core.js +++ b/src/Chart.Core.js @@ -932,15 +932,27 @@ if (reflow){ this.reflow(); } + if (this.options.animation && !reflow){ - helpers.animationLoop( - this.draw, - this.options.animationSteps, - this.options.animationEasing, - this.options.onAnimationProgress, - this.options.onAnimationComplete, - this - ); + var animation = new Chart.Animation(); + animation.numSteps = this.options.animationSteps; + animation.minSteps = 10; // TODO: add an option for this + animation.easing = this.options.animationEasing; + + // render function + animation.render = function(chartInstance, animationObject) { + var easingFunction = helpers.easingEffects[animationObject.easing]; + var stepDecimal = animationObject.currentStep / animationObject.numSteps; + var easeDecimal = easingFunction(stepDecimal); + + chartInstance.draw(chartInstance, easeDecimal, stepDecimal, currentStep); + }; + + // user events + animation.onAnimationProgress = this.options.onAnimationProgress; + animation.onAnimationComplete = this.options.onAnimationComplete; + + Chart.animationService.addAnimation(this, animation); } else{ this.draw(); @@ -1347,6 +1359,17 @@ } }); + Chart.Animation = Chart.Element.extend({ + currentStep: null, // the current animation step + numSteps: 60, // default number of steps + minSteps: 10, // TODO: create an option for this + 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 + }); + Chart.Tooltip = Chart.Element.extend({ draw : function(){ From d3579334b297fc477e594724bde5e45022d4c3da Mon Sep 17 00:00:00 2001 From: Evert Timberg Date: Sat, 11 Apr 2015 19:17:30 -0400 Subject: [PATCH 02/15] Add an option to control the minimum number of animation steps. --- src/Chart.Core.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Chart.Core.js b/src/Chart.Core.js index 2b8aa78f8..6288f9699 100755 --- a/src/Chart.Core.js +++ b/src/Chart.Core.js @@ -60,6 +60,9 @@ // Number - Number of animation steps animationSteps: 60, + + // Number - Minimum number of animation steps to complete regardless of frame rate + minAnimationSteps: 10, // String - Animation easing effect animationEasing: "easeOutQuart", @@ -936,7 +939,7 @@ if (this.options.animation && !reflow){ var animation = new Chart.Animation(); animation.numSteps = this.options.animationSteps; - animation.minSteps = 10; // TODO: add an option for this + animation.minSteps = this.options.minAnimationSteps; animation.easing = this.options.animationEasing; // render function @@ -1362,7 +1365,7 @@ Chart.Animation = Chart.Element.extend({ currentStep: null, // the current animation step numSteps: 60, // default number of steps - minSteps: 10, // TODO: create an option for this + minSteps: 10, // default minimum number of steps. easing: "", // the easing to use for this animation render: null, // render function used by the animation service From eee5af592438fa9557ae515529ae23b45cce4712 Mon Sep 17 00:00:00 2001 From: Tanner Linsley Date: Sat, 11 Apr 2015 17:19:29 -0600 Subject: [PATCH 03/15] Chart.AnimationService --- Chart.js | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/Chart.js b/Chart.js index c264262ba..f9b30f0a7 100644 --- a/Chart.js +++ b/Chart.js @@ -1984,6 +1984,58 @@ } }); + Chart.animationService = { + animations: [], + addAnimation: function(chart, animationObject) { + for (var index = 0; index < this.animations.length; ++ index){ + if (this.animations[index].chart === chart){ + // replacing an in progress animation + this.animations[index].lastTimeRun = null; + this.animations[index].animationObject = animationObject; + return; + } + } + + this.animations.push({ + chart: chart, + animationObject: animationObject, + lastTimeRun: null + }); + + // If there are no animations queued, manually kickstart a digest, for lack of a better word + if(!this.animations.length){ + helpers.requestAnimFrame(this.startDigest); + } + }, + startDigest: function() { + + for (var i = 0; i < this.animations.length; i++) { + + var currentAnimation = this.animations[i]; + + if (currentAnimation.animationObject.currentStep === null){ + currentAnimation.animationObject.currentStep = 0; + } else { + currentAnimation.animationObject.currentStep++; + } + + currentAnimation.animationObject.render(currentAnimation.animationObject); + + if (currentAnimation.animationObject.currentStep == currentAnimation.animationObject.numSteps){ + // executed the last frame. Remove the animation. + this.animations.splice(i, 1); + // Keep the index in place to offset the splice + i--; + } + } + + // Do we have more stuff to animate? + if (this.animations.length > 0){ + requestAnimationFrame(this.startDigest); + } + } + }; + // Attach global event to resize each chart instance when the browser resizes helpers.addEvent(window, "resize", (function(){ // Basic debounce of resize function so it doesn't hurt performance when resizing browser. From 7a6f2b4f06dfa8214cda4b00a2df7aad43e6c52f Mon Sep 17 00:00:00 2001 From: Tanner Linsley Date: Sat, 11 Apr 2015 17:30:06 -0600 Subject: [PATCH 04/15] Src changes... derrrr --- src/Chart.Core.js | 52 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/src/Chart.Core.js b/src/Chart.Core.js index 6288f9699..36f58723c 100755 --- a/src/Chart.Core.js +++ b/src/Chart.Core.js @@ -2074,6 +2074,58 @@ } }); + Chart.animationService = { + animations: [], + addAnimation: function(chart, animationObject) { + for (var index = 0; index < this.animations.length; ++ index){ + if (this.animations[index].chart === chart){ + // replacing an in progress animation + this.animations[index].lastTimeRun = null; + this.animations[index].animationObject = animationObject; + return; + } + } + + this.animations.push({ + chart: chart, + animationObject: animationObject, + lastTimeRun: null + }); + + // If there are no animations queued, manually kickstart a digest, for lack of a better word + if(!this.animations.length){ + helpers.requestAnimFrame(this.startDigest); + } + }, + startDigest: function() { + + for (var i = 0; i < this.animations.length; i++) { + + var currentAnimation = this.animations[i]; + + if (currentAnimation.animationObject.currentStep === null){ + currentAnimation.animationObject.currentStep = 0; + } else { + currentAnimation.animationObject.currentStep++; + } + + currentAnimation.animationObject.render(currentAnimation.animationObject); + + if (currentAnimation.animationObject.currentStep == currentAnimation.animationObject.numSteps){ + // executed the last frame. Remove the animation. + this.animations.splice(i, 1); + // Keep the index in place to offset the splice + i--; + } + } + + // Do we have more stuff to animate? + if (this.animations.length > 0){ + requestAnimationFrame(this.startDigest); + } + } + }; + // Attach global event to resize each chart instance when the browser resizes helpers.addEvent(window, "resize", (function(){ // Basic debounce of resize function so it doesn't hurt performance when resizing browser. From 3e59438646382d66b2b1313731f237f43c4cc9dc Mon Sep 17 00:00:00 2001 From: Evert Timberg Date: Sat, 11 Apr 2015 19:33:20 -0400 Subject: [PATCH 05/15] Integrate animation service with chart --- src/Chart.Core.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Chart.Core.js b/src/Chart.Core.js index 36f58723c..2a7908c6b 100755 --- a/src/Chart.Core.js +++ b/src/Chart.Core.js @@ -2076,9 +2076,9 @@ Chart.animationService = { animations: [], - addAnimation: function(chart, animationObject) { + addAnimation: function(chartInstance, animationObject) { for (var index = 0; index < this.animations.length; ++ index){ - if (this.animations[index].chart === chart){ + if (this.animations[index].chartInstance === chartInstance){ // replacing an in progress animation this.animations[index].lastTimeRun = null; this.animations[index].animationObject = animationObject; @@ -2087,7 +2087,7 @@ } this.animations.push({ - chart: chart, + chartInstance: chartInstance, animationObject: animationObject, lastTimeRun: null }); @@ -2109,7 +2109,7 @@ currentAnimation.animationObject.currentStep++; } - currentAnimation.animationObject.render(currentAnimation.animationObject); + currentAnimation.animationObject.render(currentAnimation.chartInstance, currentAnimation.animationObject); if (currentAnimation.animationObject.currentStep == currentAnimation.animationObject.numSteps){ // executed the last frame. Remove the animation. From 9a13dd78fc6a37e5239fabd428973bdfd5a8f8a0 Mon Sep 17 00:00:00 2001 From: Evert Timberg Date: Sat, 11 Apr 2015 19:42:54 -0400 Subject: [PATCH 06/15] Fix a typo --- src/Chart.Core.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Chart.Core.js b/src/Chart.Core.js index 2a7908c6b..85100573b 100755 --- a/src/Chart.Core.js +++ b/src/Chart.Core.js @@ -2093,7 +2093,7 @@ }); // If there are no animations queued, manually kickstart a digest, for lack of a better word - if(!this.animations.length){ + if (this.animations.length) { helpers.requestAnimFrame(this.startDigest); } }, From 98253fea5d0e2ecc1dcdad6321e5bf3137fd7ae2 Mon Sep 17 00:00:00 2001 From: Evert Timberg Date: Sat, 11 Apr 2015 20:08:17 -0400 Subject: [PATCH 07/15] Fix some bugs in the animation stuff --- src/Chart.Core.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Chart.Core.js b/src/Chart.Core.js index 85100573b..7e48ead00 100755 --- a/src/Chart.Core.js +++ b/src/Chart.Core.js @@ -948,7 +948,7 @@ var stepDecimal = animationObject.currentStep / animationObject.numSteps; var easeDecimal = easingFunction(stepDecimal); - chartInstance.draw(chartInstance, easeDecimal, stepDecimal, currentStep); + chartInstance.draw(easeDecimal, stepDecimal, animationObject.currentStep); }; // user events @@ -2094,9 +2094,13 @@ // If there are no animations queued, manually kickstart a digest, for lack of a better word if (this.animations.length) { - helpers.requestAnimFrame(this.startDigest); + helpers.requestAnimFrame.call(window, this.digestWrapper); } }, + // calls startDigest with the proper context + digestWrapper: function() { + Chart.animationService.startDigest.call(Chart.animationService); + }, startDigest: function() { for (var i = 0; i < this.animations.length; i++) { @@ -2121,7 +2125,7 @@ // Do we have more stuff to animate? if (this.animations.length > 0){ - requestAnimationFrame(this.startDigest); + helpers.requestAnimFrame.call(window, this.digestWrapper); } } }; From 26eca2370609204bf58284e382123a21973f52ee Mon Sep 17 00:00:00 2001 From: Evert Timberg Date: Sat, 11 Apr 2015 20:17:46 -0400 Subject: [PATCH 08/15] Implement cancel animation frame in the animation service --- src/Chart.Core.js | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/Chart.Core.js b/src/Chart.Core.js index 7e48ead00..565ceeeb1 100755 --- a/src/Chart.Core.js +++ b/src/Chart.Core.js @@ -911,7 +911,7 @@ }, stop : function(){ // Stops any current animation loop occuring - cancelAnimFrame(this.animationFrame); + Chart.animationService.cancelAnimation(this); return this; }, resize : function(callback){ @@ -2097,6 +2097,17 @@ helpers.requestAnimFrame.call(window, this.digestWrapper); } }, + // Cancel the animation for a given chart instance + cancelAnimation: function(chartInstance) { + var index = helpers.findNextWhere(this.animations, function(animationWrapper) { + return animationWrapper.chartInstance === chartInstance; + }); + + if (index != -1) + { + this.animations.splice(index, 1); + } + }, // calls startDigest with the proper context digestWrapper: function() { Chart.animationService.startDigest.call(Chart.animationService); From e0c208be581f5ef25653092dec951d0c07fc1a0a Mon Sep 17 00:00:00 2001 From: Tanner Linsley Date: Sat, 11 Apr 2015 18:34:06 -0600 Subject: [PATCH 09/15] Frame Dropper --- src/Chart.Core.js | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/Chart.Core.js b/src/Chart.Core.js index 36f58723c..6635e1bd8 100755 --- a/src/Chart.Core.js +++ b/src/Chart.Core.js @@ -2075,7 +2075,9 @@ }); Chart.animationService = { + frameDuration: 17, animations: [], + dropFrames: 0, addAnimation: function(chart, animationObject) { for (var index = 0; index < this.animations.length; ++ index){ if (this.animations[index].chart === chart){ @@ -2099,6 +2101,14 @@ }, startDigest: function() { + var startTime = Date.now(); + var framesToDrop = 0; + + if(this.dropFrames > 1){ + framesToDrop = Math.floor(this.dropFrames); + this.dropFrames -= framesToDrop; + } + for (var i = 0; i < this.animations.length; i++) { var currentAnimation = this.animations[i]; @@ -2106,7 +2116,7 @@ if (currentAnimation.animationObject.currentStep === null){ currentAnimation.animationObject.currentStep = 0; } else { - currentAnimation.animationObject.currentStep++; + currentAnimation.animationObject.currentStep += 1 + framesToDrop; } currentAnimation.animationObject.render(currentAnimation.animationObject); @@ -2119,6 +2129,14 @@ } } + var endTime = Date.now(); + var delay = endTime - startTime - this.frameDuration; + var frameDelay = delay / this.frameDuration; + + if(frameDelay > 1){ + this.dropFrames += frameDelay; + } + // Do we have more stuff to animate? if (this.animations.length > 0){ requestAnimationFrame(this.startDigest); From 70b8bf106c873a7ecb84c1773a141cb5f1a75703 Mon Sep 17 00:00:00 2001 From: Tanner Linsley Date: Sat, 11 Apr 2015 18:42:11 -0600 Subject: [PATCH 10/15] Always execute last frame --- src/Chart.Core.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Chart.Core.js b/src/Chart.Core.js index 2c1229d15..e3a83ecab 100755 --- a/src/Chart.Core.js +++ b/src/Chart.Core.js @@ -2132,6 +2132,9 @@ currentAnimation.animationObject.currentStep = 0; } else { currentAnimation.animationObject.currentStep += 1 + framesToDrop; + if(currentAnimation.animationObject.currentStep > currentAnimation.animationObject.numSteps){ + currentAnimation.animationObject.currentStep = currentAnimation.animationObject.numSteps; + } } currentAnimation.animationObject.render(currentAnimation.chartInstance, currentAnimation.animationObject); From b1cbf04f25bbdccaf2679198aa567b11116b84cf Mon Sep 17 00:00:00 2001 From: Evert Timberg Date: Sat, 11 Apr 2015 20:47:35 -0400 Subject: [PATCH 11/15] Remove the unused lastTimeRun from the animation service --- src/Chart.Core.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Chart.Core.js b/src/Chart.Core.js index e3a83ecab..058236e5b 100755 --- a/src/Chart.Core.js +++ b/src/Chart.Core.js @@ -2082,7 +2082,6 @@ for (var index = 0; index < this.animations.length; ++ index){ if (this.animations[index].chartInstance === chartInstance){ // replacing an in progress animation - this.animations[index].lastTimeRun = null; this.animations[index].animationObject = animationObject; return; } @@ -2090,8 +2089,7 @@ this.animations.push({ chartInstance: chartInstance, - animationObject: animationObject, - lastTimeRun: null + animationObject: animationObject }); // If there are no animations queued, manually kickstart a digest, for lack of a better word From 0a5f92a85435d55ce4dd0babcc9d5ded45e2285e Mon Sep 17 00:00:00 2001 From: Evert Timberg Date: Sat, 11 Apr 2015 21:11:49 -0400 Subject: [PATCH 12/15] Remove the minAnimationSteps option since we did not end up needing it. --- src/Chart.Core.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/Chart.Core.js b/src/Chart.Core.js index 058236e5b..6ee080607 100755 --- a/src/Chart.Core.js +++ b/src/Chart.Core.js @@ -60,9 +60,6 @@ // Number - Number of animation steps animationSteps: 60, - - // Number - Minimum number of animation steps to complete regardless of frame rate - minAnimationSteps: 10, // String - Animation easing effect animationEasing: "easeOutQuart", @@ -939,7 +936,6 @@ if (this.options.animation && !reflow){ var animation = new Chart.Animation(); animation.numSteps = this.options.animationSteps; - animation.minSteps = this.options.minAnimationSteps; animation.easing = this.options.animationEasing; // render function @@ -1365,7 +1361,6 @@ Chart.Animation = Chart.Element.extend({ currentStep: null, // the current animation step numSteps: 60, // default number of steps - minSteps: 10, // default minimum number of steps. easing: "", // the easing to use for this animation render: null, // render function used by the animation service From c476db0dd7269ef47e36162b3f9ab2323f409d2e Mon Sep 17 00:00:00 2001 From: Evert Timberg Date: Sat, 11 Apr 2015 23:05:05 -0400 Subject: [PATCH 13/15] Use findNextWhere correctly. --- src/Chart.Core.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Chart.Core.js b/src/Chart.Core.js index 6ee080607..fcdc77636 100755 --- a/src/Chart.Core.js +++ b/src/Chart.Core.js @@ -2098,7 +2098,7 @@ return animationWrapper.chartInstance === chartInstance; }); - if (index != -1) + if (index) { this.animations.splice(index, 1); } From 14f4fae786857c10f2a85ab00be6b78868390fff Mon Sep 17 00:00:00 2001 From: Tanner Linsley Date: Sun, 12 Apr 2015 00:05:31 -0600 Subject: [PATCH 14/15] Only start digest on first request. Cleanup --- src/Chart.Core.js | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/Chart.Core.js b/src/Chart.Core.js index fcdc77636..22a338d42 100755 --- a/src/Chart.Core.js +++ b/src/Chart.Core.js @@ -2088,7 +2088,7 @@ }); // If there are no animations queued, manually kickstart a digest, for lack of a better word - if (this.animations.length) { + if (this.animations.length == 1) { helpers.requestAnimFrame.call(window, this.digestWrapper); } }, @@ -2119,20 +2119,18 @@ for (var i = 0; i < this.animations.length; i++) { - var currentAnimation = this.animations[i]; + if (this.animations[i].animationObject.currentStep === null){ + this.animations[i].animationObject.currentStep = 0; + } - if (currentAnimation.animationObject.currentStep === null){ - currentAnimation.animationObject.currentStep = 0; - } else { - currentAnimation.animationObject.currentStep += 1 + framesToDrop; - if(currentAnimation.animationObject.currentStep > currentAnimation.animationObject.numSteps){ - currentAnimation.animationObject.currentStep = currentAnimation.animationObject.numSteps; - } + this.animations[i].animationObject.currentStep += 1 + framesToDrop; + if(this.animations[i].animationObject.currentStep > this.animations[i].animationObject.numSteps){ + this.animations[i].animationObject.currentStep = this.animations[i].animationObject.numSteps; } - currentAnimation.animationObject.render(currentAnimation.chartInstance, currentAnimation.animationObject); + this.animations[i].animationObject.render(this.animations[i].chartInstance, this.animations[i].animationObject); - if (currentAnimation.animationObject.currentStep == currentAnimation.animationObject.numSteps){ + if (this.animations[i].animationObject.currentStep == this.animations[i].animationObject.numSteps){ // executed the last frame. Remove the animation. this.animations.splice(i, 1); // Keep the index in place to offset the splice From 8cc5be1212483e3e92543c9fcbfe34abc95eb08d Mon Sep 17 00:00:00 2001 From: Tanner Linsley Date: Thu, 16 Apr 2015 13:57:27 -0600 Subject: [PATCH 15/15] Revert changes to build --- Chart.js | 52 ---------------------------------------------------- 1 file changed, 52 deletions(-) diff --git a/Chart.js b/Chart.js index f9b30f0a7..c264262ba 100644 --- a/Chart.js +++ b/Chart.js @@ -1984,58 +1984,6 @@ } }); - Chart.animationService = { - animations: [], - addAnimation: function(chart, animationObject) { - for (var index = 0; index < this.animations.length; ++ index){ - if (this.animations[index].chart === chart){ - // replacing an in progress animation - this.animations[index].lastTimeRun = null; - this.animations[index].animationObject = animationObject; - return; - } - } - - this.animations.push({ - chart: chart, - animationObject: animationObject, - lastTimeRun: null - }); - - // If there are no animations queued, manually kickstart a digest, for lack of a better word - if(!this.animations.length){ - helpers.requestAnimFrame(this.startDigest); - } - }, - startDigest: function() { - - for (var i = 0; i < this.animations.length; i++) { - - var currentAnimation = this.animations[i]; - - if (currentAnimation.animationObject.currentStep === null){ - currentAnimation.animationObject.currentStep = 0; - } else { - currentAnimation.animationObject.currentStep++; - } - - currentAnimation.animationObject.render(currentAnimation.animationObject); - - if (currentAnimation.animationObject.currentStep == currentAnimation.animationObject.numSteps){ - // executed the last frame. Remove the animation. - this.animations.splice(i, 1); - // Keep the index in place to offset the splice - i--; - } - } - - // Do we have more stuff to animate? - if (this.animations.length > 0){ - requestAnimationFrame(this.startDigest); - } - } - }; - // Attach global event to resize each chart instance when the browser resizes helpers.addEvent(window, "resize", (function(){ // Basic debounce of resize function so it doesn't hurt performance when resizing browser.