UNPKG

23.9 kBJavaScriptView Raw
1// Requires
2import { AnimationBase, Properties, CubicBezierAnimationCurve } from './animation-common';
3import { Color } from '../../color';
4import { Trace } from '../../trace';
5import { opacityProperty, backgroundColorProperty, rotateProperty, rotateXProperty, rotateYProperty, translateXProperty, translateYProperty, scaleXProperty, scaleYProperty, heightProperty, widthProperty, PercentLength } from '../styling/style-properties';
6import { layout } from '../../utils';
7import { SDK_VERSION } from '../../utils/constants';
8import { Screen } from '../../platform';
9import lazy from '../../utils/lazy';
10export * from './animation-common';
11export { KeyframeAnimation, KeyframeAnimationInfo, KeyframeDeclaration, KeyframeInfo } from './keyframe-animation';
12let argbEvaluator;
13function ensureArgbEvaluator() {
14 if (!argbEvaluator) {
15 argbEvaluator = new android.animation.ArgbEvaluator();
16 }
17}
18const easeIn = lazy(() => new android.view.animation.AccelerateInterpolator(1));
19const easeOut = lazy(() => new android.view.animation.DecelerateInterpolator(1));
20const easeInOut = lazy(() => new android.view.animation.AccelerateDecelerateInterpolator());
21const linear = lazy(() => new android.view.animation.LinearInterpolator());
22const bounce = lazy(() => new android.view.animation.BounceInterpolator());
23export function _resolveAnimationCurve(curve) {
24 switch (curve) {
25 case 'easeIn':
26 if (Trace.isEnabled()) {
27 Trace.write('Animation curve resolved to android.view.animation.AccelerateInterpolator(1).', Trace.categories.Animation);
28 }
29 return easeIn();
30 case 'easeOut':
31 if (Trace.isEnabled()) {
32 Trace.write('Animation curve resolved to android.view.animation.DecelerateInterpolator(1).', Trace.categories.Animation);
33 }
34 return easeOut();
35 case 'easeInOut':
36 if (Trace.isEnabled()) {
37 Trace.write('Animation curve resolved to android.view.animation.AccelerateDecelerateInterpolator().', Trace.categories.Animation);
38 }
39 return easeInOut();
40 case 'linear':
41 if (Trace.isEnabled()) {
42 Trace.write('Animation curve resolved to android.view.animation.LinearInterpolator().', Trace.categories.Animation);
43 }
44 return linear();
45 case 'spring':
46 if (Trace.isEnabled()) {
47 Trace.write('Animation curve resolved to android.view.animation.BounceInterpolator().', Trace.categories.Animation);
48 }
49 return bounce();
50 case 'ease':
51 return androidx.core.view.animation.PathInterpolatorCompat.create(0.25, 0.1, 0.25, 1.0);
52 default:
53 if (Trace.isEnabled()) {
54 Trace.write('Animation curve resolved to original: ' + curve, Trace.categories.Animation);
55 }
56 if (curve instanceof CubicBezierAnimationCurve) {
57 return androidx.core.view.animation.PathInterpolatorCompat.create(curve.x1, curve.y1, curve.x2, curve.y2);
58 }
59 else if (curve && curve.getInterpolation) {
60 return curve;
61 }
62 else if (curve instanceof android.view.animation.LinearInterpolator) {
63 return curve;
64 }
65 else {
66 throw new Error(`Invalid animation curve: ${curve}`);
67 }
68 }
69}
70function getAndroidRepeatCount(iterations) {
71 return iterations === Number.POSITIVE_INFINITY ? android.view.animation.Animation.INFINITE : iterations - 1;
72}
73function createObjectAnimator(nativeView, propertyName, value) {
74 const arr = Array.create('float', 1);
75 arr[0] = value;
76 return android.animation.ObjectAnimator.ofFloat(nativeView, propertyName, arr);
77}
78function createAnimationSet(animators, iterations) {
79 iterations = getAndroidRepeatCount(iterations);
80 const animatorSet = new android.animation.AnimatorSet();
81 const animatorsArray = Array.create(android.animation.Animator, animators.length);
82 animators.forEach((animator, index) => {
83 animatorsArray[index] = animator;
84 //TODO: not sure if we have to do that for each animator
85 animatorsArray[index].setRepeatCount(iterations);
86 });
87 animatorSet.playTogether(animatorsArray);
88 animatorSet.setupStartValues();
89 return animatorSet;
90}
91export class Animation extends AnimationBase {
92 constructor(animationDefinitions, playSequentially) {
93 super(animationDefinitions, playSequentially);
94 this._resetOnFinish = true;
95 this._valueSource = 'animation';
96 if (animationDefinitions.length > 0 && animationDefinitions[0].valueSource !== undefined) {
97 this._valueSource = animationDefinitions[0].valueSource;
98 }
99 const that = new WeakRef(this);
100 this._animatorListener = new android.animation.Animator.AnimatorListener({
101 onAnimationStart: function (animator) {
102 if (Trace.isEnabled()) {
103 Trace.write('MainAnimatorListener.onAndroidAnimationStart(' + animator + ')', Trace.categories.Animation);
104 }
105 },
106 onAnimationRepeat: function (animator) {
107 if (Trace.isEnabled()) {
108 Trace.write('MainAnimatorListener.onAnimationRepeat(' + animator + ')', Trace.categories.Animation);
109 }
110 },
111 onAnimationEnd: function (animator) {
112 if (Trace.isEnabled()) {
113 Trace.write('MainAnimatorListener.onAnimationEnd(' + animator + ')', Trace.categories.Animation);
114 }
115 const thisRef = that?.get();
116 if (thisRef) {
117 thisRef._onAndroidAnimationEnd();
118 }
119 },
120 onAnimationCancel: function (animator) {
121 if (Trace.isEnabled()) {
122 Trace.write('MainAnimatorListener.onAnimationCancel(' + animator + ')', Trace.categories.Animation);
123 }
124 const thisRef = that?.get();
125 if (thisRef) {
126 thisRef._onAndroidAnimationCancel();
127 }
128 },
129 });
130 }
131 play(resetOnFinish) {
132 if (resetOnFinish !== undefined) {
133 this._resetOnFinish = resetOnFinish;
134 }
135 if (this.isPlaying) {
136 return this._rejectAlreadyPlaying();
137 }
138 const animationFinishedPromise = super.play();
139 if (!this._animatorSet) {
140 this._animators = new Array();
141 this._propertyUpdateCallbacks = new Array();
142 this._propertyResetCallbacks = new Array();
143 for (let i = 0, length = this._propertyAnimations.length; i < length; i++) {
144 this._createAnimators(this._propertyAnimations[i]);
145 }
146 this._nativeAnimatorsArray = Array.create(android.animation.Animator, this._animators.length);
147 for (let i = 0, length = this._animators.length; i < length; i++) {
148 this._nativeAnimatorsArray[i] = this._animators[i];
149 }
150 this._animatorSet = new android.animation.AnimatorSet();
151 this._animatorSet.addListener(this._animatorListener);
152 }
153 this._play();
154 return animationFinishedPromise;
155 }
156 cancel() {
157 if (!this.isPlaying) {
158 Trace.write('Animation is not currently playing.', Trace.categories.Animation, Trace.messageType.warn);
159 return;
160 }
161 Trace.write('Cancelling AnimatorSet.', Trace.categories.Animation);
162 this._animatorSet.cancel();
163 }
164 _resolveAnimationCurve(curve) {
165 return _resolveAnimationCurve(curve);
166 }
167 _play() {
168 if (SDK_VERSION <= 23) {
169 this._animatorSet = new android.animation.AnimatorSet();
170 this._animatorSet.addListener(this._animatorListener);
171 }
172 if (this._animators.length > 0) {
173 if (this._playSequentially) {
174 this._animatorSet.playSequentially(this._nativeAnimatorsArray);
175 }
176 else {
177 this._animatorSet.playTogether(this._nativeAnimatorsArray);
178 }
179 }
180 if (Trace.isEnabled()) {
181 Trace.write('Starting ' + this._nativeAnimatorsArray.length + ' animations ' + (this._playSequentially ? 'sequentially.' : 'together.'), Trace.categories.Animation);
182 }
183 this._animatorSet.setupStartValues();
184 this._animatorSet.start();
185 }
186 _onAndroidAnimationEnd() {
187 // tslint:disable-line
188 if (!this.isPlaying) {
189 // It has been cancelled
190 return;
191 }
192 this._propertyUpdateCallbacks.forEach((v) => v());
193 this._resolveAnimationFinishedPromise();
194 if (this._resetOnFinish && this._target) {
195 this._target._removeAnimation(this);
196 }
197 }
198 _onAndroidAnimationCancel() {
199 // tslint:disable-line
200 this._propertyResetCallbacks.forEach((v) => v());
201 this._resolveAnimationFinishedPromise();
202 if (this._target) {
203 this._target._removeAnimation(this);
204 }
205 }
206 _createAnimators(propertyAnimation) {
207 if (!propertyAnimation.target.nativeViewProtected) {
208 return;
209 }
210 if (Trace.isEnabled()) {
211 Trace.write('Creating ObjectAnimator(s) for animation: ' + Animation._getAnimationInfo(propertyAnimation) + '...', Trace.categories.Animation);
212 }
213 if (propertyAnimation.target === null || propertyAnimation.target === undefined) {
214 throw new Error(`Animation target cannot be null or undefined; property: ${propertyAnimation.property}; value: ${propertyAnimation.value};`);
215 }
216 if (propertyAnimation.property === null || propertyAnimation.property === undefined) {
217 throw new Error(`Animation property cannot be null or undefined; target: ${propertyAnimation.target}; value: ${propertyAnimation.value};`);
218 }
219 if (propertyAnimation.value === null || propertyAnimation.value === undefined) {
220 throw new Error(`Animation value cannot be null or undefined; target: ${propertyAnimation.target}; property: ${propertyAnimation.property};`);
221 }
222 this._target = propertyAnimation.target;
223 const nativeView = propertyAnimation.target.nativeViewProtected;
224 const animators = new Array();
225 const propertyUpdateCallbacks = new Array();
226 const propertyResetCallbacks = new Array();
227 let originalValue1;
228 let originalValue2;
229 let originalValue3;
230 const density = layout.getDisplayDensity();
231 const setLocal = this._valueSource === 'animation';
232 const style = propertyAnimation.target.style;
233 switch (propertyAnimation.property) {
234 case Properties.opacity:
235 opacityProperty._initDefaultNativeValue(style);
236 originalValue1 = nativeView.getAlpha();
237 propertyUpdateCallbacks.push(() => {
238 propertyAnimation.target.style[setLocal ? opacityProperty.name : opacityProperty.keyframe] = propertyAnimation.value;
239 });
240 propertyResetCallbacks.push(() => {
241 if (setLocal) {
242 propertyAnimation.target.style[opacityProperty.name] = originalValue1;
243 }
244 else {
245 propertyAnimation.target.style[opacityProperty.keyframe] = originalValue1;
246 }
247 if (propertyAnimation.target.nativeViewProtected) {
248 propertyAnimation.target[opacityProperty.setNative](propertyAnimation.target.style.opacity);
249 }
250 });
251 animators.push(createObjectAnimator(nativeView, 'alpha', propertyAnimation.value));
252 break;
253 case Properties.backgroundColor: {
254 backgroundColorProperty._initDefaultNativeValue(style);
255 ensureArgbEvaluator();
256 originalValue1 = propertyAnimation.target.backgroundColor;
257 const nativeArray = Array.create(java.lang.Object, 2);
258 nativeArray[0] = propertyAnimation.target.backgroundColor ? java.lang.Integer.valueOf(propertyAnimation.target.backgroundColor.argb) : java.lang.Integer.valueOf(-1);
259 nativeArray[1] = java.lang.Integer.valueOf(propertyAnimation.value.argb);
260 const animator = android.animation.ValueAnimator.ofObject(argbEvaluator, nativeArray);
261 animator.addUpdateListener(new android.animation.ValueAnimator.AnimatorUpdateListener({
262 onAnimationUpdate(animator) {
263 const argb = animator.getAnimatedValue().intValue();
264 propertyAnimation.target.style[setLocal ? backgroundColorProperty.name : backgroundColorProperty.keyframe] = new Color(argb);
265 },
266 }));
267 propertyUpdateCallbacks.push(() => {
268 propertyAnimation.target.style[setLocal ? backgroundColorProperty.name : backgroundColorProperty.keyframe] = propertyAnimation.value;
269 });
270 propertyResetCallbacks.push(() => {
271 if (setLocal) {
272 propertyAnimation.target.style[backgroundColorProperty.name] = originalValue1;
273 }
274 else {
275 propertyAnimation.target.style[backgroundColorProperty.keyframe] = originalValue1;
276 }
277 if (propertyAnimation.target.nativeViewProtected && propertyAnimation.target[backgroundColorProperty.setNative]) {
278 propertyAnimation.target[backgroundColorProperty.setNative](propertyAnimation.target.style.backgroundColor);
279 }
280 });
281 animators.push(animator);
282 break;
283 }
284 case Properties.translate:
285 translateXProperty._initDefaultNativeValue(style);
286 translateYProperty._initDefaultNativeValue(style);
287 originalValue1 = nativeView.getTranslationX() / density;
288 originalValue2 = nativeView.getTranslationY() / density;
289 propertyUpdateCallbacks.push(() => {
290 propertyAnimation.target.style[setLocal ? translateXProperty.name : translateXProperty.keyframe] = propertyAnimation.value.x;
291 propertyAnimation.target.style[setLocal ? translateYProperty.name : translateYProperty.keyframe] = propertyAnimation.value.y;
292 });
293 propertyResetCallbacks.push(() => {
294 if (setLocal) {
295 propertyAnimation.target.style[translateXProperty.name] = originalValue1;
296 propertyAnimation.target.style[translateYProperty.name] = originalValue2;
297 }
298 else {
299 propertyAnimation.target.style[translateXProperty.keyframe] = originalValue1;
300 propertyAnimation.target.style[translateYProperty.keyframe] = originalValue2;
301 }
302 if (propertyAnimation.target.nativeViewProtected) {
303 propertyAnimation.target[translateXProperty.setNative](propertyAnimation.target.style.translateX);
304 propertyAnimation.target[translateYProperty.setNative](propertyAnimation.target.style.translateY);
305 }
306 });
307 animators.push(createAnimationSet([createObjectAnimator(nativeView, 'translationX', propertyAnimation.value.x * density), createObjectAnimator(nativeView, 'translationY', propertyAnimation.value.y * density)], propertyAnimation.iterations));
308 break;
309 case Properties.scale:
310 scaleXProperty._initDefaultNativeValue(style);
311 scaleYProperty._initDefaultNativeValue(style);
312 originalValue1 = nativeView.getScaleX();
313 originalValue2 = nativeView.getScaleY();
314 propertyUpdateCallbacks.push(() => {
315 propertyAnimation.target.style[setLocal ? scaleXProperty.name : scaleXProperty.keyframe] = propertyAnimation.value.x;
316 propertyAnimation.target.style[setLocal ? scaleYProperty.name : scaleYProperty.keyframe] = propertyAnimation.value.y;
317 });
318 propertyResetCallbacks.push(() => {
319 if (setLocal) {
320 propertyAnimation.target.style[scaleXProperty.name] = originalValue1;
321 propertyAnimation.target.style[scaleYProperty.name] = originalValue2;
322 }
323 else {
324 propertyAnimation.target.style[scaleXProperty.keyframe] = originalValue1;
325 propertyAnimation.target.style[scaleYProperty.keyframe] = originalValue2;
326 }
327 if (propertyAnimation.target.nativeViewProtected) {
328 propertyAnimation.target[scaleXProperty.setNative](propertyAnimation.target.style.scaleX);
329 propertyAnimation.target[scaleYProperty.setNative](propertyAnimation.target.style.scaleY);
330 }
331 });
332 animators.push(createAnimationSet([createObjectAnimator(nativeView, 'scaleX', propertyAnimation.value.x), createObjectAnimator(nativeView, 'scaleY', propertyAnimation.value.y)], propertyAnimation.iterations));
333 break;
334 case Properties.rotate:
335 rotateProperty._initDefaultNativeValue(style);
336 rotateXProperty._initDefaultNativeValue(style);
337 rotateYProperty._initDefaultNativeValue(style);
338 originalValue1 = nativeView.getRotationX();
339 originalValue2 = nativeView.getRotationY();
340 originalValue3 = nativeView.getRotation();
341 propertyUpdateCallbacks.push(() => {
342 propertyAnimation.target.style[setLocal ? rotateXProperty.name : rotateXProperty.keyframe] = propertyAnimation.value.x;
343 propertyAnimation.target.style[setLocal ? rotateYProperty.name : rotateYProperty.keyframe] = propertyAnimation.value.y;
344 propertyAnimation.target.style[setLocal ? rotateProperty.name : rotateProperty.keyframe] = propertyAnimation.value.z;
345 });
346 propertyResetCallbacks.push(() => {
347 if (setLocal) {
348 propertyAnimation.target.style[rotateXProperty.name] = originalValue1;
349 propertyAnimation.target.style[rotateYProperty.name] = originalValue2;
350 propertyAnimation.target.style[rotateProperty.name] = originalValue3;
351 }
352 else {
353 propertyAnimation.target.style[rotateXProperty.keyframe] = originalValue1;
354 propertyAnimation.target.style[rotateYProperty.keyframe] = originalValue2;
355 propertyAnimation.target.style[rotateProperty.keyframe] = originalValue3;
356 }
357 if (propertyAnimation.target.nativeViewProtected) {
358 propertyAnimation.target[rotateProperty.setNative](propertyAnimation.target.style.rotate);
359 propertyAnimation.target[rotateXProperty.setNative](propertyAnimation.target.style.rotateX);
360 propertyAnimation.target[rotateYProperty.setNative](propertyAnimation.target.style.rotateY);
361 }
362 });
363 animators.push(createAnimationSet([createObjectAnimator(nativeView, 'rotationX', propertyAnimation.value.x), createObjectAnimator(nativeView, 'rotationY', propertyAnimation.value.y), createObjectAnimator(nativeView, 'rotation', propertyAnimation.value.z)], propertyAnimation.iterations));
364 break;
365 case Properties.width:
366 case Properties.height: {
367 const isVertical = propertyAnimation.property === 'height';
368 const extentProperty = isVertical ? heightProperty : widthProperty;
369 extentProperty._initDefaultNativeValue(style);
370 const nativeArray = Array.create('float', 2);
371 let toValue = propertyAnimation.value;
372 const parent = propertyAnimation.target.parent;
373 if (!parent) {
374 throw new Error(`cannot animate ${propertyAnimation.property} on root view`);
375 }
376 const parentExtent = isVertical ? parent.getMeasuredHeight() : parent.getMeasuredWidth();
377 toValue = PercentLength.toDevicePixels(toValue, parentExtent, parentExtent) / Screen.mainScreen.scale;
378 const nativeHeight = isVertical ? nativeView.getHeight() : nativeView.getWidth();
379 const targetStyle = setLocal ? extentProperty.name : extentProperty.keyframe;
380 originalValue1 = nativeHeight / Screen.mainScreen.scale;
381 nativeArray[0] = originalValue1;
382 nativeArray[1] = toValue;
383 const extentAnimator = android.animation.ValueAnimator.ofFloat(nativeArray);
384 extentAnimator.addUpdateListener(new android.animation.ValueAnimator.AnimatorUpdateListener({
385 onAnimationUpdate(animator) {
386 const argb = animator.getAnimatedValue().floatValue();
387 propertyAnimation.target.style[setLocal ? extentProperty.name : extentProperty.keyframe] = argb;
388 },
389 }));
390 propertyUpdateCallbacks.push(() => {
391 propertyAnimation.target.style[targetStyle] = propertyAnimation.value;
392 });
393 propertyResetCallbacks.push(() => {
394 propertyAnimation.target.style[targetStyle] = originalValue1;
395 if (propertyAnimation.target.nativeViewProtected) {
396 const setter = propertyAnimation.target[extentProperty.setNative];
397 setter(propertyAnimation.target.style[propertyAnimation.property]);
398 }
399 });
400 animators.push(extentAnimator);
401 break;
402 }
403 default:
404 throw new Error(`Animating property '${propertyAnimation.property}' is unsupported`);
405 }
406 for (let i = 0, length = animators.length; i < length; i++) {
407 // Duration
408 if (propertyAnimation.duration !== undefined) {
409 animators[i].setDuration(propertyAnimation.duration);
410 }
411 // Delay
412 if (propertyAnimation.delay !== undefined) {
413 animators[i].setStartDelay(propertyAnimation.delay);
414 }
415 // Repeat Count
416 if (propertyAnimation.iterations !== undefined && animators[i] instanceof android.animation.ValueAnimator) {
417 animators[i].setRepeatCount(getAndroidRepeatCount(propertyAnimation.iterations));
418 }
419 // Interpolator
420 if (propertyAnimation.curve !== undefined) {
421 animators[i].setInterpolator(propertyAnimation.curve);
422 }
423 if (Trace.isEnabled()) {
424 Trace.write('Animator created: ' + animators[i], Trace.categories.Animation);
425 }
426 }
427 this._animators = this._animators.concat(animators);
428 this._propertyUpdateCallbacks = this._propertyUpdateCallbacks.concat(propertyUpdateCallbacks);
429 this._propertyResetCallbacks = this._propertyResetCallbacks.concat(propertyResetCallbacks);
430 }
431}
432//# sourceMappingURL=index.android.js.map
\No newline at end of file