Friday 21 August 2015

Making a simple animated startup screen in nativescript

The app I am currently working is a database of about 1000 exercises for people with injuries and disabilities.

I wanted to have a start screen where the user could pick the language they want the app to run in and display some example exercises.

As the screen dimensions could be anything, I wrote some code to layout images to fit the entire screen using the absolute layout.

On top of this, I added the title banner and then I added a timer to randomly swap the location of two images every 200 milliseconds.

An example can be seen below.



If you wanted to, you could make the images move positions, rather than just swap positions using the new animation framework coming in version 1.3. See another example here.

XML

<Page xmlns="http://www.nativescript.org/tns.xsd" loaded="pageLoaded">
  <GridLayout columns="*" rows="*,auto" cssClass="page">
    <AbsoluteLayout row="0" column="0" id="absoluteLayout"/>
    <Image src="{{image}}" row="0" column="0" verticalAlignment="center"/>
    <GridLayout cssClass="footer" row="1" column="0" rows="auto" columns="50,*,50">
      <Image src="{{flag}}" cssClass="flagimage" col="0" row="0"/>
      <Label text="{{lang}}" tap="{{goDefault}}" col="1" row="0" textAlignment="left"/>
      <Image src="~/res/icons/white/ellipsis_white.png" cssClass="iconwhitesmall" tap="{{goOther}}" row="0" col="2"/>
    </GridLayout>
  </GridLayout>
</Page>

Page code

import observable = require("data/observable");
import pages = require("ui/page");
import model = require("./intro-view-model");
import absoluteLayout = require("ui/layouts/absolute-layout");

export function pageLoaded(args: observable.EventData) {
    var page = <pages.Page>args.object;
    page.bindingContext = model.viewModel;
    model.viewModel.placeImages(<absoluteLayout.AbsoluteLayout>page.getViewById("absoluteLayout"));
}

Model code

/* tslint:disable:use-strict triple-equals max-line-length one-line */

import observableHelper = require("../../helpers/observablehelper");
import applicationSettingsHelper = require("../../helpers/applicationsettingshelper");
import languageOption = require("../../res/data/languageoption");
import frames = require("ui/frame");
import recordDevice = require("../../helpers/recorddevice");
import absoluteLayout = require("ui/layouts/absolute-layout");
import platform = require("platform");
import image = require("ui/image");

var languageOptions: Array<languageOption.LanguageOption> = languageOption.data();

enum fields { image, lang, flag };

interface IPosition {
    left: number;
    top: number;
}

export class Model extends observableHelper.BaseModel<fields> {
    constructor() {
        super(fields);

        this.setValue(fields.image, "~/res/banner/banner.png");
        var languageChosen = applicationSettingsHelper.getLanguage();

        this.setValue(fields.lang, languageChosen);

        var flag = "";
        for (var i = 0, imax = languageOptions.length; i < imax; i++) {
            if (languageOptions[i].Lang == languageChosen) {
                flag = "~/res/flags/" + languageOptions[i].Flag.toLowerCase() + ".png";
                break;
            }
        }

        this.setValue(fields.flag, flag);

        recordDevice.recordDevice();
    }

    // scaling for images

    scale = 1;

    // image store

    imagesArray: Array<image.Image> = [];

    // determine screen dimensions and place images in absolute layout to fill the entire screen
    // then set the images source and then randomly swap two image locations every 200 milliseconds

    placeImages(layout: absoluteLayout.AbsoluteLayout) {
        var deviceWidth = platform.screen.mainScreen.widthPixels / platform.screen.mainScreen.scale,
            deviceHeight = platform.screen.mainScreen.heightPixels / platform.screen.mainScreen.scale,
            doExit = false,
            leftPos = 0,
            topPos = 0,
            imageHeight = 49 * this.scale,
            imageWidth = 40 * this.scale;

        // add images until the screen is completely full

        while (!doExit) {
            var imageAdd = new image.Image();
            imageAdd.width = imageWidth;
            imageAdd.height = imageHeight;
            absoluteLayout.AbsoluteLayout.setLeft(imageAdd, leftPos);
            absoluteLayout.AbsoluteLayout.setTop(imageAdd, topPos);
            layout.addChild(imageAdd);

            leftPos = leftPos + imageWidth + 2;

            if (leftPos > deviceWidth) {
                topPos = topPos + imageHeight + 2;
                leftPos = 0;
            }

            if (topPos > deviceHeight) {
                doExit = true;
            }

            this.imagesArray.push(imageAdd);
        }

        // set images

        this.setImages();

        // swap images on timer

        setTimeout(() => {
            this.setPositions();
        }, 200);
    }

    // Randomly pick two images and swap their locations

    setPositions() {

        // Pick two images randomly

        var oldPosition = Math.round(Math.random() * (this.imagesArray.length - 1));
        var newPosition = Math.round(Math.random() * (this.imagesArray.length - 1));

        // swap Locations

        var imageAdd = this.imagesArray[oldPosition];
        var imageAddPos = { left: absoluteLayout.AbsoluteLayout.getLeft(imageAdd), top: absoluteLayout.AbsoluteLayout.getTop(imageAdd) };

        var imageAdd2 = this.imagesArray[newPosition];
        var imageAdd2Pos = { left: absoluteLayout.AbsoluteLayout.getLeft(imageAdd2), top: absoluteLayout.AbsoluteLayout.getTop(imageAdd2) };

        absoluteLayout.AbsoluteLayout.setLeft(imageAdd, imageAdd2Pos.left);
        absoluteLayout.AbsoluteLayout.setTop(imageAdd, imageAdd2Pos.top);

        absoluteLayout.AbsoluteLayout.setLeft(imageAdd2, imageAddPos.left);
        absoluteLayout.AbsoluteLayout.setTop(imageAdd2, imageAddPos.top);

        setTimeout(() => {
            this.setPositions();
        }, 200);
    }

    // loop through images and set the image source, 90 "interesting" images to pick from

    setImages() {
        var imageCounter = 0;

        for (var i = 0, imax = this.imagesArray.length; i < imax; i++) {
            if (imageCounter == 90) {
                imageCounter = 1;
            }
            else {
                imageCounter = imageCounter + 1;
            }

            this.imagesArray[i].src = "~/res/banner/" + imageCounter + ".jpg";
        }
    }

    goDefault() {
        frames.topmost().navigate({
            moduleName: "./views/main/main-page"
        });
    }

    goOther() {
        frames.topmost().navigate({
            moduleName: "./views/language/language-page"
        });
    }
}

export var viewModel = new Model();

No comments:

Post a Comment