mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-11-05 13:26:48 +08:00
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:
committed by
Nathan Walker
parent
9b5d125f42
commit
608bb1ed24
@@ -27,16 +27,6 @@ const easeInOut = lazy(() => new android.view.animation.AccelerateDecelerateInte
|
||||
const linear = lazy(() => new android.view.animation.LinearInterpolator());
|
||||
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 {
|
||||
switch (curve) {
|
||||
case 'easeIn':
|
||||
@@ -178,27 +168,27 @@ export class Animation extends AnimationBase {
|
||||
return this._rejectAlreadyPlaying();
|
||||
}
|
||||
|
||||
if (this._animatorSet) {
|
||||
return this._play();
|
||||
const animationFinishedPromise = super.play();
|
||||
|
||||
if (!this._animatorSet) {
|
||||
this._animators = new Array<android.animation.Animator>();
|
||||
this._propertyUpdateCallbacks = new Array<Function>();
|
||||
this._propertyResetCallbacks = new Array<Function>();
|
||||
|
||||
for (let i = 0, length = this._propertyAnimations.length; i < length; i++) {
|
||||
this._createAnimators(this._propertyAnimations[i]);
|
||||
}
|
||||
|
||||
this._nativeAnimatorsArray = Array.create(android.animation.Animator, this._animators.length);
|
||||
for (let i = 0, length = this._animators.length; i < length; i++) {
|
||||
this._nativeAnimatorsArray[i] = this._animators[i];
|
||||
}
|
||||
|
||||
this._animatorSet = new android.animation.AnimatorSet();
|
||||
this._animatorSet.addListener(this._animatorListener);
|
||||
}
|
||||
|
||||
this._animators = new Array<android.animation.Animator>();
|
||||
this._propertyUpdateCallbacks = new Array<Function>();
|
||||
this._propertyResetCallbacks = new Array<Function>();
|
||||
|
||||
for (let i = 0, length = this._propertyAnimations.length; i < length; i++) {
|
||||
this._createAnimators(this._propertyAnimations[i]);
|
||||
}
|
||||
|
||||
this._nativeAnimatorsArray = Array.create(android.animation.Animator, this._animators.length);
|
||||
for (let i = 0, length = this._animators.length; i < length; i++) {
|
||||
this._nativeAnimatorsArray[i] = this._animators[i];
|
||||
}
|
||||
|
||||
this._animatorSet = new android.animation.AnimatorSet();
|
||||
this._animatorSet.addListener(this._animatorListener);
|
||||
|
||||
return this._play();
|
||||
this._play();
|
||||
return animationFinishedPromise;
|
||||
}
|
||||
|
||||
public cancel(): void {
|
||||
@@ -217,9 +207,7 @@ export class Animation extends AnimationBase {
|
||||
return _resolveAnimationCurve(curve);
|
||||
}
|
||||
|
||||
private _play(): AnimationPromise {
|
||||
const animationFinishedPromise = super.play();
|
||||
|
||||
private _play(): void {
|
||||
if (Device.sdkVersion <= '23') {
|
||||
this._animatorSet = new android.animation.AnimatorSet();
|
||||
this._animatorSet.addListener(this._animatorListener);
|
||||
@@ -239,8 +227,6 @@ export class Animation extends AnimationBase {
|
||||
|
||||
this._animatorSet.setupStartValues();
|
||||
this._animatorSet.start();
|
||||
|
||||
return animationFinishedPromise;
|
||||
}
|
||||
|
||||
private _onAndroidAnimationEnd() {
|
||||
@@ -300,19 +286,6 @@ export class Animation extends AnimationBase {
|
||||
let originalValue3;
|
||||
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 style = propertyAnimation.target.style;
|
||||
switch (propertyAnimation.property) {
|
||||
@@ -320,23 +293,19 @@ export class Animation extends AnimationBase {
|
||||
opacityProperty._initDefaultNativeValue(style);
|
||||
|
||||
originalValue1 = nativeView.getAlpha();
|
||||
propertyUpdateCallbacks.push(
|
||||
checkAnimation(() => {
|
||||
propertyAnimation.target.style[setLocal ? opacityProperty.name : opacityProperty.keyframe] = propertyAnimation.value;
|
||||
})
|
||||
);
|
||||
propertyResetCallbacks.push(
|
||||
checkAnimation(() => {
|
||||
if (setLocal) {
|
||||
propertyAnimation.target.style[opacityProperty.name] = originalValue1;
|
||||
} else {
|
||||
propertyAnimation.target.style[opacityProperty.keyframe] = originalValue1;
|
||||
}
|
||||
if (propertyAnimation.target.nativeViewProtected) {
|
||||
propertyAnimation.target[opacityProperty.setNative](propertyAnimation.target.style.opacity);
|
||||
}
|
||||
})
|
||||
);
|
||||
propertyUpdateCallbacks.push(() => {
|
||||
propertyAnimation.target.style[setLocal ? opacityProperty.name : opacityProperty.keyframe] = propertyAnimation.value;
|
||||
});
|
||||
propertyResetCallbacks.push(() => {
|
||||
if (setLocal) {
|
||||
propertyAnimation.target.style[opacityProperty.name] = originalValue1;
|
||||
} else {
|
||||
propertyAnimation.target.style[opacityProperty.keyframe] = originalValue1;
|
||||
}
|
||||
if (propertyAnimation.target.nativeViewProtected) {
|
||||
propertyAnimation.target[opacityProperty.setNative](propertyAnimation.target.style.opacity);
|
||||
}
|
||||
});
|
||||
animators.push(createObjectAnimator(nativeView, 'alpha', propertyAnimation.value));
|
||||
break;
|
||||
|
||||
@@ -358,24 +327,20 @@ export class Animation extends AnimationBase {
|
||||
})
|
||||
);
|
||||
|
||||
propertyUpdateCallbacks.push(
|
||||
checkAnimation(() => {
|
||||
propertyAnimation.target.style[setLocal ? backgroundColorProperty.name : backgroundColorProperty.keyframe] = propertyAnimation.value;
|
||||
})
|
||||
);
|
||||
propertyResetCallbacks.push(
|
||||
checkAnimation(() => {
|
||||
if (setLocal) {
|
||||
propertyAnimation.target.style[backgroundColorProperty.name] = originalValue1;
|
||||
} else {
|
||||
propertyAnimation.target.style[backgroundColorProperty.keyframe] = originalValue1;
|
||||
}
|
||||
propertyUpdateCallbacks.push(() => {
|
||||
propertyAnimation.target.style[setLocal ? backgroundColorProperty.name : backgroundColorProperty.keyframe] = propertyAnimation.value;
|
||||
});
|
||||
propertyResetCallbacks.push(() => {
|
||||
if (setLocal) {
|
||||
propertyAnimation.target.style[backgroundColorProperty.name] = originalValue1;
|
||||
} else {
|
||||
propertyAnimation.target.style[backgroundColorProperty.keyframe] = originalValue1;
|
||||
}
|
||||
|
||||
if (propertyAnimation.target.nativeViewProtected && propertyAnimation.target[backgroundColorProperty.setNative]) {
|
||||
propertyAnimation.target[backgroundColorProperty.setNative](propertyAnimation.target.style.backgroundColor);
|
||||
}
|
||||
})
|
||||
);
|
||||
if (propertyAnimation.target.nativeViewProtected && propertyAnimation.target[backgroundColorProperty.setNative]) {
|
||||
propertyAnimation.target[backgroundColorProperty.setNative](propertyAnimation.target.style.backgroundColor);
|
||||
}
|
||||
});
|
||||
animators.push(animator);
|
||||
break;
|
||||
}
|
||||
@@ -386,29 +351,25 @@ export class Animation extends AnimationBase {
|
||||
originalValue1 = nativeView.getTranslationX() / density;
|
||||
originalValue2 = nativeView.getTranslationY() / density;
|
||||
|
||||
propertyUpdateCallbacks.push(
|
||||
checkAnimation(() => {
|
||||
propertyAnimation.target.style[setLocal ? translateXProperty.name : translateXProperty.keyframe] = propertyAnimation.value.x;
|
||||
propertyAnimation.target.style[setLocal ? translateYProperty.name : translateYProperty.keyframe] = propertyAnimation.value.y;
|
||||
})
|
||||
);
|
||||
propertyUpdateCallbacks.push(() => {
|
||||
propertyAnimation.target.style[setLocal ? translateXProperty.name : translateXProperty.keyframe] = propertyAnimation.value.x;
|
||||
propertyAnimation.target.style[setLocal ? translateYProperty.name : translateYProperty.keyframe] = propertyAnimation.value.y;
|
||||
});
|
||||
|
||||
propertyResetCallbacks.push(
|
||||
checkAnimation(() => {
|
||||
if (setLocal) {
|
||||
propertyAnimation.target.style[translateXProperty.name] = originalValue1;
|
||||
propertyAnimation.target.style[translateYProperty.name] = originalValue2;
|
||||
} else {
|
||||
propertyAnimation.target.style[translateXProperty.keyframe] = originalValue1;
|
||||
propertyAnimation.target.style[translateYProperty.keyframe] = originalValue2;
|
||||
}
|
||||
propertyResetCallbacks.push(() => {
|
||||
if (setLocal) {
|
||||
propertyAnimation.target.style[translateXProperty.name] = originalValue1;
|
||||
propertyAnimation.target.style[translateYProperty.name] = originalValue2;
|
||||
} else {
|
||||
propertyAnimation.target.style[translateXProperty.keyframe] = originalValue1;
|
||||
propertyAnimation.target.style[translateYProperty.keyframe] = originalValue2;
|
||||
}
|
||||
|
||||
if (propertyAnimation.target.nativeViewProtected) {
|
||||
propertyAnimation.target[translateXProperty.setNative](propertyAnimation.target.style.translateX);
|
||||
propertyAnimation.target[translateYProperty.setNative](propertyAnimation.target.style.translateY);
|
||||
}
|
||||
})
|
||||
);
|
||||
if (propertyAnimation.target.nativeViewProtected) {
|
||||
propertyAnimation.target[translateXProperty.setNative](propertyAnimation.target.style.translateX);
|
||||
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));
|
||||
break;
|
||||
@@ -420,29 +381,25 @@ export class Animation extends AnimationBase {
|
||||
originalValue1 = nativeView.getScaleX();
|
||||
originalValue2 = nativeView.getScaleY();
|
||||
|
||||
propertyUpdateCallbacks.push(
|
||||
checkAnimation(() => {
|
||||
propertyAnimation.target.style[setLocal ? scaleXProperty.name : scaleXProperty.keyframe] = propertyAnimation.value.x;
|
||||
propertyAnimation.target.style[setLocal ? scaleYProperty.name : scaleYProperty.keyframe] = propertyAnimation.value.y;
|
||||
})
|
||||
);
|
||||
propertyUpdateCallbacks.push(() => {
|
||||
propertyAnimation.target.style[setLocal ? scaleXProperty.name : scaleXProperty.keyframe] = propertyAnimation.value.x;
|
||||
propertyAnimation.target.style[setLocal ? scaleYProperty.name : scaleYProperty.keyframe] = propertyAnimation.value.y;
|
||||
});
|
||||
|
||||
propertyResetCallbacks.push(
|
||||
checkAnimation(() => {
|
||||
if (setLocal) {
|
||||
propertyAnimation.target.style[scaleXProperty.name] = originalValue1;
|
||||
propertyAnimation.target.style[scaleYProperty.name] = originalValue2;
|
||||
} else {
|
||||
propertyAnimation.target.style[scaleXProperty.keyframe] = originalValue1;
|
||||
propertyAnimation.target.style[scaleYProperty.keyframe] = originalValue2;
|
||||
}
|
||||
propertyResetCallbacks.push(() => {
|
||||
if (setLocal) {
|
||||
propertyAnimation.target.style[scaleXProperty.name] = originalValue1;
|
||||
propertyAnimation.target.style[scaleYProperty.name] = originalValue2;
|
||||
} else {
|
||||
propertyAnimation.target.style[scaleXProperty.keyframe] = originalValue1;
|
||||
propertyAnimation.target.style[scaleYProperty.keyframe] = originalValue2;
|
||||
}
|
||||
|
||||
if (propertyAnimation.target.nativeViewProtected) {
|
||||
propertyAnimation.target[scaleXProperty.setNative](propertyAnimation.target.style.scaleX);
|
||||
propertyAnimation.target[scaleYProperty.setNative](propertyAnimation.target.style.scaleY);
|
||||
}
|
||||
})
|
||||
);
|
||||
if (propertyAnimation.target.nativeViewProtected) {
|
||||
propertyAnimation.target[scaleXProperty.setNative](propertyAnimation.target.style.scaleX);
|
||||
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));
|
||||
break;
|
||||
@@ -456,32 +413,28 @@ export class Animation extends AnimationBase {
|
||||
originalValue2 = nativeView.getRotationY();
|
||||
originalValue3 = nativeView.getRotation();
|
||||
|
||||
propertyUpdateCallbacks.push(
|
||||
checkAnimation(() => {
|
||||
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 ? rotateProperty.name : rotateProperty.keyframe] = propertyAnimation.value.z;
|
||||
})
|
||||
);
|
||||
propertyResetCallbacks.push(
|
||||
checkAnimation(() => {
|
||||
if (setLocal) {
|
||||
propertyAnimation.target.style[rotateXProperty.name] = originalValue1;
|
||||
propertyAnimation.target.style[rotateYProperty.name] = originalValue2;
|
||||
propertyAnimation.target.style[rotateProperty.name] = originalValue3;
|
||||
} else {
|
||||
propertyAnimation.target.style[rotateXProperty.keyframe] = originalValue1;
|
||||
propertyAnimation.target.style[rotateYProperty.keyframe] = originalValue2;
|
||||
propertyAnimation.target.style[rotateProperty.keyframe] = originalValue3;
|
||||
}
|
||||
propertyUpdateCallbacks.push(() => {
|
||||
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 ? rotateProperty.name : rotateProperty.keyframe] = propertyAnimation.value.z;
|
||||
});
|
||||
propertyResetCallbacks.push(() => {
|
||||
if (setLocal) {
|
||||
propertyAnimation.target.style[rotateXProperty.name] = originalValue1;
|
||||
propertyAnimation.target.style[rotateYProperty.name] = originalValue2;
|
||||
propertyAnimation.target.style[rotateProperty.name] = originalValue3;
|
||||
} else {
|
||||
propertyAnimation.target.style[rotateXProperty.keyframe] = originalValue1;
|
||||
propertyAnimation.target.style[rotateYProperty.keyframe] = originalValue2;
|
||||
propertyAnimation.target.style[rotateProperty.keyframe] = originalValue3;
|
||||
}
|
||||
|
||||
if (propertyAnimation.target.nativeViewProtected) {
|
||||
propertyAnimation.target[rotateProperty.setNative](propertyAnimation.target.style.rotate);
|
||||
propertyAnimation.target[rotateXProperty.setNative](propertyAnimation.target.style.rotateX);
|
||||
propertyAnimation.target[rotateYProperty.setNative](propertyAnimation.target.style.rotateY);
|
||||
}
|
||||
})
|
||||
);
|
||||
if (propertyAnimation.target.nativeViewProtected) {
|
||||
propertyAnimation.target[rotateProperty.setNative](propertyAnimation.target.style.rotate);
|
||||
propertyAnimation.target[rotateXProperty.setNative](propertyAnimation.target.style.rotateX);
|
||||
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));
|
||||
break;
|
||||
@@ -514,20 +467,16 @@ export class Animation extends AnimationBase {
|
||||
},
|
||||
})
|
||||
);
|
||||
propertyUpdateCallbacks.push(
|
||||
checkAnimation(() => {
|
||||
propertyAnimation.target.style[targetStyle] = propertyAnimation.value;
|
||||
})
|
||||
);
|
||||
propertyResetCallbacks.push(
|
||||
checkAnimation(() => {
|
||||
propertyAnimation.target.style[targetStyle] = originalValue1;
|
||||
if (propertyAnimation.target.nativeViewProtected) {
|
||||
const setter = propertyAnimation.target[extentProperty.setNative];
|
||||
setter(propertyAnimation.target.style[propertyAnimation.property]);
|
||||
}
|
||||
})
|
||||
);
|
||||
propertyUpdateCallbacks.push(() => {
|
||||
propertyAnimation.target.style[targetStyle] = propertyAnimation.value;
|
||||
});
|
||||
propertyResetCallbacks.push(() => {
|
||||
propertyAnimation.target.style[targetStyle] = originalValue1;
|
||||
if (propertyAnimation.target.nativeViewProtected) {
|
||||
const setter = propertyAnimation.target[extentProperty.setNative];
|
||||
setter(propertyAnimation.target.style[propertyAnimation.property]);
|
||||
}
|
||||
});
|
||||
animators.push(extentAnimator);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -464,10 +464,19 @@ export class Animation extends AnimationBase {
|
||||
private static _createGroupAnimation(args: AnimationInfo, animation: PropertyAnimation) {
|
||||
const groupAnimation = CAAnimationGroup.new();
|
||||
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);
|
||||
|
||||
args.subPropertiesToAnimate.forEach((property) => {
|
||||
const basicAnimationArgs = { ...args };
|
||||
const basicAnimationArgs = { ...args, duration: undefined, repeatCount: undefined, delay: undefined, curve: undefined };
|
||||
basicAnimationArgs.propertyNameToAnimate = `${args.propertyNameToAnimate}.${property}`;
|
||||
basicAnimationArgs.fromValue = args.fromValue[property];
|
||||
basicAnimationArgs.toValue = args.toValue[property];
|
||||
|
||||
@@ -228,8 +228,7 @@ export class KeyframeAnimation {
|
||||
if (cachedAnimation) {
|
||||
animation = cachedAnimation;
|
||||
} else {
|
||||
const animationDef = this.animations[index];
|
||||
(<any>animationDef).target = view;
|
||||
const animationDef = { ...this.animations[index], target: view };
|
||||
animation = new Animation([animationDef]);
|
||||
this._nativeAnimations.push(animation);
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ const ANIMATION_PROPERTY_HANDLERS = Object.freeze({
|
||||
'animation-duration': (info: any, value: any) => (info.duration = timeConverter(value)),
|
||||
'animation-delay': (info: any, value: any) => (info.delay = timeConverter(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-fill-mode': (info: any, value: any) => (info.isForwards = value === 'forwards'),
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user