FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

feat(animation): support animating width/height properties · NativeScript/NativeScript@85a84ac · GitHub

Commit 85a84ac

Browse files
feat(animation): support animating width/height properties
- width/height can be specified in any valid PercentLength form that can be parsed. - make width/height properties be based on animatable CSS property. TODO: affectsLayout???? - add a few basic tests. Could probably use a few more? - fix a few null pointer exceptions in PercentLength helpers
1 parent 2f0d3b0 commit 85a84ac

8 files changed

Lines changed: 380 additions & 87 deletions

File tree

‎tests/app/ui/animation/animation-tests.ts‎

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { AnimationPromise } from "tns-core-modules/ui/animation";
99

1010
// >> animation-require
1111
import * as animation from "tns-core-modules/ui/animation";
12+
import {PercentLength} from "tns-core-modules/ui/styling/style-properties";
1213
// << animation-require
1314

1415
function prepareTest(): Label {
@@ -370,6 +371,65 @@ export function test_AnimateRotate(done) {
370371
});
371372
}
372373

374+
// Bad inputs for PercentLength properties
375+
const badPercentLengthInputs: string[] = [
376+
'-l??%',
377+
'qre%',
378+
'undefinedpx',
379+
'undefined',
380+
'-frog%'
381+
];
382+
383+
export function test_AnimateHeight(done) {
384+
let label = prepareTest();
385+
386+
label.animate({ height: 123, duration: 5 })
387+
.then(() => {
388+
TKUnit.assertEqual(label.height, 123, "label.height");
389+
assertIOSNativeTransformIsCorrect(label);
390+
done();
391+
})
392+
.catch((e) => {
393+
done(e);
394+
});
395+
}
396+
397+
export function test_AnimateHeight_ShouldThrow_IfCannotParsePercentLength() {
398+
const label = new Label();
399+
helper.buildUIAndRunTest(label, (views: Array<viewModule.View>) => {
400+
badPercentLengthInputs.forEach((input: PercentLength) => {
401+
TKUnit.assertThrows(() => {
402+
label.animate({ height: input });
403+
}, `Setting height to '${input}' should throw.`);
404+
});
405+
});
406+
}
407+
408+
export function test_AnimateWidth(done) {
409+
let label = prepareTest();
410+
411+
label.animate({ width: 123, duration: 5 })
412+
.then(() => {
413+
TKUnit.assertEqual(label.width, 123, "label.width");
414+
assertIOSNativeTransformIsCorrect(label);
415+
done();
416+
})
417+
.catch((e) => {
418+
done(e);
419+
});
420+
}
421+
422+
export function test_AnimateWidth_ShouldThrow_IfCannotParsePercentLength() {
423+
const label = new Label();
424+
helper.buildUIAndRunTest(label, (views: Array<viewModule.View>) => {
425+
badPercentLengthInputs.forEach((input: PercentLength) => {
426+
TKUnit.assertThrows(() => {
427+
label.animate({ width: input });
428+
}, `Setting width to '${input}' should throw.`);
429+
});
430+
});
431+
}
432+
373433
export function test_AnimateTranslateScaleAndRotateSimultaneously(done) {
374434
let label = prepareTest();
375435

‎tns-core-modules/ui/animation/animation-common.ts‎

Lines changed: 44 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,28 @@
11
// Definitions.
22
import {
3-
CubicBezierAnimationCurve as CubicBezierAnimationCurveDefinition,
4-
AnimationPromise as AnimationPromiseDefinition,
53
Animation as AnimationBaseDefinition,
64
AnimationDefinition,
5+
AnimationPromise as AnimationPromiseDefinition,
6+
CubicBezierAnimationCurve as CubicBezierAnimationCurveDefinition,
77
Pair
88
} from ".";
9-
import { View } from "../core/view";
10-
9+
import {View} from "../core/view";
1110
// Types.
12-
import { Color } from "../../color";
13-
import { isEnabled as traceEnabled, write as traceWrite, categories as traceCategories } from "../../trace";
11+
import {Color} from "../../color";
12+
import {categories as traceCategories, isEnabled as traceEnabled, write as traceWrite} from "../../trace";
13+
import {PercentLength} from "../styling/style-properties";
1414

1515
export { Color, traceEnabled, traceWrite, traceCategories };
1616
export { AnimationPromise } from ".";
1717

1818
export module Properties {
19-
export var opacity = "opacity";
20-
export var backgroundColor = "backgroundColor";
21-
export var translate = "translate";
22-
export var rotate = "rotate";
23-
export var scale = "scale";
19+
export const opacity = "opacity";
20+
export const backgroundColor = "backgroundColor";
21+
export const translate = "translate";
22+
export const rotate = "rotate";
23+
export const scale = "scale";
24+
export const height = "height";
25+
export const width = "width";
2426
}
2527

2628
export interface PropertyAnimation {
@@ -163,6 +165,9 @@ export abstract class AnimationBase implements AnimationBaseDefinition {
163165
throw new Error(`Property ${item} must be valid Pair. Value: ${animationDefinition[item]}`);
164166
} else if (item === Properties.backgroundColor && !Color.isValid(animationDefinition.backgroundColor)) {
165167
throw new Error(`Property ${item} must be valid color. Value: ${animationDefinition[item]}`);
168+
} else if (item === Properties.width || item === Properties.height) {
169+
// parse will throw if it sees an invalid value
170+
PercentLength.parse(animationDefinition[item]);
166171
}
167172
}
168173

@@ -234,8 +239,34 @@ export abstract class AnimationBase implements AnimationBaseDefinition {
234239
});
235240
}
236241

242+
// height
243+
if (animationDefinition.height !== undefined) {
244+
propertyAnimations.push({
245+
target: animationDefinition.target,
246+
property: Properties.height,
247+
value: animationDefinition.height,
248+
duration: animationDefinition.duration,
249+
delay: animationDefinition.delay,
250+
iterations: animationDefinition.iterations,
251+
curve: animationDefinition.curve
252+
});
253+
}
254+
255+
// width
256+
if (animationDefinition.width !== undefined) {
257+
propertyAnimations.push({
258+
target: animationDefinition.target,
259+
property: Properties.width,
260+
value: animationDefinition.width,
261+
duration: animationDefinition.duration,
262+
delay: animationDefinition.delay,
263+
iterations: animationDefinition.iterations,
264+
curve: animationDefinition.curve
265+
});
266+
}
267+
237268
if (propertyAnimations.length === 0) {
238-
throw new Error("No animation property specified.");
269+
throw new Error('No known animation properties specified');
239270
}
240271

241272
return propertyAnimations;
@@ -252,4 +283,4 @@ export abstract class AnimationBase implements AnimationBaseDefinition {
252283
curve: animation.curve
253284
});
254285
}
255-
}
286+
}

‎tns-core-modules/ui/animation/animation.android.ts‎

Lines changed: 106 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,35 @@
11
// Definitions.
2-
import { AnimationDefinition } from ".";
3-
import { View } from "../core/view";
2+
import {AnimationDefinition} from '.';
3+
import {View} from '../core/view';
44

5-
import { AnimationBase, Properties, PropertyAnimation, CubicBezierAnimationCurve, AnimationPromise, Color, traceWrite, traceEnabled, traceCategories } from "./animation-common";
65
import {
7-
opacityProperty, backgroundColorProperty, rotateProperty,
8-
translateXProperty, translateYProperty, scaleXProperty, scaleYProperty
9-
} from "../styling/style-properties";
10-
11-
import { layout } from "../../utils/utils";
12-
import lazy from "../../utils/lazy";
6+
AnimationBase,
7+
AnimationPromise,
8+
Color,
9+
CubicBezierAnimationCurve,
10+
Properties,
11+
PropertyAnimation,
12+
traceCategories,
13+
traceEnabled,
14+
traceWrite
15+
} from './animation-common';
16+
import {
17+
backgroundColorProperty,
18+
heightProperty,
19+
opacityProperty,
20+
PercentLength,
21+
rotateProperty,
22+
scaleXProperty,
23+
scaleYProperty,
24+
translateXProperty,
25+
translateYProperty,
26+
widthProperty
27+
} from '../styling/style-properties';
28+
29+
import {layout} from '../../utils/utils';
30+
import lazy from '../../utils/lazy';
31+
32+
import * as platform from '../../platform';
1333

1434
export * from "./animation-common";
1535

@@ -37,6 +57,8 @@ propertyKeys[Properties.opacity] = Symbol(keyPrefix + Properties.opacity);
3757
propertyKeys[Properties.rotate] = Symbol(keyPrefix + Properties.rotate);
3858
propertyKeys[Properties.scale] = Symbol(keyPrefix + Properties.scale);
3959
propertyKeys[Properties.translate] = Symbol(keyPrefix + Properties.translate);
60+
propertyKeys[Properties.height] = Symbol(keyPrefix + Properties.height);
61+
propertyKeys[Properties.width] = Symbol(keyPrefix + Properties.width);
4062

4163
export function _resolveAnimationCurve(curve: string | CubicBezierAnimationCurve | android.view.animation.Interpolator | android.view.animation.LinearInterpolator): android.view.animation.Interpolator {
4264
switch (curve) {
@@ -195,7 +217,7 @@ export class Animation extends AnimationBase {
195217
}
196218
}
197219

198-
private _onAndroidAnimationCancel() { // tslint:disable-line
220+
private _onAndroidAnimationCancel() { // tslint:disable-line
199221
this._propertyResetCallbacks.forEach(v => v());
200222
this._rejectAnimationFinishedPromise();
201223

@@ -301,7 +323,7 @@ export class Animation extends AnimationBase {
301323
} else {
302324
propertyAnimation.target.style[backgroundColorProperty.keyframe] = originalValue1;
303325
}
304-
326+
305327
if (propertyAnimation.target.nativeViewProtected && propertyAnimation.target[backgroundColorProperty.setNative]) {
306328
propertyAnimation.target[backgroundColorProperty.setNative](propertyAnimation.target.style.backgroundColor);
307329
}
@@ -414,13 +436,85 @@ export class Animation extends AnimationBase {
414436
} else {
415437
propertyAnimation.target.style[rotateProperty.keyframe] = originalValue1;
416438
}
417-
439+
418440
if (propertyAnimation.target.nativeViewProtected) {
419441
propertyAnimation.target[rotateProperty.setNative](propertyAnimation.target.style.rotate);
420442
}
421443
}));
422444
animators.push(android.animation.ObjectAnimator.ofFloat(nativeView, "rotation", nativeArray));
423445
break;
446+
case Properties.height:
447+
heightProperty._initDefaultNativeValue(style);
448+
nativeArray = Array.create("float", 2);
449+
let toValue = propertyAnimation.value;
450+
let parent = propertyAnimation.target.parent as View;
451+
if (!parent) {
452+
throw new Error('cannot animate height on root view');
453+
}
454+
const parentHeight: number = parent.getMeasuredHeight();
455+
toValue = PercentLength.toDevicePixels(toValue, parentHeight, parentHeight) / platform.screen.mainScreen.scale;
456+
let fromValue = originalValue1 = nativeView.getHeight() / platform.screen.mainScreen.scale;
457+
nativeArray[0] = fromValue;
458+
nativeArray[1] = toValue;
459+
let heightAnimator = android.animation.ValueAnimator.ofFloat(nativeArray);
460+
heightAnimator.addUpdateListener(new android.animation.ValueAnimator.AnimatorUpdateListener({
461+
onAnimationUpdate(animator: android.animation.ValueAnimator) {
462+
let argb = (<java.lang.Float>animator.getAnimatedValue()).floatValue();
463+
propertyAnimation.target.style[setLocal ? heightProperty.name : heightProperty.keyframe] = argb;
464+
}
465+
}));
466+
propertyUpdateCallbacks.push(checkAnimation(() => {
467+
propertyAnimation.target.style[setLocal ? heightProperty.name : heightProperty.keyframe] = propertyAnimation.value;
468+
}));
469+
propertyResetCallbacks.push(checkAnimation(() => {
470+
if (setLocal) {
471+
propertyAnimation.target.style[heightProperty.name] = originalValue1;
472+
} else {
473+
propertyAnimation.target.style[heightProperty.keyframe] = originalValue1;
474+
}
475+
476+
if (propertyAnimation.target.nativeViewProtected) {
477+
propertyAnimation.target[heightProperty.setNative](propertyAnimation.target.style.height);
478+
}
479+
}));
480+
animators.push(heightAnimator);
481+
break;
482+
483+
case Properties.width:
484+
widthProperty._initDefaultNativeValue(style);
485+
nativeArray = Array.create("float", 2);
486+
let toWidthValue = propertyAnimation.value;
487+
let widthParent = propertyAnimation.target.parent as View;
488+
if (!widthParent) {
489+
throw new Error('cannot animate width on root view');
490+
}
491+
const parentWidth: number = widthParent.getMeasuredWidth();
492+
toWidthValue = PercentLength.toDevicePixels(toWidthValue, parentWidth, parentWidth) / platform.screen.mainScreen.scale;
493+
originalValue1 = nativeArray[0] = nativeView.getWidth() / platform.screen.mainScreen.scale;
494+
nativeArray[1] = toWidthValue;
495+
let widthAnimator = android.animation.ValueAnimator.ofFloat(nativeArray);
496+
widthAnimator.addUpdateListener(new android.animation.ValueAnimator.AnimatorUpdateListener({
497+
onAnimationUpdate(animator: android.animation.ValueAnimator) {
498+
let argb = (<java.lang.Float>animator.getAnimatedValue()).floatValue();
499+
propertyAnimation.target.style[setLocal ? widthProperty.name : widthProperty.keyframe] = argb;
500+
}
501+
}));
502+
propertyUpdateCallbacks.push(checkAnimation(() => {
503+
propertyAnimation.target.style[setLocal ? widthProperty.name : widthProperty.keyframe] = propertyAnimation.value;
504+
}));
505+
propertyResetCallbacks.push(checkAnimation(() => {
506+
if (setLocal) {
507+
propertyAnimation.target.style[widthProperty.name] = originalValue1;
508+
} else {
509+
propertyAnimation.target.style[widthProperty.keyframe] = originalValue1;
510+
}
511+
512+
if (propertyAnimation.target.nativeViewProtected) {
513+
propertyAnimation.target[widthProperty.setNative](propertyAnimation.target.style.width);
514+
}
515+
}));
516+
animators.push(widthAnimator);
517+
break;
424518

425519
default:
426520
throw new Error("Cannot animate " + propertyAnimation.property);

‎tns-core-modules/ui/animation/animation.d.ts‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
*/ /** */
44

55
import { View, Color } from "../core/view";
6+
import {PercentLength} from '../styling/style-properties';
67

78
/**
89
* Defines animation options for the View.animate method.
@@ -33,6 +34,16 @@ export interface AnimationDefinition {
3334
*/
3435
scale?: Pair;
3536

37+
/**
38+
* Animates the height of a view.
39+
*/
40+
height?: PercentLength;
41+
42+
/**
43+
* Animates the width of a view.
44+
*/
45+
width?: PercentLength;
46+
3647
/**
3748
* Animates the rotate affine transform of the view. Value should be a number specifying the rotation amount in degrees.
3849
*/

0 commit comments

Comments
 (0)

Back | FazBrowse Home | New Git URL