Added custom animation curve support and spring animations

This commit is contained in:
Tsvetan Raikov
2016-02-09 14:50:32 +02:00
parent 630daa03e9
commit cc92fc329f
7 changed files with 212 additions and 42 deletions

View File

@ -53,7 +53,6 @@
"apps/connectivity-demo/app.ts",
"apps/connectivity-demo/main-page.ts",
"apps/custom-root-view/app.ts",
"apps/custom-root-view/list-view.ts",
"apps/cuteness.io/app.ts",
"apps/cuteness.io/details-page.ts",
"apps/cuteness.io/main-page.ts",

View File

@ -27,6 +27,21 @@ export interface PropertyAnimation {
curve?: any;
}
export class CustomAnimationCurve implements definition.CustomAnimationCurve {
public x1: number;
public y1: number;
public x2: number;
public y2: number;
constructor(x1: number, y1: number, x2: number, y2: number) {
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
}
}
export class Animation implements definition.Animation {
public _propertyAnimations: Array<PropertyAnimation>;
public _playSequentially: boolean;
@ -98,7 +113,7 @@ export class Animation implements definition.Animation {
}
var propertyAnimations = new Array<PropertyAnimation>();
// opacity
if (animationDefinition.opacity !== undefined) {
propertyAnimations.push({

View File

@ -26,7 +26,7 @@ propertyKeys[common.Properties.translate] = Symbol(keyPrefix + common.Properties
export class Animation extends common.Animation implements definition.Animation {
private _animatorListener: android.animation.Animator.AnimatorListener;
private _nativeAnimatorsArray: any;
private _animatorSet: android.animation.AnimatorSet;
private _animatorSet: android.animation.AnimatorSet;
private _animators: Array<android.animation.Animator>;
private _propertyUpdateCallbacks: Array<Function>;
private _propertyResetCallbacks: Array<Function>;
@ -102,7 +102,7 @@ export class Animation extends common.Animation implements definition.Animation
// It has been cancelled
return;
}
var i = 0;
var length = this._propertyUpdateCallbacks.length;
for (; i < length; i++) {
@ -145,7 +145,7 @@ export class Animation extends common.Animation implements definition.Animation
var density = utils.layout.getDisplayDensity();
var xyObjectAnimators: any;
var animatorSet: android.animation.AnimatorSet;
var key = propertyKeys[propertyAnimation.property];
if (key) {
propertyAnimation.target[key] = propertyAnimation;
@ -158,7 +158,7 @@ export class Animation extends common.Animation implements definition.Animation
}
}
}
switch (propertyAnimation.property) {
case common.Properties.opacity:
@ -270,7 +270,7 @@ export class Animation extends common.Animation implements definition.Animation
var i = 0;
var length = animators.length;
for (; i < length; i++) {
// Duration
if (propertyAnimation.duration !== undefined) {
animators[i].setDuration(propertyAnimation.duration);
@ -281,7 +281,7 @@ export class Animation extends common.Animation implements definition.Animation
animators[i].setStartDelay(propertyAnimation.delay);
}
// Repeat Count
// Repeat Count
if (propertyAnimation.iterations !== undefined && animators[i] instanceof android.animation.ValueAnimator) {
(<android.animation.ValueAnimator>animators[i]).setRepeatCount(Animation._getAndroidRepeatCount(propertyAnimation.iterations));
}
@ -307,6 +307,7 @@ var easeIn = new android.view.animation.AccelerateInterpolator(1);
var easeOut = new android.view.animation.DecelerateInterpolator(1);
var easeInOut = new android.view.animation.AccelerateDecelerateInterpolator();
var linear = new android.view.animation.LinearInterpolator();
var bounce = new android.view.animation.BounceInterpolator();
export function _resolveAnimationCurve(curve: any): any {
switch (curve) {
case enums.AnimationCurve.easeIn:
@ -321,8 +322,16 @@ export function _resolveAnimationCurve(curve: any): any {
case enums.AnimationCurve.linear:
trace.write("Animation curve resolved to android.view.animation.LinearInterpolator().", trace.categories.Animation);
return linear;
case enums.AnimationCurve.spring:
trace.write("Animation curve resolved to android.view.animation.BounceInterpolator().", trace.categories.Animation);
return bounce;
default:
trace.write("Animation curve resolved to original: " + curve, trace.categories.Animation);
if (curve instanceof common.CustomAnimationCurve) {
var animationCurve = <common.CustomAnimationCurve>curve;
var interpolator = (<any>(<any>(<any>android.support.v4.view).animation).PathInterpolatorCompat).create(animationCurve.x1, animationCurve.y1, animationCurve.x2, animationCurve.y2);
return interpolator;
}
return curve;
}
}

View File

@ -7,10 +7,10 @@
*/
export interface AnimationDefinition {
/**
* The view whose property is to be animated.
* The view whose property is to be animated.
*/
target?: viewModule.View;
/**
* Animates the opacity of the view. Value should be a number between 0.0 and 1.0
*/
@ -35,24 +35,24 @@
* Animates the rotate affine transform of the view. Value should be a number specifying the rotation amount in degrees.
*/
rotate?: number;
/**
* The length of the animation in milliseconds. The default duration is 300 milliseconds.
*/
duration?: number;
/**
* The amount of time, in milliseconds, to delay starting the animation.
* The amount of time, in milliseconds, to delay starting the animation.
*/
delay?: number;
/**
* Specifies how many times the animation should be played. Default is 1.
* iOS animations support fractional iterations, i.e. 1.5.
* To repeat an animation infinitely, use Number.POSITIVE_INFINITY
*/
iterations?: number;
/**
* An optional animation curve. Possible values are contained in the [AnimationCurve enumeration](../enums/AnimationCurve/README.md).
* Alternatively, you can pass an instance of type UIViewAnimationCurve for iOS or android.animation.TimeInterpolator for Android.
@ -60,6 +60,20 @@
curve?: any;
}
/**
* Defines a custom animation timing curve by using the cubic-bezier function.
* Possible values are numeric values from 0 to 1
*/
export class CustomAnimationCurve {
public x1: number;
public y1: number;
public x2: number;
public y2: number;
constructor(x1: number, y1: number, x2: number, y2: number);
}
/**
* Defines a pair of values (horizontal and vertical) for translate and scale animations.
*/
@ -81,4 +95,4 @@
//@private
export function _resolveAnimationCurve(curve: any): any;
//@endprivate
}
}

View File

@ -11,6 +11,8 @@ var _skip = "_skip";
var FLT_MAX = 340282346638528859811704183484516925440.000000;
declare var CASpringAnimation:any;
class AnimationDelegateImpl extends NSObject {
public nextAnimation: Function;
@ -73,6 +75,27 @@ class AnimationDelegateImpl extends NSObject {
this.nextAnimation();
}
}
public uiview_animationWillStart(animationID: string, context: any): void {
trace.write("AnimationDelegateImpl.animationWillStart, animationID: " + animationID, trace.categories.Animation);
}
public uiview_animationDidStop(animationID: string, finished: boolean, context: any): void {
trace.write("AnimationDelegateImpl.animationDidStop, animationID: " + animationID + ", finished: " + finished, trace.categories.Animation);
if (this._finishedCallback) {
var cancelled = !finished;
// This could either be the master finishedCallback or an nextAnimationCallback depending on the playSequentially argument values.
this._finishedCallback(cancelled);
}
if (!finished) {
if ((<any>this._propertyAnimation)._propertyResetCallback) {
(<any>this._propertyAnimation)._propertyResetCallback((<any>this._propertyAnimation)._originalValue);
}
}
if (finished && this.nextAnimation) {
this.nextAnimation();
}
}
}
export class Animation extends common.Animation implements definition.Animation {
@ -149,6 +172,7 @@ export class Animation extends common.Animation implements definition.Animation
finishedCallback(cancelled);
return;
}
var animation = propertyAnimations[index];
var nativeView = <UIView>animation.target._nativeView;
var propertyNameToAnimate = animation.property;
@ -163,11 +187,18 @@ export class Animation extends common.Animation implements definition.Animation
(<any>animation)._originalValue = animation.target.backgroundColor;
(<any>animation)._propertyResetCallback = (value) => { animation.target.backgroundColor = value };
if (presentationLayer != null) {
if (nativeView instanceof UILabel) {
nativeView.backgroundColor = UIColor.clearColor();
}
originalValue = presentationLayer.backgroundColor;
}
else {
originalValue = nativeView.layer.backgroundColor;
}
if (nativeView instanceof UILabel) {
originalValue = nativeView.layer.backgroundColor;
nativeView.setValueForKey(UIColor.clearColor(), "backgroundColor");
}
value = value.CGColor;
break;
case common.Properties.opacity:
@ -242,15 +273,100 @@ export class Animation extends common.Animation implements definition.Animation
throw new Error("Cannot animate " + animation.property);
}
var nativeAnimation = CABasicAnimation.animationWithKeyPath(propertyNameToAnimate);
var nativeAnimation;
var isSpring = false;
if (animation.curve === enums.AnimationCurve.spring) {
// nativeAnimation = CASpringAnimation.animationWithKeyPath(propertyNameToAnimate);
// nativeAnimation.duration = nativeAnimation.settlingDuration;
// nativeAnimation.damping = 3;
var duration = 0.3;
if (animation.duration !== undefined) {
duration = animation.duration / 1000.0;
}
var delay = 0;
if (animation.delay !== undefined) {
delay = CACurrentMediaTime() + (animation.delay / 1000.0);
}
var animationDelegate: AnimationDelegateImpl;
animationDelegate = AnimationDelegateImpl.initWithFinishedCallback(finishedCallback, animation);
var callback = undefined;
if (index+1 < propertyAnimations.length) {
callback = Animation._createiOSAnimationFunction(propertyAnimations, index+1, playSequentially, finishedCallback, that);
if (!playSequentially) {
callback();
}
else {
animationDelegate.nextAnimation = callback;
}
}
UIView.animateWithDurationDelayUsingSpringWithDampingInitialSpringVelocityOptionsAnimationsCompletion(duration, delay, 0.2, 0,
UIViewKeyframeAnimationOptions.UIViewKeyframeAnimationOptionCalculationModeLinear,
() => {
if (animationDelegate) {
UIView.setAnimationDelegate(animationDelegate);
UIView.setAnimationWillStartSelector("uiview_animationWillStart");
UIView.setAnimationDidStopSelector("uiview_animationDidStop");
}
if (animation.iterations !== undefined) {
if (animation.iterations === Number.POSITIVE_INFINITY) {
UIView.setAnimationRepeatCount(FLT_MAX);
}
else {
UIView.setAnimationRepeatCount(animation.iterations - 1);
}
}
switch (animation.property) {
case common.Properties.backgroundColor:
animation.target.backgroundColor = value;
break;
case common.Properties.opacity:
animation.target.opacity = value;
break;
case common.Properties.rotate:
nativeView.layer.setValueForKey(value, propertyNameToAnimate);
break;
case _transform:
nativeView.layer.setValueForKey(value, propertyNameToAnimate);
break;
}
}, function (finished:boolean) {
if (finished) {
if (animation.property === _transform) {
if (animation.value[common.Properties.translate] !== undefined) {
animation.target.translateX = animation.value[common.Properties.translate].x;
animation.target.translateY = animation.value[common.Properties.translate].y;
}
if (animation.value[common.Properties.scale] !== undefined) {
animation.target.scaleX = animation.value[common.Properties.scale].x;
animation.target.scaleY = animation.value[common.Properties.scale].y;
}
}
}
});
return;
}
else {
nativeAnimation = CABasicAnimation.animationWithKeyPath(propertyNameToAnimate);
}
nativeAnimation.fromValue = originalValue;
nativeAnimation.toValue = value;
nativeAnimation.toValue = value;
if (animation.duration !== undefined) {
nativeAnimation.duration = animation.duration / 1000.0;
}
else {
nativeAnimation.duration = 0.3;
};
}
if (animation.delay !== undefined) {
nativeAnimation.beginTime = CACurrentMediaTime() + (animation.delay / 1000.0);
}
@ -263,7 +379,7 @@ export class Animation extends common.Animation implements definition.Animation
nativeAnimation.repeatCount = animation.iterations - 1;
}
}
if (animation.curve !== undefined) {
if (!isSpring && animation.curve !== undefined) {
trace.write("The animation curve is " + animation.curve, trace.categories.Animation);
nativeAnimation.timingFunction = animation.curve;
}
@ -353,8 +469,10 @@ export class Animation extends common.Animation implements definition.Animation
value: {},
duration: propertyAnimations[i].duration,
delay: propertyAnimations[i].delay,
iterations: propertyAnimations[i].iterations
iterations: propertyAnimations[i].iterations,
curve: propertyAnimations[i].curve
};
trace.write("Curve: " + propertyAnimations[i].curve, trace.categories.Animation);
newTransformAnimation.value[propertyAnimations[i].property] = propertyAnimations[i].value;
trace.write("Created new transform animation: " + common.Animation._getAnimationInfo(newTransformAnimation), trace.categories.Animation);
@ -388,7 +506,16 @@ export function _resolveAnimationCurve(curve: any): any {
return CAMediaTimingFunction.functionWithName(kCAMediaTimingFunctionEaseInEaseOut);
case enums.AnimationCurve.linear:
return CAMediaTimingFunction.functionWithName(kCAMediaTimingFunctionLinear);
case enums.AnimationCurve.spring:
return curve;
default:
if (curve instanceof CAMediaTimingFunction) {
return curve;
}
else if (curve instanceof common.CustomAnimationCurve) {
var animationCurve = <common.CustomAnimationCurve>curve;
return CAMediaTimingFunction.functionWithControlPoints(animationCurve.x1, animationCurve.y1, animationCurve.x2, animationCurve.y2);
}
return undefined;
}
}

47
ui/enums/enums.d.ts vendored
View File

@ -9,25 +9,25 @@
* iOS: [UIKeyboardTypeNumbersAndPunctuation](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITextInputTraits_Protocol/index.html#//apple_ref/c/tdef/UIKeyboardType)
*/
export var datetime: string;
/**
* Android: [TYPE_CLASS_PHONE](http://developer.android.com/reference/android/text/InputType.html#TYPE_CLASS_PHONE)
* iOS: [UIKeyboardTypePhonePad](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITextInputTraits_Protocol/index.html#//apple_ref/c/tdef/UIKeyboardType)
*/
export var phone: string;
/**
* Android: [TYPE_CLASS_NUMBER](http://developer.android.com/reference/android/text/InputType.html#TYPE_CLASS_NUMBER) | android.text.InputType.TYPE_NUMBER_VARIATION_NORMAL | [TYPE_NUMBER_FLAG_SIGNED](http://developer.android.com/reference/android/text/InputType.html#TYPE_NUMBER_FLAG_SIGNED) | [TYPE_NUMBER_FLAG_DECIMAL](http://developer.android.com/reference/android/text/InputType.html#TYPE_NUMBER_FLAG_DECIMAL)
* iOS: [UIKeyboardTypeNumbersAndPunctuation](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITextInputTraits_Protocol/index.html#//apple_ref/c/tdef/UIKeyboardType)
*/
export var number: string;
/**
* Android: [TYPE_CLASS_TEXT](http://developer.android.com/reference/android/text/InputType.html#TYPE_CLASS_TEXT) | [TYPE_TEXT_VARIATION_URI](http://developer.android.com/reference/android/text/InputType.html#TYPE_TEXT_VARIATION_URI)
* iOS: [UIKeyboardTypeURL](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITextInputTraits_Protocol/index.html#//apple_ref/c/tdef/UIKeyboardType)
*/
export var url: string;
/**
* Android: [TYPE_CLASS_TEXT](http://developer.android.com/reference/android/text/InputType.html#TYPE_CLASS_TEXT) | [TYPE_TEXT_VARIATION_EMAIL_ADDRESS](http://developer.android.com/reference/android/text/InputType.html#TYPE_TEXT_VARIATION_EMAIL_ADDRESS)
* iOS: [UIKeyboardTypeEmailAddress](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITextInputTraits_Protocol/index.html#//apple_ref/c/tdef/UIKeyboardType)
@ -56,13 +56,13 @@
* iOS: [UIReturnKeyGo](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITextInputTraits_Protocol/index.html#//apple_ref/c/tdef/UIReturnKeyType)
*/
export var go: string;
/**
* Android: [IME_ACTION_SEARCH](http://developer.android.com/reference/android/view/inputmethod/EditorInfo.html#IME_ACTION_SEARCH)
* iOS: [UIReturnKeySearch](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITextInputTraits_Protocol/index.html#//apple_ref/c/tdef/UIReturnKeyType)
*/
export var search: string;
/**
* Android: [IME_ACTION_SEND](http://developer.android.com/reference/android/view/inputmethod/EditorInfo.html#IME_ACTION_SEND)
* iOS: [UIReturnKeySend](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITextInputTraits_Protocol/index.html#//apple_ref/c/tdef/UIReturnKeyType)
@ -324,17 +324,17 @@
export var none: string;
/**
* Capitalize the first letter of each word automatically.
* Capitalize the first letter of each word automatically.
*/
export var words: string;
/**
* Capitalize the first letter of each sentence automatically.
* Capitalize the first letter of each sentence automatically.
*/
export var sentences: string;
/**
* Capitalize all characters automatically.
* Capitalize all characters automatically.
*/
export var allCharacters: string;
}
@ -402,7 +402,7 @@
*/
export var popup: string;
}
/**
* Specifies different font styles.
*/
@ -411,7 +411,7 @@
* Normal font style.
*/
export var normal: string;
/**
* Italic font style.
*/
@ -426,7 +426,7 @@
* No decoration.
*/
export var none: string;
/**
* Text decoration underline.
*/
@ -446,7 +446,7 @@
* No transform.
*/
export var none: string;
/**
* Text transform capitalize.
*/
@ -456,7 +456,7 @@
* Text transform uppercase.
*/
export var uppercase: string;
/**
* Text transform lowercase.
*/
@ -471,13 +471,13 @@
* Normal wrap.
*/
export var normal: string;
/**
* No wrap.
*/
export var nowrap: string;
}
/**
* Specifies different font weights.
*/
@ -486,13 +486,13 @@
* Normal font weight.
*/
export var normal: string;
/**
* Bold font weight.
*/
export var bold: string;
}
/**
* Specifies background repeat.
*/
@ -536,10 +536,15 @@
* An ease-in ease-out curve causes the animation to begin slowly, accelerate through the middle of its duration, and then slow again before completing.
*/
export var easeInOut: string;
/**
* A linear animation curve causes an animation to occur evenly over its duration.
*/
export var linear: string;
/**
* A spring animation curve causes an animation to produce a spring (bounce) effect.
*/
export var spring: string;
}
}
}

View File

@ -162,4 +162,5 @@ export module AnimationCurve {
export var easeOut = "easeOut";
export var easeInOut = "easeInOut";
export var linear = "linear";
}
export var spring = "spring";
}