You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@royale.apache.org by ha...@apache.org on 2022/01/06 14:00:15 UTC

[royale-asjs] branch develop updated: animateFunction now returns an Animated which can be started and stopped. Animated can be instantiated directly. When it is, it can be optionally reusable, so the animation can be run morew than once.

This is an automated email from the ASF dual-hosted git repository.

harbs pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/royale-asjs.git


The following commit(s) were added to refs/heads/develop by this push:
     new e7d41e4  animateFunction now returns an Animated which can be started and stopped. Animated can be instantiated directly. When it is, it can be optionally reusable, so the animation can be run morew than once.
e7d41e4 is described below

commit e7d41e48c34c18b8e43e48abb194457337ad0653
Author: Harbs <ha...@in-tools.com>
AuthorDate: Thu Jan 6 16:00:04 2022 +0200

    animateFunction now returns an Animated which can be started and stopped.
    Animated can be instantiated directly. When it is, it can be optionally reusable,
    so the animation can be run morew than once.
---
 .../org/apache/royale/utils/functional/Animated.as | 301 +++++++++++++++++++++
 .../royale/utils/functional/animateFunction.as     |  94 +------
 .../test/royale/flexUnitTests/FunctionalTests.as   |  23 +-
 3 files changed, 325 insertions(+), 93 deletions(-)

diff --git a/frameworks/projects/Core/src/main/royale/org/apache/royale/utils/functional/Animated.as b/frameworks/projects/Core/src/main/royale/org/apache/royale/utils/functional/Animated.as
new file mode 100644
index 0000000..a5ed1f8
--- /dev/null
+++ b/frameworks/projects/Core/src/main/royale/org/apache/royale/utils/functional/Animated.as
@@ -0,0 +1,301 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  Licensed to the Apache Software Foundation (ASF) under one or more
+//  contributor license agreements.  See the NOTICE file distributed with
+//  this work for additional information regarding copyright ownership.
+//  The ASF licenses this file to You under the Apache License, Version 2.0
+//  (the "License"); you may not use this file except in compliance with
+//  the License.  You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+//  Unless required by applicable law or agreed to in writing, software
+//  distributed under the License is distributed on an "AS IS" BASIS,
+//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//  See the License for the specific language governing permissions and
+//  limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////////
+package org.apache.royale.utils.functional
+{
+
+	COMPILE::SWF
+	{
+		import flash.utils.setTimeout;
+		import flash.utils.clearTimeout;
+		import flash.utils.Timer;
+		import flash.events.TimerEvent;
+	}
+
+	public class Animated
+	{
+
+		public static const STOPPED:String = "stopped";
+		public static const RUNNING:String = "running";
+		public static const FINISHED:String = "finished";
+
+		public function Animated(toAnimate:Function,fps:Number=60,reusable:Boolean=false)
+		{
+			_reusable = reusable;
+			this.fps = fps;
+			animateFunction = toAnimate;
+
+			lastTimeStamp = 0;
+			invocations = [];
+
+		}
+		private var animateFunction:Function;
+		private var limit:Number;
+		private var lastTimeStamp:Number;
+		private var invocations:Array;
+
+		private var state:String = STOPPED;
+
+		private var _reusable:Boolean;
+
+		/**
+		 * Whether the animation can be run again. (restart)
+		 * 
+		 * @langversion 3.0
+		 * @productversion Royale 0.9.9
+		 */
+		public function get reusable():Boolean
+		{
+			return _reusable;
+		}
+
+		private var _fps:Number;
+
+		/**
+		 * frames per second to run the animation.
+		 * 
+		 * @langversion 3.0
+		 * @productversion Royale 0.9.9
+		 */
+		public function get fps():Number
+		{
+			return _fps;
+		}
+
+		public function set fps(value:Number):void
+		{
+			_fps = value;
+			limit = 1000/value;
+		}
+
+		COMPILE::SWF
+		private var timer:Timer;
+		COMPILE::SWF
+		public function start():void
+		{
+			
+			if(!invocations.length)
+			{
+				killTimer(FINISHED);
+				return;
+			}
+			if(timer)
+				return;
+
+			var currentTime:Number = new Date().getTime();
+			var previousState:String = state;
+			state = RUNNING;
+			createTimer();
+			swfCallback(null);			
+		}
+
+		COMPILE::SWF
+		private function swfCallback(ev:TimerEvent):void
+		{
+			if(state == STOPPED)
+			{
+				killTimer(state)
+				return;
+			}
+			
+			if(invocations.length == 0)
+			{
+				killTimer(FINISHED);
+				return;
+			}	
+
+			if(invocations.length)
+			{
+				var currentArgs:Array = shiftInvocation();
+				animateFunction.apply(null,currentArgs);
+			}
+			else
+			{
+				killTimer(FINISHED);
+			}
+		}
+		private var usedInvocations:Array;
+		private function shiftInvocation():Array
+		{
+			var invoc:Array = invocations.shift();
+			if(_reusable)
+			{
+				if(!usedInvocations)
+					usedInvocations = [];
+
+				usedInvocations.push(invoc);
+			}
+			return invoc;
+		}
+		COMPILE::SWF
+		private function createTimer():void
+		{
+			killTimer(state);
+			timer = new Timer(limit);
+			timer.addEventListener(TimerEvent.TIMER,swfCallback);
+			timer.start();
+		}
+		COMPILE::SWF
+		private function killTimer(state:String):void
+		{
+			this.state = state;
+			if(timer){
+				timer.stop();
+				timer.removeEventListener(TimerEvent.TIMER,swfCallback);
+			}
+		}
+		/**
+		 * starts the animation
+		 * 
+		 * @langversion 3.0
+		 * @productversion Royale 0.9.9
+		 */
+
+		COMPILE::JS
+		public function start():void
+		{
+			if(state == RUNNING)
+				return;
+
+			if(invocations.length)
+			{
+				state = RUNNING;
+				requestAnimationFrame(jsCallback);
+			}
+			else
+				state = FINISHED;
+		}
+
+		COMPILE::JS
+		private function jsCallback(timeStamp:Number):void
+		{
+			if(state == STOPPED)
+				return;
+			
+			if(invocations.length == 0)
+			{
+				state = FINISHED;
+				return;
+			}	
+
+			// we can't rely on getting time stamps ourselves,
+			// so hopefully this is not slower than our target rate...
+			if ( (timeStamp - lastTimeStamp) >= limit)
+			{
+				if(lastTimeStamp == 0)
+					lastTimeStamp = timeStamp;
+				else
+					lastTimeStamp += limit; // make sure we stick to the desired rate
+				var args:Array = shiftInvocation();
+				animateFunction.apply(null,args);
+			}
+			// if there's no more operations, we'll catch it on the next animation frame and set the state to finished.
+			// It's possible to push another call before the next animation frame.
+			requestAnimationFrame(jsCallback);
+		}
+
+
+		/**
+		 * stops an animation. If there are pending operations, they can be run by calling start.
+		 * 
+		 * @langversion 3.0
+		 * @productversion Royale 0.9.9
+		 */
+		public function stop():void
+		{
+			state = STOPPED;
+		}
+		/**
+		 * Reruns all the functions from the beginning. Only works if the Animated is reusable.
+		 * Returns true if there was something to rerun, otherwise returns false.
+		 * Either way, restart will function as start.
+		 * 
+		 * @langversion 3.0
+		 * @productversion Royale 0.9.9
+		 */
+		public function restart():Boolean
+		{
+			var restarted:Boolean = false;
+			if(usedInvocations && usedInvocations.length)
+			{
+				lastTimeStamp = 0;
+				restarted = true;
+				invocations = usedInvocations.concat(invocations);
+			}
+			start();
+			return restarted;
+		}
+
+		/**
+		 * returns true if the animation is currently running
+		 * 
+		 * @langversion 3.0
+		 * @productversion Royale 0.9.9
+		 */
+		public function isRunning():Boolean
+		{
+			return state == RUNNING;
+		}
+
+		/**
+		 * returns true if the animation has more operations to call
+		 * 
+		 * @langversion 3.0
+		 * @productversion Royale 0.9.9
+		 */
+		public function isPending():Boolean
+		{
+			return invocations.length > 0;
+		}
+
+		/**
+		 * returns true if the animation finished running
+		 * 
+		 * @langversion 3.0
+		 * @productversion Royale 0.9.9
+		 */
+		public function isFinished():Boolean
+		{
+			return state == FINISHED;
+		}
+
+		/**
+		 * adds an invocation to the animation
+		 * 
+		 * @langversion 3.0
+		 * @productversion Royale 0.9.9
+		 */
+		public function push(args:Array):void
+		{
+			trace(args);
+			invocations.push(args);
+		}
+		/**
+		 * Removes all operations from the animation
+		 * 
+		 * @langversion 3.0
+		 * @productversion Royale 0.9.9
+		 */
+		public function clear():void
+		{
+			invocations = [];
+			usedInvocations = null;
+		}
+
+	}
+}
\ No newline at end of file
diff --git a/frameworks/projects/Core/src/main/royale/org/apache/royale/utils/functional/animateFunction.as b/frameworks/projects/Core/src/main/royale/org/apache/royale/utils/functional/animateFunction.as
index 32b9fbf..190eb3e 100644
--- a/frameworks/projects/Core/src/main/royale/org/apache/royale/utils/functional/animateFunction.as
+++ b/frameworks/projects/Core/src/main/royale/org/apache/royale/utils/functional/animateFunction.as
@@ -34,97 +34,13 @@ package org.apache.royale.utils.functional
 	 */
 	public function animateFunction(method:Function, fps:Number):Function
 	{
-		COMPILE::SWF
-		{
-			var limit:Number = 1000/fps;
-			var timeStamp:Number = 0;
-			var timeoutRef:*;
-			var invocations:Array = [];
-			return function(...args):void
-			{
-				if(timeoutRef){
-					clearTimeout(timeoutRef);
-					timeoutRef = null;
-				}
-				invocations.push(args);
-				var currentTime:Number = new Date().getTime();
-				var timeDiff:Number = currentTime - timeStamp;
-				if(timeDiff >= limit)
-				{
-					if(timeStamp == 0)
-						timeStamp = currentTime;
-					else
-						timeStamp += limit;
-					method.apply(null,invocations.shift());
-				}
-				if(invocations.length && timeoutRef == null)
-				{
-					// currentTime = new Date().getTime();
-					timeDiff = currentTime - timeStamp + limit;
-					var nextInterval:Number = Math.max(timeDiff,0);
-					timeoutRef = setTimeout(callback, nextInterval);
-				}
-
-				function callback():void
-				{
-					timeoutRef = null;
+		var animated:Animated = new Animated(method,fps);
 
-					if(!invocations.length)
-						return;
-					
-					var currentArgs:Array = invocations.shift();
-					method.apply(null,currentArgs);
-					timeStamp += limit;
-					var timeDiff:Number = new Date().getTime() - timeStamp + limit;
-					while(timeDiff < 0)
-					{
-						// catch up on the missing frames
-						method.apply(null,invocations.shift());
-						if(invocations.length == 0)
-						{
-							return;
-						}
-						timeDiff+=limit;
-					}
-					if(invocations.length)
-					{
-						timeoutRef = setTimeout(callback, timeDiff);
-					}
-				}
-			}
-
-		}
-
-		COMPILE::JS
+		return function(...args):Animated
 		{
-			var limit:Number = 1000/fps;
-			var lastTimeStamp:Number = 0;
-			var timeoutRef:*;
-			var invocations:Array = [];
-			return function(...args):void
-			{
-				invocations.push(args);
-				requestAnimationFrame(callback);
-				function callback(timeStamp:Number):void
-				{
-					if(invocations.length == 0)
-						return;
-
-					// we can't rely on getting time stamps ourselves,
-					// so hopefully this is not slower than our target rate...
-					if ( (timeStamp - lastTimeStamp) >= limit)
-					{
-						if(lastTimeStamp == 0)
-							lastTimeStamp = timeStamp;
-						else
-							lastTimeStamp += limit; // make sure we stick to the desired rate
-
-						method.apply(null,invocations.shift());
-					}
-					if(invocations.length)
-						requestAnimationFrame(callback);
-				}
-			}
+			animated.push(args);
+			animated.start();
+			return animated;
 		}
 	}
 }
\ No newline at end of file
diff --git a/frameworks/projects/Core/src/test/royale/flexUnitTests/FunctionalTests.as b/frameworks/projects/Core/src/test/royale/flexUnitTests/FunctionalTests.as
index f17c1a7..fc68806 100644
--- a/frameworks/projects/Core/src/test/royale/flexUnitTests/FunctionalTests.as
+++ b/frameworks/projects/Core/src/test/royale/flexUnitTests/FunctionalTests.as
@@ -206,7 +206,7 @@ package flexUnitTests
             var savedThisValue:Number;
             setTimeout(function():void{
                 savedThisValue = foo.value;
-            },50);
+            },75);
 
             var value:Number = 0;
             function increment(val:Number):void{
@@ -220,11 +220,26 @@ package flexUnitTests
 
             setTimeout(function():void{
                 savedValue = value;
-            },50);
+            },75);
+
+            var stoppedValue:Number = 0;
+            function incrementStopped(val:Number):void{
+                stoppedValue+=val;
+            }
+            var stopped:Function = animateFunction(incrementStopped,20);
+            for(i=0;i<30;i++){
+                var stoppedRef:Animated = stopped(1) as Animated;
+            }
+            setTimeout(function():void{
+                stoppedRef.stop();
+            },10);
+
+
             Async.delayCall(this, function():void
             {
-                assertTrue(savedThisValue<3,"foo value should be 2");
-                assertTrue(savedValue<3,"value should be 2");
+                assertEquals(savedThisValue,2,"foo value should be 2");
+                assertEquals(savedValue,2,"value should be 2");
+                assertEquals(stoppedValue,1,"value should be 1");
             }, 300);
         }
         public function testDelay():void