diff --git a/apps/automated/src/ui/animation/animation-tests.ts b/apps/automated/src/ui/animation/animation-tests.ts index ad75839ce4..7a96d2a7a5 100644 --- a/apps/automated/src/ui/animation/animation-tests.ts +++ b/apps/automated/src/ui/animation/animation-tests.ts @@ -79,7 +79,7 @@ export function test_PlayRejectsWhenAlreadyPlayingAnimation(done) { if (e === 'Animation is already playing.') { done(); } - } + }, ); } @@ -164,8 +164,8 @@ export function test_ChainingAnimations(done) { .then(() => label.animate({ translate: { x: 0, y: 0 }, duration: duration })) .then(() => label.animate({ scale: { x: 5, y: 5 }, duration: duration })) .then(() => label.animate({ scale: { x: 1, y: 1 }, duration: duration })) - .then(() => label.animate({ rotate: 180, duration: duration })) - .then(() => label.animate({ rotate: 0, duration: duration })) + .then(() => label.animate({ rotate: { x: 90, y: 0, z: 180 }, duration: duration })) + .then(() => label.animate({ rotate: { x: 0, y: 0, z: 0 }, duration: duration })) .then(() => { //console.log("Animation finished"); // >> (hide) @@ -610,7 +610,7 @@ export function test_PlayPromiseIsResolvedWhenAnimationFinishes(done) { function onRejected(e) { TKUnit.assert(false, 'Animation play promise should be resolved, not rejected.'); done(e); - } + }, ); } @@ -627,7 +627,7 @@ export function test_PlayPromiseIsRejectedWhenAnimationIsCancelled(done) { function onRejected(e) { TKUnit.assert(animation.isPlaying === false, 'Animation.isPlaying should be false when animation play promise is rejected.'); done(); - } + }, ); animation.cancel(); diff --git a/packages/core/ui/animation/index.ios.ts b/packages/core/ui/animation/index.ios.ts index 893ceadf9d..fcc09327c4 100644 --- a/packages/core/ui/animation/index.ios.ts +++ b/packages/core/ui/animation/index.ios.ts @@ -79,8 +79,8 @@ class AnimationDelegateImpl extends NSObject implements CAAnimationDelegate { targetStyle[setLocal ? widthProperty.name : widthProperty.keyframe] = value; break; case Properties.scale: - targetStyle[setLocal ? scaleXProperty.name : scaleXProperty.keyframe] = value.x === 0 ? 0.001 : value.x; - targetStyle[setLocal ? scaleYProperty.name : scaleYProperty.keyframe] = value.y === 0 ? 0.001 : value.y; + targetStyle[setLocal ? scaleXProperty.name : scaleXProperty.keyframe] = value.x === 0 ? 1e-6 : value.x; + targetStyle[setLocal ? scaleYProperty.name : scaleYProperty.keyframe] = value.y === 0 ? 1e-6 : value.y; break; case _transform: if (value[Properties.translate] !== undefined) { @@ -95,8 +95,8 @@ class AnimationDelegateImpl extends NSObject implements CAAnimationDelegate { if (value[Properties.scale] !== undefined) { const x = value[Properties.scale].x; const y = value[Properties.scale].y; - targetStyle[setLocal ? scaleXProperty.name : scaleXProperty.keyframe] = x === 0 ? 0.001 : x; - targetStyle[setLocal ? scaleYProperty.name : scaleYProperty.keyframe] = y === 0 ? 0.001 : y; + targetStyle[setLocal ? scaleXProperty.name : scaleXProperty.keyframe] = x === 0 ? 1e-6 : x; + targetStyle[setLocal ? scaleYProperty.name : scaleYProperty.keyframe] = y === 0 ? 1e-6 : y; } break; } @@ -309,7 +309,6 @@ export class Animation extends AnimationBase { const parent = view.parent as View; let propertyNameToAnimate = animation.property; - let subPropertyNameToAnimate; let toValue = animation.value; let fromValue; if (nativeView) { @@ -347,30 +346,9 @@ export class Animation extends AnimationBase { style[setLocal ? rotateYProperty.name : rotateYProperty.keyframe] = value.y; }; - propertyNameToAnimate = 'transform.rotation'; - subPropertyNameToAnimate = ['x', 'y', 'z']; - fromValue = { - x: nativeView.layer.valueForKeyPath('transform.rotation.x'), - y: nativeView.layer.valueForKeyPath('transform.rotation.y'), - z: nativeView.layer.valueForKeyPath('transform.rotation.z'), - }; - - if (animation.target.rotateX !== undefined && animation.target.rotateX !== 0 && Math.floor(toValue / 360) - toValue / 360 === 0) { - fromValue.x = (animation.target.rotateX * Math.PI) / 180; - } - if (animation.target.rotateY !== undefined && animation.target.rotateY !== 0 && Math.floor(toValue / 360) - toValue / 360 === 0) { - fromValue.y = (animation.target.rotateY * Math.PI) / 180; - } - if (animation.target.rotate !== undefined && animation.target.rotate !== 0 && Math.floor(toValue / 360) - toValue / 360 === 0) { - fromValue.z = (animation.target.rotate * Math.PI) / 180; - } - - // Respect only value.z for back-compat until 3D rotations are implemented - toValue = { - x: (toValue.x * Math.PI) / 180, - y: (toValue.y * Math.PI) / 180, - z: (toValue.z * Math.PI) / 180, - }; + propertyNameToAnimate = 'transform'; + fromValue = NSValue.valueWithCATransform3D(nativeView.layer.transform); + toValue = NSValue.valueWithCATransform3D(iosHelper.applyRotateTransform(nativeView.layer.transform, toValue.x, toValue.y, toValue.z)); break; case Properties.translate: animation._originalValue = { @@ -387,10 +365,10 @@ export class Animation extends AnimationBase { break; case Properties.scale: if (toValue.x === 0) { - toValue.x = 0.001; + toValue.x = 1e-6; } if (toValue.y === 0) { - toValue.y = 0.001; + toValue.y = 1e-6; } animation._originalValue = { x: view.scaleX, y: view.scaleY }; animation._propertyResetCallback = (value, valueSource) => { @@ -473,7 +451,6 @@ export class Animation extends AnimationBase { return { propertyNameToAnimate: propertyNameToAnimate, fromValue: fromValue, - subPropertiesToAnimate: subPropertyNameToAnimate, toValue: toValue, duration: duration, repeatCount: repeatCount, @@ -517,8 +494,10 @@ export class Animation extends AnimationBase { } private static _createGroupAnimation(args: AnimationInfo, animation: PropertyAnimation) { + const animations = NSMutableArray.alloc().initWithCapacity(args.subPropertiesToAnimate.length); const groupAnimation = CAAnimationGroup.new(); groupAnimation.duration = args.duration; + if (args.repeatCount !== undefined) { groupAnimation.repeatCount = args.repeatCount; } @@ -528,7 +507,6 @@ export class Animation extends AnimationBase { if (animation.curve !== undefined) { groupAnimation.timingFunction = animation.curve; } - const animations = NSMutableArray.alloc().initWithCapacity(3); args.subPropertiesToAnimate.forEach((property) => { const basicAnimationArgs = { ...args, duration: undefined, repeatCount: undefined, delay: undefined, curve: undefined }; @@ -671,16 +649,30 @@ export class Animation extends AnimationBase { } if (value[Properties.scale] !== undefined) { - const x = value[Properties.scale].x; - const y = value[Properties.scale].y; - result = CATransform3DScale(result, x === 0 ? 0.001 : x, y === 0 ? 0.001 : y, 1); + const x = value[Properties.scale].x || 1e-6; + const y = value[Properties.scale].y || 1e-6; + result = CATransform3DScale(result, x, y, 1); + } + + if (value[Properties.rotate] !== undefined) { + const x = value[Properties.rotate].x; + const y = value[Properties.rotate].y; + const z = value[Properties.rotate].z; + const perspective = animation.target.perspective || 300; + + // Set perspective in case of rotation since we use z + if (x || y) { + result.m34 = -1 / perspective; + } + + result = iosHelper.applyRotateTransform(result, x, y, z); } return result; } private static _isAffineTransform(property: string): boolean { - return property === _transform || property === Properties.translate || property === Properties.scale; + return property === _transform || property === Properties.translate || property === Properties.scale || property === Properties.rotate; } private static _canBeMerged(animation1: PropertyAnimation, animation2: PropertyAnimation) { @@ -947,7 +939,8 @@ function calculateTransform(view: View): CATransform3D { // Order is important: translate, rotate, scale let expectedTransform = new CATransform3D(CATransform3DIdentity); - // Only set perspective if there is 3D rotation + // TODO: Add perspective property to transform animations (not just rotation) + // Set perspective in case of rotation since we use z if (view.rotateX || view.rotateY) { expectedTransform.m34 = -1 / perspective; }