@nativescript/core
Version:
A JavaScript library providing an easy to use api for interacting with iOS and Android platform APIs.
805 lines • 42.7 kB
JavaScript
// Requires
import { AnimationBase, Properties, CubicBezierAnimationCurve } from './animation-common';
import { Trace } from '../../trace';
import { opacityProperty, backgroundColorProperty, rotateProperty, rotateXProperty, rotateYProperty, translateXProperty, translateYProperty, scaleXProperty, scaleYProperty, heightProperty, widthProperty, PercentLength } from '../styling/style-properties';
import { ios as iosBackground } from '../styling/background';
import { ios as iosViewUtils } from '../utils';
import { ios as iosHelper } from '../../utils/native-helper';
import { Screen } from '../../platform';
export * from './animation-common';
export { KeyframeAnimation, KeyframeAnimationInfo, KeyframeDeclaration, KeyframeInfo } from './keyframe-animation';
const _transform = '_transform';
const _skip = '_skip';
const FLT_MAX = 340282346638528859811704183484516925440.0;
class AnimationInfo {
}
var AnimationDelegateImpl = /** @class */ (function (_super) {
__extends(AnimationDelegateImpl, _super);
function AnimationDelegateImpl() {
return _super !== null && _super.apply(this, arguments) || this;
}
AnimationDelegateImpl.initWithFinishedCallback = function (finishedCallback, propertyAnimation, valueSource) {
var delegate = AnimationDelegateImpl.new();
delegate._finishedCallback = finishedCallback;
delegate._propertyAnimation = propertyAnimation;
delegate._valueSource = valueSource;
return delegate;
};
AnimationDelegateImpl.prototype.animationDidStart = function (anim) {
var value = this._propertyAnimation.value;
var setLocal = this._valueSource === 'animation';
var targetStyle = this._propertyAnimation.target.style;
this._propertyAnimation.target._suspendPresentationLayerUpdates();
switch (this._propertyAnimation.property) {
case Properties.backgroundColor:
targetStyle[setLocal ? backgroundColorProperty.name : backgroundColorProperty.keyframe] = value;
break;
case Properties.opacity:
targetStyle[setLocal ? opacityProperty.name : opacityProperty.keyframe] = value;
break;
case Properties.rotate:
targetStyle[setLocal ? rotateXProperty.name : rotateXProperty.keyframe] = value.x;
targetStyle[setLocal ? rotateYProperty.name : rotateYProperty.keyframe] = value.y;
targetStyle[setLocal ? rotateProperty.name : rotateProperty.keyframe] = value.z;
break;
case Properties.translate:
targetStyle[setLocal ? translateXProperty.name : translateXProperty.keyframe] = value.x;
targetStyle[setLocal ? translateYProperty.name : translateYProperty.keyframe] = value.y;
break;
case Properties.height:
targetStyle[setLocal ? heightProperty.name : heightProperty.keyframe] = value;
break;
case Properties.width:
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;
break;
case _transform:
if (value[Properties.translate] !== undefined) {
targetStyle[setLocal ? translateXProperty.name : translateXProperty.keyframe] = value[Properties.translate].x;
targetStyle[setLocal ? translateYProperty.name : translateYProperty.keyframe] = value[Properties.translate].y;
}
if (value[Properties.rotate] !== undefined) {
targetStyle[setLocal ? rotateXProperty.name : rotateXProperty.keyframe] = value[Properties.rotate].x;
targetStyle[setLocal ? rotateYProperty.name : rotateYProperty.keyframe] = value[Properties.rotate].y;
targetStyle[setLocal ? rotateProperty.name : rotateProperty.keyframe] = value[Properties.rotate].z;
}
if (value[Properties.scale] !== undefined) {
var x = value[Properties.scale].x;
var 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;
}
break;
}
this._propertyAnimation.target._resumePresentationLayerUpdates();
};
AnimationDelegateImpl.prototype.animationDidStopFinished = function (anim, finished) {
if (this._finishedCallback) {
this._finishedCallback(!finished);
}
if (finished && this.nextAnimation) {
this.nextAnimation();
}
};
// The CAAnimationDelegate protocol has been introduced in the iOS 10 SDK
AnimationDelegateImpl.ObjCProtocols = global.CAAnimationDelegate ? [global.CAAnimationDelegate] : [];
return AnimationDelegateImpl;
}(NSObject));
export function _resolveAnimationCurve(curve) {
switch (curve) {
case 'easeIn':
return CAMediaTimingFunction.functionWithName(kCAMediaTimingFunctionEaseIn);
case 'easeOut':
return CAMediaTimingFunction.functionWithName(kCAMediaTimingFunctionEaseOut);
case 'easeInOut':
return CAMediaTimingFunction.functionWithName(kCAMediaTimingFunctionEaseInEaseOut);
case 'linear':
return CAMediaTimingFunction.functionWithName(kCAMediaTimingFunctionLinear);
case 'spring':
return curve;
case 'ease':
return CAMediaTimingFunction.functionWithControlPoints(0.25, 0.1, 0.25, 1.0);
default:
if (curve instanceof CAMediaTimingFunction) {
return curve;
}
else if (curve instanceof CubicBezierAnimationCurve) {
const animationCurve = curve;
return CAMediaTimingFunction.functionWithControlPoints(animationCurve.x1, animationCurve.y1, animationCurve.x2, animationCurve.y2);
}
else {
console.error(`Invalid animation curve: ${curve}`);
}
}
}
export class Animation extends AnimationBase {
constructor(animationDefinitions, playSequentially) {
super(animationDefinitions, playSequentially);
this._valueSource = 'animation';
if (animationDefinitions.length > 0 && animationDefinitions[0].valueSource !== undefined) {
this._valueSource = animationDefinitions[0].valueSource;
}
if (!playSequentially) {
if (Trace.isEnabled()) {
Trace.write('Non-merged Property Animations: ' + this._propertyAnimations.length, Trace.categories.Animation);
}
this._mergedPropertyAnimations = Animation._mergeAffineTransformAnimations(this._propertyAnimations);
if (Trace.isEnabled()) {
Trace.write('Merged Property Animations: ' + this._mergedPropertyAnimations.length, Trace.categories.Animation);
}
}
else {
this._mergedPropertyAnimations = this._propertyAnimations;
}
const animationFinishedCallback = (cancelled) => {
if (this._playSequentially) {
// This function will be called by the last animation when done or by another animation if the user cancels them halfway through.
if (!cancelled) {
this._resolveAnimationFinishedPromise();
}
}
else {
// This callback will be called by each INDIVIDUAL animation when it finishes or is cancelled.
if (cancelled) {
this._cancelledAnimations++;
}
else {
this._finishedAnimations++;
}
if (this._cancelledAnimations > 0 && this._cancelledAnimations + this._finishedAnimations === this._mergedPropertyAnimations.length) {
if (Trace.isEnabled()) {
Trace.write(this._cancelledAnimations + ' animations cancelled.', Trace.categories.Animation);
}
}
else if (this._finishedAnimations === this._mergedPropertyAnimations.length) {
if (Trace.isEnabled()) {
Trace.write(this._finishedAnimations + ' animations finished.', Trace.categories.Animation);
}
this._resolveAnimationFinishedPromise();
}
}
};
this._iOSAnimationFunction = Animation._createiOSAnimationFunction(this._mergedPropertyAnimations, 0, this._playSequentially, this._valueSource, animationFinishedCallback);
}
play() {
if (this.isPlaying) {
return this._rejectAlreadyPlaying();
}
const animationFinishedPromise = super.play();
this._finishedAnimations = 0;
this._cancelledAnimations = 0;
this._iOSAnimationFunction();
return animationFinishedPromise;
}
cancel() {
if (!this.isPlaying) {
Trace.write('Animation is not currently playing.', Trace.categories.Animation, Trace.messageType.warn);
return;
}
if (this._mergedPropertyAnimations) {
for (let i = 0; i < this._mergedPropertyAnimations.length; i++) {
const propertyAnimation = this._mergedPropertyAnimations[i];
if (propertyAnimation) {
if (propertyAnimation.target?.nativeViewProtected) {
const nativeView = propertyAnimation.target.nativeViewProtected;
if (nativeView.layer.mask) {
nativeView.layer.mask.removeAllAnimations();
}
nativeView.layer.removeAllAnimations();
// Gradient background animations
if (nativeView.gradientLayer) {
nativeView.gradientLayer.removeAllAnimations();
}
// Border animations
if (nativeView.borderLayer) {
if (nativeView.borderLayer.mask) {
nativeView.borderLayer.mask.removeAllAnimations();
}
const borderLayers = nativeView.borderLayer.sublayers;
if (borderLayers?.count) {
for (let i = 0, count = borderLayers.count; i < count; i++) {
borderLayers[i].removeAllAnimations();
}
}
nativeView.borderLayer.removeAllAnimations();
}
// Shadow animations
if (nativeView.outerShadowContainerLayer) {
if (nativeView.outerShadowContainerLayer.mask) {
nativeView.outerShadowContainerLayer.mask.removeAllAnimations();
}
const outerShadowLayers = nativeView.outerShadowContainerLayer.sublayers;
if (outerShadowLayers?.count) {
for (let i = 0, count = outerShadowLayers.count; i < count; i++) {
const shadowLayer = outerShadowLayers[i];
if (shadowLayer.mask) {
shadowLayer.mask.removeAllAnimations();
}
shadowLayer.removeAllAnimations();
}
}
nativeView.outerShadowContainerLayer.removeAllAnimations();
}
}
if (propertyAnimation._propertyResetCallback) {
propertyAnimation._propertyResetCallback(propertyAnimation._originalValue, this._valueSource);
}
}
}
}
}
_resolveAnimationCurve(curve) {
return _resolveAnimationCurve(curve);
}
static _createiOSAnimationFunction(propertyAnimations, index, playSequentially, valueSource, finishedCallback) {
return (cancelled) => {
if (cancelled && finishedCallback) {
if (Trace.isEnabled()) {
Trace.write('Animation ' + (index - 1).toString() + ' was cancelled. Will skip the rest of animations and call finishedCallback(true).', Trace.categories.Animation);
}
finishedCallback(cancelled);
return;
}
const animation = propertyAnimations[index];
const args = Animation._getNativeAnimationArguments(animation, valueSource);
if (animation.curve === 'spring') {
Animation._createNativeSpringAnimation(propertyAnimations, index, playSequentially, args, animation, valueSource, finishedCallback);
}
else {
Animation._createNativeAnimation(propertyAnimations, index, playSequentially, args, animation, valueSource, finishedCallback);
}
};
}
static _getNativeAnimationArguments(animation, valueSource) {
const view = animation.target;
const style = view.style;
const nativeView = view.nativeViewProtected;
const parent = view.parent;
let propertyNameToAnimate = animation.property;
let subPropertyNameToAnimate;
let toValue = animation.value;
let fromValue;
if (nativeView) {
const setLocal = valueSource === 'animation';
switch (animation.property) {
case Properties.backgroundColor:
animation._originalValue = view.backgroundColor;
animation._propertyResetCallback = (value, valueSource) => {
style[setLocal ? backgroundColorProperty.name : backgroundColorProperty.keyframe] = value;
};
fromValue = nativeView.layer.backgroundColor;
if (nativeView instanceof UILabel) {
nativeView.setValueForKey(UIColor.clearColor, 'backgroundColor');
}
toValue = toValue?.ios?.CGColor;
break;
case Properties.opacity:
animation._originalValue = view.opacity;
animation._propertyResetCallback = (value, valueSource) => {
style[setLocal ? opacityProperty.name : opacityProperty.keyframe] = value;
};
fromValue = nativeView.layer.opacity;
break;
case Properties.rotate:
animation._originalValue = {
x: view.rotateX,
y: view.rotateY,
z: view.rotate,
};
animation._propertyResetCallback = (value, valueSource) => {
style[setLocal ? rotateProperty.name : rotateProperty.keyframe] = value.z;
style[setLocal ? rotateXProperty.name : rotateXProperty.keyframe] = value.x;
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,
};
break;
case Properties.translate:
animation._originalValue = {
x: view.translateX,
y: view.translateY,
};
animation._propertyResetCallback = (value, valueSource) => {
style[setLocal ? translateXProperty.name : translateXProperty.keyframe] = value.x;
style[setLocal ? translateYProperty.name : translateYProperty.keyframe] = value.y;
};
propertyNameToAnimate = 'transform';
fromValue = NSValue.valueWithCATransform3D(nativeView.layer.transform);
toValue = NSValue.valueWithCATransform3D(CATransform3DTranslate(nativeView.layer.transform, toValue.x, toValue.y, 0));
break;
case Properties.scale:
if (toValue.x === 0) {
toValue.x = 0.001;
}
if (toValue.y === 0) {
toValue.y = 0.001;
}
animation._originalValue = { x: view.scaleX, y: view.scaleY };
animation._propertyResetCallback = (value, valueSource) => {
style[setLocal ? scaleXProperty.name : scaleXProperty.keyframe] = value.x;
style[setLocal ? scaleYProperty.name : scaleYProperty.keyframe] = value.y;
};
propertyNameToAnimate = 'transform';
fromValue = NSValue.valueWithCATransform3D(nativeView.layer.transform);
toValue = NSValue.valueWithCATransform3D(CATransform3DScale(nativeView.layer.transform, toValue.x, toValue.y, 1));
break;
case _transform:
fromValue = NSValue.valueWithCATransform3D(nativeView.layer.transform);
animation._originalValue = {
xs: view.scaleX,
ys: view.scaleY,
xt: view.translateX,
yt: view.translateY,
rx: view.rotateX,
ry: view.rotateY,
rz: view.rotate,
};
animation._propertyResetCallback = (value, valueSource) => {
style[setLocal ? translateXProperty.name : translateXProperty.keyframe] = value.xt;
style[setLocal ? translateYProperty.name : translateYProperty.keyframe] = value.yt;
style[setLocal ? scaleXProperty.name : scaleXProperty.keyframe] = value.xs;
style[setLocal ? scaleYProperty.name : scaleYProperty.keyframe] = value.ys;
style[setLocal ? rotateXProperty.name : rotateXProperty.keyframe] = value.rx;
style[setLocal ? rotateYProperty.name : rotateYProperty.keyframe] = value.ry;
style[setLocal ? rotateProperty.name : rotateProperty.keyframe] = value.rz;
};
propertyNameToAnimate = 'transform';
toValue = NSValue.valueWithCATransform3D(Animation._createNativeAffineTransform(animation));
break;
case Properties.width:
case Properties.height: {
const direction = animation.property;
const isHeight = direction === 'height';
propertyNameToAnimate = 'bounds';
if (!parent) {
console.error(`cannot animate ${direction} on root view`);
}
const parentExtent = isHeight ? parent.getMeasuredHeight() : parent.getMeasuredWidth();
const asNumber = PercentLength.toDevicePixels(PercentLength.parse(toValue), parentExtent, parentExtent) / Screen.mainScreen.scale;
const currentBounds = nativeView.layer.bounds;
const extentX = isHeight ? currentBounds.size.width : asNumber;
const extentY = isHeight ? asNumber : currentBounds.size.height;
fromValue = NSValue.valueWithCGRect(currentBounds);
toValue = NSValue.valueWithCGRect(CGRectMake(currentBounds.origin.x, currentBounds.origin.y, extentX, extentY));
animation._originalValue = view[isHeight ? 'height' : 'width'];
animation._propertyResetCallback = (value, valueSource) => {
const prop = isHeight ? heightProperty : widthProperty;
style[setLocal ? prop.name : prop.keyframe] = value;
};
break;
}
default:
console.error(`Animating property '${animation.property}' is unsupported`);
}
}
let duration = 0.3;
if (animation.duration !== undefined) {
duration = animation.duration / 1000.0;
}
let delay = undefined;
if (animation.delay) {
delay = animation.delay / 1000.0;
}
let repeatCount = undefined;
if (animation.iterations !== undefined) {
if (animation.iterations === Number.POSITIVE_INFINITY) {
repeatCount = FLT_MAX;
}
else {
repeatCount = animation.iterations;
}
}
return {
propertyNameToAnimate: propertyNameToAnimate,
fromValue: fromValue,
subPropertiesToAnimate: subPropertyNameToAnimate,
toValue: toValue,
duration: duration,
repeatCount: repeatCount,
delay: delay,
};
}
static _createNativeAnimation(propertyAnimations, index, playSequentially, args, animation, valueSource, finishedCallback) {
const nativeView = animation.target.nativeViewProtected;
let nativeAnimation;
if (args.subPropertiesToAnimate) {
nativeAnimation = this._createGroupAnimation(args, animation);
}
else {
nativeAnimation = this._createBasicAnimation(args, animation);
}
const animationDelegate = AnimationDelegateImpl.initWithFinishedCallback(finishedCallback, animation, valueSource);
nativeAnimation.setValueForKey(animationDelegate, 'delegate');
if (nativeView) {
nativeView.layer.addAnimationForKey(nativeAnimation, args.propertyNameToAnimate);
if (args.propertyNameToAnimate === 'bounds') {
this.animateNestedLayerSizeUsingBasicAnimation(nativeView, args.toValue.CGRectValue, animation, args, nativeAnimation);
}
// Shadow layers do not inherit from animating view layer
if (nativeView.outerShadowContainerLayer) {
nativeView.outerShadowContainerLayer.addAnimationForKey(nativeAnimation, args.propertyNameToAnimate);
}
}
let callback = undefined;
if (index + 1 < propertyAnimations.length) {
callback = Animation._createiOSAnimationFunction(propertyAnimations, index + 1, playSequentially, valueSource, finishedCallback);
if (!playSequentially) {
callback();
}
else {
animationDelegate.nextAnimation = callback;
}
}
}
static _createGroupAnimation(args, animation) {
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, duration: undefined, repeatCount: undefined, delay: undefined, curve: undefined };
basicAnimationArgs.propertyNameToAnimate = `${args.propertyNameToAnimate}.${property}`;
basicAnimationArgs.fromValue = args.fromValue[property];
basicAnimationArgs.toValue = args.toValue[property];
const basicAnimation = this._createBasicAnimation(basicAnimationArgs, animation);
animations.addObject(basicAnimation);
});
groupAnimation.animations = animations;
return groupAnimation;
}
static _createBasicAnimation(args, animation) {
const basicAnimation = CABasicAnimation.animationWithKeyPath(args.propertyNameToAnimate);
basicAnimation.fromValue = args.fromValue;
basicAnimation.toValue = args.toValue;
basicAnimation.duration = args.duration;
if (args.repeatCount !== undefined) {
basicAnimation.repeatCount = args.repeatCount;
}
if (args.delay !== undefined) {
basicAnimation.beginTime = CACurrentMediaTime() + args.delay;
}
if (animation.curve !== undefined) {
basicAnimation.timingFunction = animation.curve;
}
return basicAnimation;
}
static _createNativeSpringAnimation(propertyAnimations, index, playSequentially, args, animation, valueSource, finishedCallback) {
const nativeView = animation.target.nativeViewProtected;
let callback = undefined;
let nextAnimation;
if (index + 1 < propertyAnimations.length) {
callback = Animation._createiOSAnimationFunction(propertyAnimations, index + 1, playSequentially, valueSource, finishedCallback);
if (!playSequentially) {
callback();
}
else {
nextAnimation = callback;
}
}
let delay = 0;
if (args.delay) {
delay = args.delay;
}
UIView.animateWithDurationDelayUsingSpringWithDampingInitialSpringVelocityOptionsAnimationsCompletion(args.duration, delay, 0.2, 0, 196608 /* UIViewAnimationOptions.CurveLinear */, () => {
if (args.repeatCount !== undefined) {
UIView.setAnimationRepeatCount(args.repeatCount);
}
switch (animation.property) {
case Properties.backgroundColor:
animation.target.backgroundColor = args.toValue;
break;
case Properties.opacity:
animation.target.opacity = args.toValue;
break;
case Properties.height:
case Properties.width:
animation._originalValue = animation.target[animation.property];
nativeView.layer.setValueForKey(args.toValue, args.propertyNameToAnimate);
// Resize background during animation
iosBackground.drawBackgroundVisualEffects(animation.target);
animation._propertyResetCallback = function (value) {
animation.target[animation.property] = value;
};
break;
case _transform:
animation._originalValue = nativeView.layer.transform;
nativeView.layer.setValueForKey(args.toValue, args.propertyNameToAnimate);
// Shadow layers do not inherit from animating view layer
if (nativeView.outerShadowContainerLayer) {
nativeView.outerShadowContainerLayer.setValueForKey(args.toValue, args.propertyNameToAnimate);
}
animation._propertyResetCallback = function (value) {
nativeView.layer.transform = value;
};
break;
}
}, function (animationDidFinish) {
if (animationDidFinish) {
if (animation.property === _transform) {
if (animation.value[Properties.translate] !== undefined) {
animation.target.translateX = animation.value[Properties.translate].x;
animation.target.translateY = animation.value[Properties.translate].y;
}
if (animation.value[Properties.rotate] !== undefined) {
animation.target.rotateX = animation.value[Properties.rotate].x;
animation.target.rotateY = animation.value[Properties.rotate].y;
animation.target.rotate = animation.value[Properties.rotate].z;
}
if (animation.value[Properties.scale] !== undefined) {
animation.target.scaleX = animation.value[Properties.scale].x;
animation.target.scaleY = animation.value[Properties.scale].y;
}
}
}
else {
if (animation._propertyResetCallback) {
animation._propertyResetCallback(animation._originalValue);
}
}
if (finishedCallback) {
const cancelled = !animationDidFinish;
finishedCallback(cancelled);
}
if (animationDidFinish && nextAnimation) {
nextAnimation();
}
});
}
static _createNativeAffineTransform(animation) {
const value = animation.value;
let result = CATransform3DIdentity;
if (value[Properties.translate] !== undefined) {
const x = value[Properties.translate].x;
const y = value[Properties.translate].y;
result = CATransform3DTranslate(result, x, y, 0);
}
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);
}
return result;
}
static _isAffineTransform(property) {
return property === _transform || property === Properties.translate || property === Properties.scale;
}
static _canBeMerged(animation1, animation2) {
const result = Animation._isAffineTransform(animation1.property) && Animation._isAffineTransform(animation2.property) && animation1.target === animation2.target && animation1.duration === animation2.duration && animation1.delay === animation2.delay && animation1.iterations === animation2.iterations && animation1.curve === animation2.curve;
return result;
}
static _mergeAffineTransformAnimations(propertyAnimations) {
const result = new Array();
let i = 0;
let j;
const length = propertyAnimations.length;
for (; i < length; i++) {
if (propertyAnimations[i][_skip]) {
continue;
}
if (!Animation._isAffineTransform(propertyAnimations[i].property)) {
// This is not an affine transform animation, so there is nothing to merge.
result.push(propertyAnimations[i]);
}
else {
// This animation has not been merged anywhere. Create a new transform animation.
// The value becomes a JSON object combining all affine transforms together like this:
// {
// translate: {x: 100, y: 100 },
// rotate: 90,
// scale: {x: 2, y: 2 }
// }
const newTransformAnimation = {
target: propertyAnimations[i].target,
property: _transform,
value: {},
duration: propertyAnimations[i].duration,
delay: propertyAnimations[i].delay,
iterations: propertyAnimations[i].iterations,
curve: propertyAnimations[i].curve,
};
if (Trace.isEnabled()) {
Trace.write('Curve: ' + propertyAnimations[i].curve, Trace.categories.Animation);
}
newTransformAnimation.value[propertyAnimations[i].property] = propertyAnimations[i].value;
if (Trace.isEnabled()) {
Trace.write('Created new transform animation: ' + Animation._getAnimationInfo(newTransformAnimation), Trace.categories.Animation);
}
// Merge all compatible affine transform animations to the right into this new animation.
j = i + 1;
if (j < length) {
for (; j < length; j++) {
if (Animation._canBeMerged(propertyAnimations[i], propertyAnimations[j])) {
if (Trace.isEnabled()) {
Trace.write('Merging animations: ' + Animation._getAnimationInfo(newTransformAnimation) + ' + ' + Animation._getAnimationInfo(propertyAnimations[j]) + ';', Trace.categories.Animation);
}
newTransformAnimation.value[propertyAnimations[j].property] = propertyAnimations[j].value;
// Mark that it has been merged so we can skip it on our outer loop.
propertyAnimations[j][_skip] = true;
}
}
}
result.push(newTransformAnimation);
}
}
return result;
}
static animateNestedLayerSizeUsingBasicAnimation(nativeView, bounds, animation, args, nativeAnimation) {
const view = animation.target;
// Gradient background animation
if (nativeView.gradientLayer) {
nativeView.gradientLayer.addAnimationForKey(nativeAnimation, 'bounds');
}
let clipPath; // This is also used for animating shadow
// Clipping mask animation
if (nativeView.layer.mask instanceof CAShapeLayer) {
let toValue;
if (nativeView.maskType === iosViewUtils.LayerMask.BORDER) {
toValue = iosBackground.generateNonUniformBorderOuterClipRoundedPath(view, bounds);
}
else if (nativeView.maskType === iosViewUtils.LayerMask.CLIP_PATH) {
clipPath = iosBackground.generateClipPath(view, bounds);
toValue = clipPath;
}
else {
Trace.write('Unknown mask on animating view: ' + view, Trace.categories.Animation, Trace.messageType.info);
}
if (toValue) {
nativeView.layer.mask.addAnimationForKey(this._createBasicAnimation({
...args,
propertyNameToAnimate: 'path',
fromValue: nativeView.layer.mask.path,
toValue,
}, animation), 'path');
}
}
// Border animations (uniform and non-uniform)
if (nativeView.hasNonUniformBorder) {
if (nativeView.borderLayer) {
const innerClipPath = iosBackground.generateNonUniformBorderInnerClipRoundedPath(animation.target, bounds);
if (nativeView.hasNonUniformBorderColor) {
const borderMask = nativeView.borderLayer.mask;
if (borderMask instanceof CAShapeLayer) {
borderMask.addAnimationForKey(this._createBasicAnimation({
...args,
propertyNameToAnimate: 'path',
fromValue: borderMask.path,
toValue: innerClipPath,
}, animation), 'path');
}
const borderLayers = nativeView.borderLayer.sublayers;
if (borderLayers?.count) {
const paths = iosBackground.generateNonUniformMultiColorBorderRoundedPaths(animation.target, bounds);
for (let i = 0, count = borderLayers.count; i < count; i++) {
const layer = nativeView.borderLayer.sublayers[i];
if (layer instanceof CAShapeLayer) {
layer.addAnimationForKey(this._createBasicAnimation({
...args,
propertyNameToAnimate: 'path',
fromValue: layer.path,
toValue: paths[i],
}, animation), 'path');
}
}
}
}
else {
nativeView.borderLayer.addAnimationForKey(this._createBasicAnimation({
...args,
propertyNameToAnimate: 'path',
fromValue: nativeView.borderLayer.path,
toValue: innerClipPath,
}, animation), 'path');
}
}
}
else {
// TODO: Animate border width when borders get support for percentage values
// Uniform corner radius also relies on view size
if (nativeView.layer.cornerRadius) {
nativeView.layer.addAnimationForKey(this._createBasicAnimation({
...args,
propertyNameToAnimate: 'cornerRadius',
fromValue: nativeView.layer.cornerRadius,
toValue: iosBackground.getUniformBorderRadius(animation.target, bounds),
}, animation), 'cornerRadius');
}
}
// Shadow layers do not inherit from animating view layer
if (nativeView.outerShadowContainerLayer) {
const shadowClipMask = nativeView.outerShadowContainerLayer.mask;
// This is for animating view clip path on shadow
if (clipPath && shadowClipMask instanceof CAShapeLayer) {
shadowClipMask.addAnimationForKey(this._createBasicAnimation({
...args,
propertyNameToAnimate: 'path',
fromValue: shadowClipMask.path,
toValue: clipPath,
}, animation), 'path');
}
const outerShadowLayers = nativeView.outerShadowContainerLayer.sublayers;
if (outerShadowLayers?.count) {
const { maskPath, shadowPath } = iosBackground.generateShadowLayerPaths(view, bounds);
for (let i = 0, count = outerShadowLayers.count; i < count; i++) {
const shadowLayer = outerShadowLayers[i];
shadowLayer.addAnimationForKey(this._createBasicAnimation({
...args,
propertyNameToAnimate: 'shadowPath',
fromValue: shadowLayer.shadowPath,
toValue: shadowPath,
}, animation), 'shadowPath');
if (shadowLayer.mask instanceof CAShapeLayer) {
shadowLayer.mask.addAnimationForKey(this._createBasicAnimation({
...args,
propertyNameToAnimate: 'path',
fromValue: shadowLayer.mask.path,
toValue: maskPath,
}, animation), 'path');
}
}
}
}
}
}
export function _getTransformMismatchErrorMessage(view) {
const expectedTransform = calculateTransform(view);
const expectedTransformString = getCATransform3DString(expectedTransform);
const actualTransformString = getCATransform3DString(view.nativeViewProtected.layer.transform);
if (actualTransformString !== expectedTransformString) {
return 'View and Native transforms do not match.\nActual: ' + actualTransformString + ';\nExpected: ' + expectedTransformString;
}
return undefined;
}
function calculateTransform(view) {
const scaleX = view.scaleX || 1e-6;
const scaleY = view.scaleY || 1e-6;
const perspective = view.perspective || 300;
// Order is important: translate, rotate, scale
let expectedTransform = new CATransform3D(CATransform3DIdentity);
// Only set perspective if there is 3D rotation
if (view.rotateX || view.rotateY) {
expectedTransform.m34 = -1 / perspective;
}
expectedTransform = CATransform3DTranslate(expectedTransform, view.translateX, view.translateY, 0);
expectedTransform = iosHelper.applyRotateTransform(expectedTransform, view.rotateX, view.rotateY, view.rotate);
expectedTransform = CATransform3DScale(expectedTransform, scaleX, scaleY, 1);
return expectedTransform;
}
function getCATransform3DString(t) {
return `[
${t.m11}, ${t.m12}, ${t.m13}, ${t.m14},
${t.m21}, ${t.m22}, ${t.m23}, ${t.m24},
${t.m31}, ${t.m32}, ${t.m33}, ${t.m34},
${t.m41}, ${t.m42}, ${t.m43}, ${t.m44}]`;
}
//# sourceMappingURL=index.ios.js.map