Active Frame made easy

One of the coolest thing in BB10 is the possibility for an app to display informations in it’s Active Frame when the app is minimize. If you’re building your first app, you might want to add Active Frame support, but looking at the official documentation can discourage some of you because it looks hard to code: having to deal with C++, creating a custom class, inherit from SceneCover… For a new developer, this can look like an impossible task.

Fortunately, there’s an easy way to create an Active Frame using almost only QML, with only a few lines of code.

First, let’s register SceneCover and AbstractCover in C++ so that QML knows what we’re talking about. Add this at the top of your applicationUI.cpp file

#include <bb/cascades/SceneCover>
#include <bb/cascades/AbstractCover>

And then, in the same file, add this in your constructor

ApplicationUI::ApplicationUI() :
        QObject()
{
    qmlRegisterType<SceneCover>("bb.cascades", 1, 2, "SceneCover");
    qmlRegisterUncreatableType<AbstractCover>("bb.cascades", 1, 2, "AbstractCover", "");
...

Now, let’s go to QML.

import bb.cascades 1.2

Page {
    attachedObjects: [
        // An Active Frame is a Scene Cover
        SceneCover {
            id: sceneCover
            content: Container {
                Label {
                    id: label
                    textStyle.color: Color.Red
                    multiline: true
                }
            }
        }
    ]
    
    // Set the Scene Cover for this application on app start
    onCreationCompleted: Application.setCover(sceneCover)
    
    // This is your main app
    Container {
        TextArea {
            hintText: "Text shown in Active Frame"
            onTextChanging: label.text = text
        }
    }
}

That’s it! Easy, isn’t it?
Even updating the Active Frame is really easy with this method, you just need to call the Active Frame object you want to update by it’s id.

So, don’t be afraid anymore, Active Frames are fun to play with and give your app a professional look.

Cascades animations in games

This article is a follow-up of How to use Cascades built-in animations

Animations are perfect for simple games, so let’s create one right now. The game we’ll be building is called ‘No Fly Zone’ and consist of a fly flying around the screen that we need to kill. First, we’ll need a fly image, so I’ve found this one from http://www.openclipart.org :

Fly

 

We’ll separate this image in two parts : the body and wings. Save those 2 images in ‘assets/images/’ folder (you’ll need to create ‘images’ folder).

fly_wingsfly_body

To make the wings flap, we’ll put both images on top of each other and then set a small back and forth rotation transition to the wings. Let’s create a custom component for that fly and name it FlyImage.qml :

 

// FlyImage.qml
import bb.cascades 1.0

Container {
    id: flyContainer
    layout: DockLayout {}
    property int angleForWingsFlapping: 20

    maxHeight: 100
    maxWidth: maxHeight
    
    ImageView {
        imageSource: &amp;quot;asset:///images/fly_wings.png&amp;quot;
        onCreationCompleted: {
            wingsFlappingAnimation.play()
        }
        animations: [
            SequentialAnimation {
                id: wingsFlappingAnimation
                repeatCount: AnimationRepeatCount.Forever
                RotateTransition {
                    // Rotate wings clockwise slightly
                    toAngleZ: angleForWingsFlapping
                    duration: 2
                }
                RotateTransition {
                    // Rotate wings counterclockwise slightly
                    toAngleZ: 0 - angleForWingsFlapping
                    duration: 2
                }
            }
        ]
    }
    
    ImageView {
        id: flyBodyImage
        imageSource: &amp;quot;asset:///images/fly_body.png&amp;quot;
    }
}

Now, in main.qml, we can add this custom component by calling its name, and we’ll have a fly with flapping wings on the screen :

// main.qml
import bb.cascades 1.0

Page {
    content: Container {
        FlyImage {}
    }
}

Now that we have a fly with animated wings, let’s move that fly around the screen :

 

// main.qml
import bb.cascades 1.0

Page {
    id: mainPage
    
    // You may need to change deviceWidth and deviceHeight values depending on your device
    property int deviceWidth: 1440
    property int deviceHeight: 1440
    property int speed: 3000
    
    onCreationCompleted: {
        // Starts the animation when the app launch
        thisAnimation.play()
    }
    
    content: Container {
        layout: DockLayout {}
        background: Color.LightGray
        horizontalAlignment: HorizontalAlignment.Fill
        verticalAlignment: VerticalAlignment.Fill
        
        FlyImage {
            id: flyImageAlive
            
            maxHeight: 100
            maxWidth: maxHeight
            
            // Starts at the bottom of the screen                        
            translationX: Math.random() * deviceWidth 
            translationY: deviceHeight + 100
            
            animations: [
                ParallelAnimation {
                    id: thisAnimation
                    
                    onEnded: {
                        // This is what happens when one animation cycle is done
                        
                        // Recalculate new translation points
                        thisTranslation.toX = Math.random() * deviceWidth
                        thisTranslation.toY = Math.random() * deviceHeight
                        
                        // Recalculate new scale transition value
                        thisScale.toX = (Math.random() * 1.5) + 0.5
                        thisScale.toY = thisScale.toX
                        
                        // Restart the animation with the new values
                        thisAnimation.play()
                    }
                    
                    TranslateTransition {
                        // Move the fly toX and toY values
                        id: thisTranslation
                        duration: speed
                        easingCurve: StockCurve.Linear
                        toX: Math.random() * deviceWidth
                        toY: Math.random() * deviceHeight
                    }
                    
                    ScaleTransition {
                        // Rescale the fly randomly
                        id: thisScale
                        duration: speed
                        toX: (Math.random() * 1.5) + 0.5
                        toY: toX
                    }
                }
            ]
        }
    }
}

So now we have a fly flying randomly around the screen. Only thing left to do is handling tap event for when the user hit the fly and creating a score board, and there you have a simple game using Cascades built-in animations.

Complete source code is available on GitHub

You can find ‘No Fly Zone’ free game in BlackBerry World at :
http://appworld.blackberry.com/webstore/content/59946084

How to use Cascades built-in animations

Nowadays, there’s a lot of mobile app developers and your apps need to stand out to catch the user’s attention. Most users like to interact with the app, and animations can bring this interaction to another level, not only letting the user touch a component on the screen and have a reaction, but also having this component to move, scale, fade or rotate to create a real sense of interaction and fluidity.

Hopefully, Cascades makes it easy to use animations with only a few lines of code.

 

IMPLICIT AND EXPLICIT ANIMATIONS

Cascades support 2 types of animations, implicit and explicit animations. Implicit animations doesn’t need any coding and can be used on 2 types of properties :

  • Properties that determine how a control looks, such as rotation, translation, and opacity
  • Properties that determine the layout of a control in a container, such as preferred width and preferred height

So if you set the opacity of a Button to 0, it will fade out gradually instead of becoming invisible right away. Cascades takes care of the animation, you don’t have to write any line of code for this fade out animation.

Here’s some sample code to test implicit animations :

import bb.cascades 1.0

Page {
    
    content: Button {
        
        text: "Click me"
        
        onClicked: {
            
            translationX += 20
            
            opacity -= 0.2
            
            if (opacity == 0)
                
                opacity = 1
        
        }
    
    }

}

EXPLICIT ANIMATIONS : FADE, ROTATE, TRANSLATE, SCALE

If you want to have more control on the animation used, then you’ll want to look into explicit animations, where you can set from and to values, duration, easing curve, a delay before the animation start or number of repeat for your animation. Let’s take a fade transition, we want a Button to fade slowly from 1.0 to 0.0 and make it repeat forever :

import bb.cascades 1.0

Page {
    
    content: Container {
        
        Button {
            
            text: "Click me"
            
            animations: [
                
                FadeTransition {
                    
                    id: fadeTransition
                    
                    duration: 3000
                    
                    fromOpacity: 1.0
                    
                    toOpacity: 0.0
                    
                    repeatCount: AnimationRepeatCount.Forever
                
                }
            
            ]
            
            onClicked: {
                
                fadeTransition.play()
            
            }
        
        }
    
    }

}

So we have a better control of the animation with an explicit animation. There are 4 types of animations that can be controlled with explicit animation :

  • Fade transition
  • Rotate transition
  • Translate transition
  • Scale transition

 

GROUP ANIMATIONS

It’s even possible to group animations together and make them perform one after the other or all at the same time. Those are called SequentialAnimation and ParallelAnimation. Let’s try both to see the difference.

import bb.cascades 1.0

Page {
    content: Container {
        Button {
            text: "Click me"
            
            animations: [
                ParallelAnimation {
                    id: parallelAnimation
                    
                    animations: [
                        TranslateTransition {
                            toX: 400
                            duration: 2000
                        },
                        RotateTransition {
                            toAngleZ: 180
                            duration: 2000
                        }
                    ]
                }
            ]
            
            onClicked: {
                parallelAnimation.play();
            }
        }
        Button {
            text: "Click me"
            
            animations: [
                SequentialAnimation {
                    id: sequentialAnimation
                    
                    animations: [
                        TranslateTransition {
                            toX: 400
                            duration: 2000
                        },
                        RotateTransition {
                            toAngleZ: 180
                            duration: 2000
                        }
                    ]
                }
            ]
            
            onClicked: {
                sequentialAnimation.play();
            }
        }
    }
}

For now, all the animations used were a little bit useless, but they showed possibilities. For a real life example, let’s say we have a freemium app and we want to invite the user to upgrade to Pro version, we can do it like this :

import bb.cascades 1.0

Page {
    property int deviceWidth: 1440 // Set it to your screen width
    property int deviceHeight: 1440 // Set it to your screen height
    content: Container {
        layout: DockLayout {}
        horizontalAlignment: HorizontalAlignment.Fill
        verticalAlignment: VerticalAlignment.Fill
        Container {
            id: mainAppContainer
            Label {
                text: "This is your main app"
            }
            Container {
                topPadding: 30
                Button {
                    text: "Random action that prompt user to upgrade"
                    onClicked: upgradeContainer.visible = true
                }
            }
        }
        Container {
            id: upgradeContainer
            background: Color.Black
            horizontalAlignment: HorizontalAlignment.Fill
            verticalAlignment: VerticalAlignment.Fill
            opacity: 0.7
            visible: false
            onVisibleChanged: {
                if (visible) {
                    upgradeButton.translationX = deviceWidth // Make it out of sight
                    upgradeButton.translationY = deviceHeight // Make it out of sight
                    noThanksButton.translationX = deviceWidth // Make it out of sight
                    noThanksButton.translationY = deviceHeight // Make it out of sight
                    
                    upgradeButtonAnimation.play()
                    noThanksButtonAnimation.play()
                } 
            }
            Button {
                id: upgradeButton
                text: "Upgrade"
                preferredWidth: 300
                preferredHeight: 30
                onClicked: upgradeContainer.visible = false
                animations: [
                    ParallelAnimation {
                        id: upgradeButtonAnimation
                        RotateTransition {
                            fromAngleZ: 160
                            toAngleZ: 0
                            duration: 1500
                            easingCurve: StockCurve.ElasticOut
                        }
                        TranslateTransition {
                            fromX: deviceWidth * 0.75
                            fromY: deviceHeight
                            toX: (deviceWidth / 2) - (upgradeButton.preferredWidth / 2)
                            toY: (deviceHeight / 2) - (upgradeButton.preferredHeight / 2)
                            duration: 1500
                            easingCurve: StockCurve.ElasticOut 
                        }
                    }
                ]
            }
            Button {
                id: noThanksButton
                text: "No Thanks"
                preferredWidth: 300
                preferredHeight: 30
                onClicked: upgradeContainer.visible = false
                animations: [
                    ParallelAnimation {
                        id: noThanksButtonAnimation
                        delay: 4000 // Setting a delay will make this button appear later
                        RotateTransition {
                            fromAngleZ: 160
                            toAngleZ: 0
                            duration: 1500
                            easingCurve: StockCurve.ElasticOut
                        }
                        TranslateTransition {
                            fromX: deviceWidth * 0.75
                            fromY: deviceHeight
                            toX: (deviceWidth / 2) - (noThanksButton.preferredWidth / 2)
                            toY: (deviceHeight / 2) - (noThanksButton.preferredHeight / 2) + upgradeButton.preferredHeight
                            duration: 1500
                            easingCurve: StockCurve.ElasticOut
                        }
                    }
                ]
            }
        }
    }
}

What’s happening here is when a certain action is performed by the user, it shows a semi-transparent black screen (opacity 0.7) and then 2 buttons comes from out of sight to the middle of the screen, an ‘Upgrade’ button and a ‘No Thanks’ button. User have to click either one to continue to use the app.

Animations can be a lot of fun in Cascades, it’s powerful, simple to code and look nice. It allows your app to stand out from the crowd, so go ahead, and play with it!

Reference : https://developer.blackberry.com/native/documentation/cascades/ui/animations/index.html