fix(core): animation iteration correct for android css animations and iOS rotation (#9628)

closes #7712

Co-authored-by: Nathan Walker <walkerrunpdx@gmail.com>
This commit is contained in:
Dimitris - Rafail Katsampas
2022-01-23 19:49:19 +02:00
committed by Nathan Walker
parent 9b5d125f42
commit 608bb1ed24
5 changed files with 125 additions and 168 deletions

View File

@@ -73,7 +73,7 @@ export function test_ReadIterations() {
let animation = createAnimationFromCSS('.test { animation-iteration-count: 5; }', 'test'); let animation = createAnimationFromCSS('.test { animation-iteration-count: 5; }', 'test');
TKUnit.assertEqual(animation.iterations, 5); TKUnit.assertEqual(animation.iterations, 5);
animation = createAnimationFromCSS('.test { animation-iteration-count: infinite; }', 'test'); animation = createAnimationFromCSS('.test { animation-iteration-count: infinite; }', 'test');
TKUnit.assertEqual(animation.iterations, Number.MAX_VALUE); TKUnit.assertEqual(animation.iterations, Number.POSITIVE_INFINITY);
} }
export function test_ReadFillMode() { export function test_ReadFillMode() {

View File

@@ -27,16 +27,6 @@ const easeInOut = lazy(() => new android.view.animation.AccelerateDecelerateInte
const linear = lazy(() => new android.view.animation.LinearInterpolator()); const linear = lazy(() => new android.view.animation.LinearInterpolator());
const bounce = lazy(() => new android.view.animation.BounceInterpolator()); const bounce = lazy(() => new android.view.animation.BounceInterpolator());
const keyPrefix = 'ui.animation.';
const propertyKeys = {};
propertyKeys[Properties.backgroundColor] = Symbol(keyPrefix + Properties.backgroundColor);
propertyKeys[Properties.opacity] = Symbol(keyPrefix + Properties.opacity);
propertyKeys[Properties.rotate] = Symbol(keyPrefix + Properties.rotate);
propertyKeys[Properties.scale] = Symbol(keyPrefix + Properties.scale);
propertyKeys[Properties.translate] = Symbol(keyPrefix + Properties.translate);
propertyKeys[Properties.height] = Symbol(keyPrefix + Properties.height);
propertyKeys[Properties.width] = Symbol(keyPrefix + Properties.width);
export function _resolveAnimationCurve(curve: string | CubicBezierAnimationCurve | android.view.animation.Interpolator | android.view.animation.LinearInterpolator): android.view.animation.Interpolator { export function _resolveAnimationCurve(curve: string | CubicBezierAnimationCurve | android.view.animation.Interpolator | android.view.animation.LinearInterpolator): android.view.animation.Interpolator {
switch (curve) { switch (curve) {
case 'easeIn': case 'easeIn':
@@ -178,10 +168,9 @@ export class Animation extends AnimationBase {
return this._rejectAlreadyPlaying(); return this._rejectAlreadyPlaying();
} }
if (this._animatorSet) { const animationFinishedPromise = super.play();
return this._play();
}
if (!this._animatorSet) {
this._animators = new Array<android.animation.Animator>(); this._animators = new Array<android.animation.Animator>();
this._propertyUpdateCallbacks = new Array<Function>(); this._propertyUpdateCallbacks = new Array<Function>();
this._propertyResetCallbacks = new Array<Function>(); this._propertyResetCallbacks = new Array<Function>();
@@ -197,8 +186,9 @@ export class Animation extends AnimationBase {
this._animatorSet = new android.animation.AnimatorSet(); this._animatorSet = new android.animation.AnimatorSet();
this._animatorSet.addListener(this._animatorListener); this._animatorSet.addListener(this._animatorListener);
}
return this._play(); this._play();
return animationFinishedPromise;
} }
public cancel(): void { public cancel(): void {
@@ -217,9 +207,7 @@ export class Animation extends AnimationBase {
return _resolveAnimationCurve(curve); return _resolveAnimationCurve(curve);
} }
private _play(): AnimationPromise { private _play(): void {
const animationFinishedPromise = super.play();
if (Device.sdkVersion <= '23') { if (Device.sdkVersion <= '23') {
this._animatorSet = new android.animation.AnimatorSet(); this._animatorSet = new android.animation.AnimatorSet();
this._animatorSet.addListener(this._animatorListener); this._animatorSet.addListener(this._animatorListener);
@@ -239,8 +227,6 @@ export class Animation extends AnimationBase {
this._animatorSet.setupStartValues(); this._animatorSet.setupStartValues();
this._animatorSet.start(); this._animatorSet.start();
return animationFinishedPromise;
} }
private _onAndroidAnimationEnd() { private _onAndroidAnimationEnd() {
@@ -300,19 +286,6 @@ export class Animation extends AnimationBase {
let originalValue3; let originalValue3;
const density = layout.getDisplayDensity(); const density = layout.getDisplayDensity();
const key = propertyKeys[propertyAnimation.property];
if (key) {
propertyAnimation.target[key] = propertyAnimation;
}
function checkAnimation(cb) {
return () => {
if (propertyAnimation.target[key] === propertyAnimation) {
delete propertyAnimation.target[key];
cb();
}
};
}
const setLocal = this._valueSource === 'animation'; const setLocal = this._valueSource === 'animation';
const style = propertyAnimation.target.style; const style = propertyAnimation.target.style;
switch (propertyAnimation.property) { switch (propertyAnimation.property) {
@@ -320,13 +293,10 @@ export class Animation extends AnimationBase {
opacityProperty._initDefaultNativeValue(style); opacityProperty._initDefaultNativeValue(style);
originalValue1 = nativeView.getAlpha(); originalValue1 = nativeView.getAlpha();
propertyUpdateCallbacks.push( propertyUpdateCallbacks.push(() => {
checkAnimation(() => {
propertyAnimation.target.style[setLocal ? opacityProperty.name : opacityProperty.keyframe] = propertyAnimation.value; propertyAnimation.target.style[setLocal ? opacityProperty.name : opacityProperty.keyframe] = propertyAnimation.value;
}) });
); propertyResetCallbacks.push(() => {
propertyResetCallbacks.push(
checkAnimation(() => {
if (setLocal) { if (setLocal) {
propertyAnimation.target.style[opacityProperty.name] = originalValue1; propertyAnimation.target.style[opacityProperty.name] = originalValue1;
} else { } else {
@@ -335,8 +305,7 @@ export class Animation extends AnimationBase {
if (propertyAnimation.target.nativeViewProtected) { if (propertyAnimation.target.nativeViewProtected) {
propertyAnimation.target[opacityProperty.setNative](propertyAnimation.target.style.opacity); propertyAnimation.target[opacityProperty.setNative](propertyAnimation.target.style.opacity);
} }
}) });
);
animators.push(createObjectAnimator(nativeView, 'alpha', propertyAnimation.value)); animators.push(createObjectAnimator(nativeView, 'alpha', propertyAnimation.value));
break; break;
@@ -358,13 +327,10 @@ export class Animation extends AnimationBase {
}) })
); );
propertyUpdateCallbacks.push( propertyUpdateCallbacks.push(() => {
checkAnimation(() => {
propertyAnimation.target.style[setLocal ? backgroundColorProperty.name : backgroundColorProperty.keyframe] = propertyAnimation.value; propertyAnimation.target.style[setLocal ? backgroundColorProperty.name : backgroundColorProperty.keyframe] = propertyAnimation.value;
}) });
); propertyResetCallbacks.push(() => {
propertyResetCallbacks.push(
checkAnimation(() => {
if (setLocal) { if (setLocal) {
propertyAnimation.target.style[backgroundColorProperty.name] = originalValue1; propertyAnimation.target.style[backgroundColorProperty.name] = originalValue1;
} else { } else {
@@ -374,8 +340,7 @@ export class Animation extends AnimationBase {
if (propertyAnimation.target.nativeViewProtected && propertyAnimation.target[backgroundColorProperty.setNative]) { if (propertyAnimation.target.nativeViewProtected && propertyAnimation.target[backgroundColorProperty.setNative]) {
propertyAnimation.target[backgroundColorProperty.setNative](propertyAnimation.target.style.backgroundColor); propertyAnimation.target[backgroundColorProperty.setNative](propertyAnimation.target.style.backgroundColor);
} }
}) });
);
animators.push(animator); animators.push(animator);
break; break;
} }
@@ -386,15 +351,12 @@ export class Animation extends AnimationBase {
originalValue1 = nativeView.getTranslationX() / density; originalValue1 = nativeView.getTranslationX() / density;
originalValue2 = nativeView.getTranslationY() / density; originalValue2 = nativeView.getTranslationY() / density;
propertyUpdateCallbacks.push( propertyUpdateCallbacks.push(() => {
checkAnimation(() => {
propertyAnimation.target.style[setLocal ? translateXProperty.name : translateXProperty.keyframe] = propertyAnimation.value.x; propertyAnimation.target.style[setLocal ? translateXProperty.name : translateXProperty.keyframe] = propertyAnimation.value.x;
propertyAnimation.target.style[setLocal ? translateYProperty.name : translateYProperty.keyframe] = propertyAnimation.value.y; propertyAnimation.target.style[setLocal ? translateYProperty.name : translateYProperty.keyframe] = propertyAnimation.value.y;
}) });
);
propertyResetCallbacks.push( propertyResetCallbacks.push(() => {
checkAnimation(() => {
if (setLocal) { if (setLocal) {
propertyAnimation.target.style[translateXProperty.name] = originalValue1; propertyAnimation.target.style[translateXProperty.name] = originalValue1;
propertyAnimation.target.style[translateYProperty.name] = originalValue2; propertyAnimation.target.style[translateYProperty.name] = originalValue2;
@@ -407,8 +369,7 @@ export class Animation extends AnimationBase {
propertyAnimation.target[translateXProperty.setNative](propertyAnimation.target.style.translateX); propertyAnimation.target[translateXProperty.setNative](propertyAnimation.target.style.translateX);
propertyAnimation.target[translateYProperty.setNative](propertyAnimation.target.style.translateY); propertyAnimation.target[translateYProperty.setNative](propertyAnimation.target.style.translateY);
} }
}) });
);
animators.push(createAnimationSet([createObjectAnimator(nativeView, 'translationX', propertyAnimation.value.x * density), createObjectAnimator(nativeView, 'translationY', propertyAnimation.value.y * density)], propertyAnimation.iterations)); animators.push(createAnimationSet([createObjectAnimator(nativeView, 'translationX', propertyAnimation.value.x * density), createObjectAnimator(nativeView, 'translationY', propertyAnimation.value.y * density)], propertyAnimation.iterations));
break; break;
@@ -420,15 +381,12 @@ export class Animation extends AnimationBase {
originalValue1 = nativeView.getScaleX(); originalValue1 = nativeView.getScaleX();
originalValue2 = nativeView.getScaleY(); originalValue2 = nativeView.getScaleY();
propertyUpdateCallbacks.push( propertyUpdateCallbacks.push(() => {
checkAnimation(() => {
propertyAnimation.target.style[setLocal ? scaleXProperty.name : scaleXProperty.keyframe] = propertyAnimation.value.x; propertyAnimation.target.style[setLocal ? scaleXProperty.name : scaleXProperty.keyframe] = propertyAnimation.value.x;
propertyAnimation.target.style[setLocal ? scaleYProperty.name : scaleYProperty.keyframe] = propertyAnimation.value.y; propertyAnimation.target.style[setLocal ? scaleYProperty.name : scaleYProperty.keyframe] = propertyAnimation.value.y;
}) });
);
propertyResetCallbacks.push( propertyResetCallbacks.push(() => {
checkAnimation(() => {
if (setLocal) { if (setLocal) {
propertyAnimation.target.style[scaleXProperty.name] = originalValue1; propertyAnimation.target.style[scaleXProperty.name] = originalValue1;
propertyAnimation.target.style[scaleYProperty.name] = originalValue2; propertyAnimation.target.style[scaleYProperty.name] = originalValue2;
@@ -441,8 +399,7 @@ export class Animation extends AnimationBase {
propertyAnimation.target[scaleXProperty.setNative](propertyAnimation.target.style.scaleX); propertyAnimation.target[scaleXProperty.setNative](propertyAnimation.target.style.scaleX);
propertyAnimation.target[scaleYProperty.setNative](propertyAnimation.target.style.scaleY); propertyAnimation.target[scaleYProperty.setNative](propertyAnimation.target.style.scaleY);
} }
}) });
);
animators.push(createAnimationSet([createObjectAnimator(nativeView, 'scaleX', propertyAnimation.value.x), createObjectAnimator(nativeView, 'scaleY', propertyAnimation.value.y)], propertyAnimation.iterations)); animators.push(createAnimationSet([createObjectAnimator(nativeView, 'scaleX', propertyAnimation.value.x), createObjectAnimator(nativeView, 'scaleY', propertyAnimation.value.y)], propertyAnimation.iterations));
break; break;
@@ -456,15 +413,12 @@ export class Animation extends AnimationBase {
originalValue2 = nativeView.getRotationY(); originalValue2 = nativeView.getRotationY();
originalValue3 = nativeView.getRotation(); originalValue3 = nativeView.getRotation();
propertyUpdateCallbacks.push( propertyUpdateCallbacks.push(() => {
checkAnimation(() => {
propertyAnimation.target.style[setLocal ? rotateXProperty.name : rotateXProperty.keyframe] = propertyAnimation.value.x; propertyAnimation.target.style[setLocal ? rotateXProperty.name : rotateXProperty.keyframe] = propertyAnimation.value.x;
propertyAnimation.target.style[setLocal ? rotateYProperty.name : rotateYProperty.keyframe] = propertyAnimation.value.y; propertyAnimation.target.style[setLocal ? rotateYProperty.name : rotateYProperty.keyframe] = propertyAnimation.value.y;
propertyAnimation.target.style[setLocal ? rotateProperty.name : rotateProperty.keyframe] = propertyAnimation.value.z; propertyAnimation.target.style[setLocal ? rotateProperty.name : rotateProperty.keyframe] = propertyAnimation.value.z;
}) });
); propertyResetCallbacks.push(() => {
propertyResetCallbacks.push(
checkAnimation(() => {
if (setLocal) { if (setLocal) {
propertyAnimation.target.style[rotateXProperty.name] = originalValue1; propertyAnimation.target.style[rotateXProperty.name] = originalValue1;
propertyAnimation.target.style[rotateYProperty.name] = originalValue2; propertyAnimation.target.style[rotateYProperty.name] = originalValue2;
@@ -480,8 +434,7 @@ export class Animation extends AnimationBase {
propertyAnimation.target[rotateXProperty.setNative](propertyAnimation.target.style.rotateX); propertyAnimation.target[rotateXProperty.setNative](propertyAnimation.target.style.rotateX);
propertyAnimation.target[rotateYProperty.setNative](propertyAnimation.target.style.rotateY); propertyAnimation.target[rotateYProperty.setNative](propertyAnimation.target.style.rotateY);
} }
}) });
);
animators.push(createAnimationSet([createObjectAnimator(nativeView, 'rotationX', propertyAnimation.value.x), createObjectAnimator(nativeView, 'rotationY', propertyAnimation.value.y), createObjectAnimator(nativeView, 'rotation', propertyAnimation.value.z)], propertyAnimation.iterations)); animators.push(createAnimationSet([createObjectAnimator(nativeView, 'rotationX', propertyAnimation.value.x), createObjectAnimator(nativeView, 'rotationY', propertyAnimation.value.y), createObjectAnimator(nativeView, 'rotation', propertyAnimation.value.z)], propertyAnimation.iterations));
break; break;
@@ -514,20 +467,16 @@ export class Animation extends AnimationBase {
}, },
}) })
); );
propertyUpdateCallbacks.push( propertyUpdateCallbacks.push(() => {
checkAnimation(() => {
propertyAnimation.target.style[targetStyle] = propertyAnimation.value; propertyAnimation.target.style[targetStyle] = propertyAnimation.value;
}) });
); propertyResetCallbacks.push(() => {
propertyResetCallbacks.push(
checkAnimation(() => {
propertyAnimation.target.style[targetStyle] = originalValue1; propertyAnimation.target.style[targetStyle] = originalValue1;
if (propertyAnimation.target.nativeViewProtected) { if (propertyAnimation.target.nativeViewProtected) {
const setter = propertyAnimation.target[extentProperty.setNative]; const setter = propertyAnimation.target[extentProperty.setNative];
setter(propertyAnimation.target.style[propertyAnimation.property]); setter(propertyAnimation.target.style[propertyAnimation.property]);
} }
}) });
);
animators.push(extentAnimator); animators.push(extentAnimator);
break; break;
} }

View File

@@ -464,10 +464,19 @@ export class Animation extends AnimationBase {
private static _createGroupAnimation(args: AnimationInfo, animation: PropertyAnimation) { private static _createGroupAnimation(args: AnimationInfo, animation: PropertyAnimation) {
const groupAnimation = CAAnimationGroup.new(); const groupAnimation = CAAnimationGroup.new();
groupAnimation.duration = args.duration; groupAnimation.duration = args.duration;
if (args.repeatCount !== undefined) {
groupAnimation.repeatCount = args.repeatCount;
}
if (args.delay !== undefined) {
groupAnimation.beginTime = CACurrentMediaTime() + args.delay;
}
if (animation.curve !== undefined) {
groupAnimation.timingFunction = animation.curve;
}
const animations = NSMutableArray.alloc<CAAnimation>().initWithCapacity(3); const animations = NSMutableArray.alloc<CAAnimation>().initWithCapacity(3);
args.subPropertiesToAnimate.forEach((property) => { args.subPropertiesToAnimate.forEach((property) => {
const basicAnimationArgs = { ...args }; const basicAnimationArgs = { ...args, duration: undefined, repeatCount: undefined, delay: undefined, curve: undefined };
basicAnimationArgs.propertyNameToAnimate = `${args.propertyNameToAnimate}.${property}`; basicAnimationArgs.propertyNameToAnimate = `${args.propertyNameToAnimate}.${property}`;
basicAnimationArgs.fromValue = args.fromValue[property]; basicAnimationArgs.fromValue = args.fromValue[property];
basicAnimationArgs.toValue = args.toValue[property]; basicAnimationArgs.toValue = args.toValue[property];

View File

@@ -228,8 +228,7 @@ export class KeyframeAnimation {
if (cachedAnimation) { if (cachedAnimation) {
animation = cachedAnimation; animation = cachedAnimation;
} else { } else {
const animationDef = this.animations[index]; const animationDef = { ...this.animations[index], target: view };
(<any>animationDef).target = view;
animation = new Animation([animationDef]); animation = new Animation([animationDef]);
this._nativeAnimations.push(animation); this._nativeAnimations.push(animation);
} }

View File

@@ -10,7 +10,7 @@ const ANIMATION_PROPERTY_HANDLERS = Object.freeze({
'animation-duration': (info: any, value: any) => (info.duration = timeConverter(value)), 'animation-duration': (info: any, value: any) => (info.duration = timeConverter(value)),
'animation-delay': (info: any, value: any) => (info.delay = timeConverter(value)), 'animation-delay': (info: any, value: any) => (info.delay = timeConverter(value)),
'animation-timing-function': (info: any, value: any) => (info.curve = animationTimingFunctionConverter(value)), 'animation-timing-function': (info: any, value: any) => (info.curve = animationTimingFunctionConverter(value)),
'animation-iteration-count': (info: any, value: any) => (info.iterations = value === 'infinite' ? Number.MAX_VALUE : parseFloat(value)), 'animation-iteration-count': (info: any, value: any) => (info.iterations = value === 'infinite' ? Number.POSITIVE_INFINITY : parseFloat(value)),
'animation-direction': (info: any, value: any) => (info.isReverse = value === 'reverse'), 'animation-direction': (info: any, value: any) => (info.isReverse = value === 'reverse'),
'animation-fill-mode': (info: any, value: any) => (info.isForwards = value === 'forwards'), 'animation-fill-mode': (info: any, value: any) => (info.isForwards = value === 'forwards'),
}); });