Arbitrary Type Interpolation - Functional and Design Specification
Glossary
Interpolator: I use this term to refer to calculating an animated value v given an initial value v1, a final value v2, and an elapsed fraction f of the duration of the animation. A typical linear interpolation is usually represented by the parametric equation:
v = v1 + f * (v2 - v1)
The use of interpolators in this feature is in deriving the value v for arbitrary types (colors, Numbers, Path segments, whatever). So where the actual interpolation equation may be the same for all types, the actual function may not be because we must access the data in a type-specific way (colors need to break up the numbers into RGB channels, etc.).
So think of interpolators, and the IInterpolator implementations in particular, as a way to translate from an elapsed fraction and the endpoints of an animation into a linearly-interpolated value of any given type.
Easing: The easing classes (both existing and proposed) also interpolate, but they serve a different purpose than the IInterpolator implementation classes. In the existing code, the functionality overlaps, because they define both an arbitrary easing function (linear or non-linear) upon the animation and the interpolation of values between the endpoints. In essence, they are performing both time interpolation (this is the 'easing' part) and value interpolation (where all values are of type Number). In the new system, we will use the IInterpolator implementing classes to perform the interpolation (because we are dealing with objects, not just numbers). The easing classes will provide the smaller case of just the timing behavior interpolation. Given an elapsed fraction of the animation, an easing function will return a potentially different (eased) fraction. This fraction is then passed into an IInterpolator to retrieve an appropriate value for that eased fraction.
Summary and Background
The current Tween class interpolates only between Number values. This is sufficient for the bulk of cases in our current components, where most animatable attributes are represented as numbers. But it is not sufficient for all objects and properties.
For example, a simple RGB interpolation between different colors requires splitting up each number into its component channels and interpolating separately between those values.
Similarly, interpolating between different objects in the new FXG elements, such as Path segments, requires custom work to extract the numbers of the different elements of each segment, interpolating on those values, and then recomposing the results.
What we need is a more general Tween system that can handle interpolating between arbitrary types.
Usage Scenarios
The most prevalent use case is color-interpolating effects that can supply start/end values for the colors and expect the system to correctly interpolate the inter-frame values of the colors.
Another example is interpolating between different states of FXG elements, such as a moving Path object.
A third example is a custom effect that a developer supplies which animates between states of an object of some unknown type. Ideally, our system would not only have interpolators for some standard known types, such as colors, but would also have the ability to plug in custom interpolators for types that the system does not know.
Detailed Description
This feature will supply a new Tween class, called Animation. This new class will share much of the functionality of the original Tween class (runs off a single Timer, is able to pause/stop/reverse, uses easing functions, dispatches events, interpolates values and sends them out to listeners), but will differ in some important ways:
- new "interpolator" property: The user may supply an optional interpolator class which, for each frame, will be passed the start/end values supplied to the Animation constructor along with the elapsed fraction of the animation. A new value will be calculated by this interpolator and dispatched returned to the caller. By default, if the user does not supply an interpolator, the Animation will use an interpolator that operates on Numbers or an Array of Numbers, depending on the type of the input start/end values.
- new constructor: The old Tween class has a constructor that takes an opaque Object as the event listener. Presumably this pre-dates AS3, as we should give the user more information about what this object is by having it be some Interface instead. But more importantly, we already have an event listening mechanism that we should use instead. Also, some of the other constructor parameters seem far less important at construction time, and we may want a constructor that does not require them. For example, minFps does not seem to be used at all in Tween, and the updateFunction and endFunction parameters are optional and redundant with the concept of event listeners. In essence, I would like to provide a simplified constructor for this new Animation class that just takes a duration, start and end values.
- no auto-play: The current Tween class automatically starts upon construction. I think that it is desirable for developers to be able to construct objects separate from actually using them. I would like to add a play() function so that construction simply creates the data structures involved and play() actually starts the Animation. This will also necessitate adding a 'start' event that gets sent out, like the current update/end calls.
- mx_internal API: There are some mx_internal properties and functions in Tween that I would like to get away from in the new class.
- Easing functions: the current easing function API has useful functionality but a somewhat awkward API. I would like to provide a simpler API for easing functions that allows more customization, while still providing the ability to use these older easing functions for developers that are used to them.
- Repeat behavior: Currently, there is repeat behavior specified in Effect, by way of the int value repeatCount. I would like to see this behavior moved to the Animation level, since repetition is not just a characteristic of effects, but is instead a timing-engine attribute. Also, the current repeat behavior is very minimal; I would like to see the behavior enhanced to provide more capabilities (and allow easier coding for developers because the engine does more for them). The Animation API below shows the new repeatBehavior functionality at work.
API Description
There are a few separate pieces to the new API. First, we need a new "IInterpolator" interface that standard and user-supplied interpolators will implement:
There are several standard implementations of IInterpolator that Flex will provide, including one that interpolates Numbers, one that interpolates Arrays of elements (where the caller supplies another interpolator for each array element), and one that interpolates Arrays of Numbers (just like the previous one, but preconfigured to deal with interpolating Number elements for this common case). Here, for example, are the NumberInterpolator and ArrayInterpolator classes:
Note that NumberInterpolator provides a Singleton accessor because there is no need for more than one of these objects for any given system. It is expected, although not required, that other IInterpolator implementations would do similiarly.
One Additional IInterpolator implementation class that we should ship with Flex is one that interpolates colors. This is probably the most common case of interpolation that cannot be handled simply by our current Number interpolation.
We also plan to offer a new Animation class. It will have similar functionality to the existing Tween class, but will allow custom type interpolation and offers a simpler API for creating and playing Animations. It is the main class used for running time-based effects (apart from the old TweenEffect classes, which will still use Tween).
The new effects for Gumbo use Animation. Please refer to the New Effects for Components and Graphics spec for more information on these effects.
Finally, while we're changing the core Tween API, we should update the easing functions API as well. The current API is somewhat awkward and inflexible, and we should consider adding a more flexible and simple API instead. I would like to do this in a way where people could still use the existing easing functions, because their functionality is quite useful.
To be more specific, some of the problems I have with the current easing API include:
- The easing functions all take 4 parameters, but they could really be simplified to just one: the elapsed duration of the animation. This single value encapsulates all of the information in the current parameters: elapsed time (t), duration (d), (start - end) value (c), and initial value (b). Given an elapsed fraction, the function could return an eased value which represents that fraction eased, which could then be applied by the Animation and its interpolator to the actual start/end values.
- The easing functions are consistent ... to a fault. It doesn't make sense, from an API standpoint, to have Linear.EaseIn, Linear.EaseOut, and Linear.EaseInOut all as different functions which do exactly the same thing: linearly interpolate. There is no concept of "easing" where linear interpolation is concerned, so having these functions on the Linear class seems confusing and wrong. I would like to see the easing classes more clearly represent what they do (and don't do).
- The easing function is provided to Tween as an opaque "Function" object, which doesn't give much information to the API reader what they are supposed to do to handle calls to the function. I would like to see the API document more clearly what an easing function is supposed to do, by providing an interface or class type that will be checked at compile time to make sure that the easing call will be handled appropriately.
- The current easing functions are very inflexible - they calculate specific easing functions ... period. There is one for Sine, one for Cubic, one for Quadratic, etc. But there are none that take parameters that define the shape of a curve, or the amount of time spent easing in and out. It seems like useful functionality to define some classes that have flexible parameters that define the timing curve, and which allow developers to customize the easing function without having to define entirely new easing classes.
Most importantly, I'd like to have a simple interface that could be implemented easily to provide arbitrary easing functionality.
There will be simple subclasses that implement this interface in order to provide most expected easing functionality out of the box (in addition to an interface that is easy to implement for custom easing functionality that developers wish to provide). For example, here is a minimum set of easing classes that should be provided. These will take the place of the current Linear, Sine, Cubic, Quartic, Quadratic, and Quintic classes (note that the last four are replaced by the single Power class below). Constant is an additional class that provides functionality that does not exist now. And All of these, apart from Linear, provide parameterization of the easing in and out fractions, which give more flexibility than the previous easing functions. The EaseInOut class is a superclass of several of these classes, providing shared functionality. It is not intended to be used directly by callers.
B Features
Flash Easing Functions
The Flash "Motion Objects" work in Flash 10 provides several canonical easing functions, all of which use the same method of interpolating a single fractional (0-1) value. We should consider using some or all of their functions (as appropriate for open source Flex) to enable sharing of code and approaches across the products. Rather than invent new easing functions specific to Flex, let's use functions that already exist.
Examples and Usage
The real use of Animation from MXML would be indirectly, through the use of effects. This usage is shown in the New Effects for Components and Graphics spec. When Animation is used directly, it will usually be done from ActionScript code, as shown below. In this example, we set up an Animation to run between 0 and 1 for 1000 milliseconds. The animation will run twice, playing in reverse at the end of the first cycle. It will be set up with the specified 'easer' object and will call our 'updateHandler' listener.
Additional Implementation Details
All of the items above except those in the B Features section have already been implemented and checked in. These classes are currently used by the new effects that are specified in the New Effects for Components and Graphics spec.
Prototype Work
Most of the above classes, other than the items in 'B features', have working prototypes.
Compiler Work
None anticipated.
Web Tier Compiler Impact
N/A
Flex Feature Dependencies
The effects documented in New Effects for Components and Graphics depend on the classes specified here.
Backwards Compatibility
Syntax changes
As specified above, there are no compatibility issues. We might consider integrating some of the functionality above into existing classes instead of new classes. For example, maybe TweenEffectInstance should spawn an Animation object instead of a Tween object; this would affect some API in this existing class if we choose to go this route.
Behavior
none anticipated
Warnings/Deprecation
None anticipated, although if we choose to move all Effects to use Animation instead of the old Tween class, we might think about deprecating classes such as Tween and the Easing functions.
Accessibility
None anticipated
Performance
As with any animation system, timely performance is key to usefulness. We will need to ensure that the functionality we add can be accomplished in a performance-sensitive manner.
Globalization
None anticipated
Localization
Compiler Features
None anticipated
Framework Features
None anticipated
Issues and Recommendations
We need to decide whether to reinvent or revise existing classes, where applicable. For example, should we leave the old Tween and add the new Animation class? Or should we change the implementation of Tween in an incompatible way? Should we add new effect base classes that use the new Animation class? Or should we change existing effect classes to use the new Animation class instead?
When we change the Easing API, how much functionality should we ship out of the box? Is it good enough to offer the existing easing functions via the new API? Or some subset of these functions? Should we provide a wrapper layer for the old functions ('Classic') that provide exactly the old easing functions through the new interface? Should we try to re-use the Flash authoring functions (directly or indirectly through their easing algorithms)?
Documentation
None anticipated, other than typical API documentation
QA
Yes
|
 |