Skip to main content
Version: 6.17.1-beta.3

Animator

API Reference: UI.Animator

Animator is used to change the appearance of the UI objects with animation.

note

The components in the example are added from the code for better showcase purposes. To learn more about the subject you can refer to:

Adding Component From Code

As a best practice, Smartface recommends using the WYSIWYG editor in order to add components and styles to your page or library. To learn how to use UI Editor better, please refer to this documentation

UI Editor Basics
import PageSampleDesign from "generated/pages/pageSample";
import Application from "@smartface/native/application";
import Color from "@smartface/native/ui/color";
import View from "@smartface/native/ui/view";
import System from "@smartface/native/device/system";
import Button from "@smartface/native/ui/button";
import Animator from "@smartface/native/ui/animator";
import { withDismissAndBackButton } from "@smartface/mixins";
import { Route, Router } from "@smartface/router";
import { styleableComponentMixin } from "@smartface/styling-context";

class StyleableButton extends styleableComponentMixin(Button) {}
class StyleableView extends styleableComponentMixin(View) {}


//You should create new Page from UI-Editor and extend with it.
export default class AnimatorSample extends withDismissAndBackButton(PageSampleDesign) {
myView: StyleableView;
myButton: StyleableButton;

constructor(private router?: Router, private route?: Route) {
super({});
}

// The page design has been made from the code for better
// showcase purposes. As a best practice, remove this and
// use WYSIWYG editor to style your pages.
centerizeTheChildrenLayout() {
this.dispatch({
type: "updateUserStyle",
userStyle: {
flexProps: {
flexDirection: 'ROW',
justifyContent: 'CENTER',
alignItems: 'CENTER'
}
}
})
}

onShow() {
super.onShow();
const { headerBar } = System.OS === System.OSType.ANDROID ? this : this.parentController;
Application.statusBar.visible = false;
headerBar.visible = false;
}

onLoad() {
super.onLoad();
this.centerizeTheChildrenLayout();

this.myView = new StyleableView();
this.myButton = new StyleableButton({
text: "Animate",
onPress: (): void => {
this.myView.backgroundColor = Color.RED;
let animationRootView =
System.OS === System.OSType.IOS ? this.layout : this.myView.getParent();

Animator.animate(animationRootView, 5000, (): void => {
this.myView.left = 150;
this.myView.right = 150;
})
.then(2500, (): void => {
this.myView.left = 10;
this.myView.right = 10;
})
.complete((): void => {
this.myView.backgroundColor = Color.GREEN;
});
},
});

this.addChild(this.myView, "myView", ".sf-view", {
left: 10,
top: 10,
right: 10,
height: 100,
flexProps: {
positionType: "ABSOLUTE",
},
backgroundColor: "#008000",
});

this.addChild(this.myButton, "myButton", ".sf-button", {
left: 10,
top: 150,
right: 10,
height: 65,
flexProps: {
positionType: "ABSOLUTE",
},
backgroundColor: "#808080",
});
}
}
caution

On iOS side, Animator is not working smoothly if it is applied to button object. We suggest using other UI elements for animations.

Animation Root View

The animation is happening within the Animation Root View. It is similar, but not exactly, during the animation, FlexLayout.applyLayout() is called to the root object. It is important to pick the correct root.

Best practices for selecting the perfect root

  • On Android just pick the parent view
  • On iOS, pick the most parent object. If the animating view is a direct descendant of the page, then select the page.layout or the view is within a ScrollView, then select the scrollView.layout. The rule is simple, traverse to all the way to the parents, if there is no parent, use that view. So page.layout, scrollView.layout do not have any parents.

Limitations of the root view

A root view can handle one animation at a time. It is not possible to use different animate functions and providing same root view most likely to cause exception

View Animations

Views on the screen need to be repositioned. It is possible to animate the scale, position, rotation, and alpha of any type of View with View animations.

Moving Into Header Bar

View can have position on HeaderBar by means of Dialog and getScreenLocation() function for a View or a HeaderBarItem.

Using Dialog on Android and iOS with differences

Android runs asynchronous on onShow() function of Dialog, so it must be animated onShow() function of Page. Dialog BackgroundColor must be set transparent on iOS.

note

The components in the example are added from the code for better showcase purposes. To learn more about the subject you can refer to:

Adding Component From Code

As a best practice, Smartface recommends using the WYSIWYG editor in order to add components and styles to your page or library. To learn how to use UI Editor better, please refer to this documentation

UI Editor Basics
import PageSampleDesign from "generated/pages/pageSample";
import Application from "@smartface/native/application";
import Color from "@smartface/native/ui/color";
import View from "@smartface/native/ui/view";
import System from "@smartface/native/device/system";
import Button from "@smartface/native/ui/button";
import Animator from "@smartface/native/ui/animator";
import ImageView from "@smartface/native/ui/imageview";
import HeaderBarItem from "@smartface/native/ui/headerbaritem";
import Dialog from "@smartface/native/ui/dialog";
import { withDismissAndBackButton } from "@smartface/mixins";
import { Route, Router } from "@smartface/router";
import { styleableComponentMixin } from "@smartface/styling-context";

class StyleableButton extends styleableComponentMixin(Button) {}
class StyleableImageView extends styleableComponentMixin(ImageView) {}


//You should create new Page from UI-Editor and extend with it.
export default class AnimatorSample extends withDismissAndBackButton(PageSampleDesign) {
myFlProd: StyleableImageView;
myButton: StyleableButton;
myFlCart: StyleableImageView;
headerBarItem: HeaderBarItem;
myDialog: Dialog;
animationRootView: View;

constructor(private router?: Router, private route?: Route) {
super({});
}

// The page design has been made from the code for better
// showcase purposes. As a best practice, remove this and
// use WYSIWYG editor to style your pages.
centerizeTheChildrenLayout() {
this.dispatch({
type: "updateUserStyle",
userStyle: {
flexProps: {
flexDirection: 'ROW',
justifyContent: 'CENTER',
alignItems: 'CENTER'
}
}
})
}

onShow() {
super.onShow();
const { headerBar } = System.OS === System.OSType.ANDROID ? this : this.parentController;
Application.statusBar.visible = true;
headerBar.visible = true;

this.myButton.onPress = (): void => {
this.myDialog.show();
if (System.OS === System.OSType.IOS) {
this.myDialog.layout.backgroundColor = Color.TRANSPARENT;
this.animate();
}
};
if (System.OS === System.OSType.ANDROID) {
this.animate();
}
}

animate(): void {
let itemPos = this.headerBarItem.getScreenLocation();
let itemSize = this.headerBarItem.size;
//@ts-ignore
let animationRootView =
System.OS === System.OSType.IOS ? this.myDialog.layout : this.myFlProd.getParent();
Animator.animate(animationRootView, 1200, (): void => {
this.myFlProd.top =
itemPos.y +
itemSize.height / 2 -
this.myFlProd.height / 2 -
(System.OS === System.OSType.ANDROID ? Application.statusBar.height : 0);
this.myFlProd.left =
itemPos.x + itemSize.width / 2 - this.myFlProd.width / 2;
this.myFlProd.scale = { x: 0.01, y: 0.01 };
}).complete((): void => {
this.myFlProd.visible = false;
this.myDialog.hide();
this.headerBarItem.badge.text = "1";
});
}

onLoad() {
super.onLoad();
this.centerizeTheChildrenLayout();

this.myButton = new StyleableButton({
text: "Add To Cart",
});
this.myFlProd = new StyleableImageView({
image: "images://bird.png", // Don't forget to generate your bird image
});
this.myFlCart = new StyleableImageView({
image: "images://bird.png",
});

this.headerBarItem = new HeaderBarItem({
title: "Cart",
color: Color.BLACK,
});
this.headerBar.setItems([this.headerBarItem]);

this.myDialog = new Dialog();
this.myDialog.android.isTransparent = true;
this.myDialog.layout.addChild(this.myFlProd);

this.animationRootView =
System.OS === System.OSType.IOS ? this.myDialog.layout : this.myFlProd.getParent();
this.myDialog.layout.applyLayout();

this.addChild(this.myButton, "myButton", ".sf-button", {
width: 150,
height: 70,
backgroundColor: "#57A5CF",
top: 100,
left: 100,
});

this.addChild(this.myFlCart, "myFlCart", ".sf-imageView", {
width: 200,
height: 200,
top: 110,
left: 80,
});
}
}

Non-practical animations

There are several scenarios that animate can cause problems or may not work as intended.

ListView

Do not animate anything within the ListViewItem. ListView is re-using the objects. In that case, row rendering operations will conflict with the ongoing animations.

Non-size & Non-position

Animation works best with sizes and position affecting scenarios. Animating other properties might not result as expected (such as colors, texts)

SwipeView page change animations

Moving the page indicator with animation in a SwipeView to make a carousel display, can cause problems. The user is going to swipe the pages again before the animation is complete. In that case, another animation is about to start with using same root view.