diff --git a/apps/automated/src/ui/animation/css-animation-tests.ts b/apps/automated/src/ui/animation/css-animation-tests.ts index 49e581257..ae4206ff8 100644 --- a/apps/automated/src/ui/animation/css-animation-tests.ts +++ b/apps/automated/src/ui/animation/css-animation-tests.ts @@ -73,7 +73,7 @@ export function test_ReadIterations() { let animation = createAnimationFromCSS('.test { animation-iteration-count: 5; }', 'test'); TKUnit.assertEqual(animation.iterations, 5); 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() { diff --git a/packages/core/ui/animation/index.android.ts b/packages/core/ui/animation/index.android.ts index b2aad158d..30332c62d 100644 --- a/packages/core/ui/animation/index.android.ts +++ b/packages/core/ui/animation/index.android.ts @@ -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(); + this._propertyUpdateCallbacks = new Array(); + this._propertyResetCallbacks = new Array(); + + 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(); - this._propertyUpdateCallbacks = new Array(); - this._propertyResetCallbacks = new Array(); - - 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; } diff --git a/packages/core/ui/animation/index.ios.ts b/packages/core/ui/animation/index.ios.ts index 95714bf44..9838b7054 100644 --- a/packages/core/ui/animation/index.ios.ts +++ b/packages/core/ui/animation/index.ios.ts @@ -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().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]; diff --git a/packages/core/ui/animation/keyframe-animation.ts b/packages/core/ui/animation/keyframe-animation.ts index c5df0dd5f..188bf0a64 100644 --- a/packages/core/ui/animation/keyframe-animation.ts +++ b/packages/core/ui/animation/keyframe-animation.ts @@ -228,8 +228,7 @@ export class KeyframeAnimation { if (cachedAnimation) { animation = cachedAnimation; } else { - const animationDef = this.animations[index]; - (animationDef).target = view; + const animationDef = { ...this.animations[index], target: view }; animation = new Animation([animationDef]); this._nativeAnimations.push(animation); } diff --git a/packages/core/ui/styling/css-animation-parser.ts b/packages/core/ui/styling/css-animation-parser.ts index 5674f3269..6e540171e 100644 --- a/packages/core/ui/styling/css-animation-parser.ts +++ b/packages/core/ui/styling/css-animation-parser.ts @@ -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'), });