Ø Event Triggers
Ø Named Resources
Ø Settings Page
Ø Color Picker
Silly Eye is a very eye-catching application, especially among a group of children. The application will show a huge cartoon eyeball in a fun and almost crazy way. To show the application, just place your phone in front of your right eye and pretend it is your right eye. This application introduces some useful new technologies and is related to creating new pages and selecting user-defined colors. But most importantly, this is the first application related to "Animations" in the topic "Transforms & Animations" in the second part of this book.
When most people see an animation, they think it is something similar to a cartoon, which uses continuous and rapid playback of pictures to simulate motion effects. In Silverlight, animation has a more detailed definition: changing the value of a property on the timeline. This is related to movement, for example, by increasing the width of an element to create a growth effect, or changing the opacity of an element to create a completely different effect.
There are many ways to change the value of an attribute on the timeline. The most classic method is to use a timer, such as the DispatcherTimer used in many previous chapters, and a periodic callback method (Tick event processing) based on the timer trigger time. In this method, we can manually update the target attribute (that is, based on the elapsed time, through mathematical calculations to determine the current attribute value), until the final set value. At this time, we can stop the timer and/or delete the event handler.
However, Silverlight provides an easier-to-use animation mechanism, which is more powerful and has better performance than the timer method. This is a mechanism centered on Storyboard objects. Storyboard contains one or more specific animation objects, which are applied to specific attributes of specific elements.
Silly Eye uses three Storyboards to achieve animation. In order to understand the concept and working principle of Storyboard, let's learn the following three contents:
➔ Storyboard related to pupils
➔ Storyboard related to iris
➔ Storyboard related to eyelids
The Pupil Storyboard
Below is the Silly Eye application, applying Storyboard to the pupil to make it expand and contract repeatedly. The points of attention are as follows:
➔ Storyboard.TargetName, an attachable property, indicates that the animation is applied to an element named Pupil on this page, and Pupil is an ellipse.
➔ Storyboard.TargetProperty, an attachable property, indicates that Pupil’s StrokeThickness property has been animated.
➔ The DoubleAnimation in the Storyboard shows that the value of StrokeThickness will change from 100 to 70 within half a second. The "Double" in DoubleAnimation represents the type of the target attribute (because the StrokeThickness type is double).
➔ Because AutoReverse is set to true, when StrokeThickness reaches 70, it will automatically change from 70 to 100. Since RepeatBehavior is set to Forever, this repeated action will continue as long as the application is started.
➔ The EasingFunction property (set to an instance of ElasticEase) controls how the StrokeThickness value is rewritten on the timeline. The content of this part will be introduced in the "Interpolation" section below.
In the beginning of the animation, the Begin method of the storyboard is called as follows:
The effect of this animation in the entire application is shown in Figure 12.2. The elliptical element of Pupil is assigned a bright blue brush in the code behind.
Figure 12.2 PupilStoryboard reduces the thickness of Pupil's stroke (blue part) from 100 to 70, thus making the black fill larger.
In XAML, there is a way to trigger all actions of the storyboard. Therefore, we do not need to call its Begin method in the code behind. We can add an event trigger to the Triggers property of an element. Thanks to this special BeginStoryboard element, in the Loaded event of the grid, it will internally call the Begin method of the storyboard. The Loaded event is the only event supported in Silverlight event triggering.
Types of Animations
Silverlight provides animation classes to implement four different data types: double, Color, Point and Object. In the Silly Eye application, only the double attribute is used. The remaining types are introduced in Chapter 15 "Mood Ring". If we want to change the attribute value of the double type in the element on the timeline (such as Width, Height, Opacity, Canvas.Left, etc.), we can use an instance of DoubleAnimation. If we want to change the attribute value of the Point type in the element on the timeline (such as the StartPoint and EndPoint attributes of a linear gradient brush), we can use an instance of PointAnimation. So far, since a large number of double type properties require animation effects, DoubleAnimation is the most commonly used animation class.
Not all properties can be animated, even if its data type matches the data type used by the animation class! The animation mechanisms discussed in this chapter can only be used by types called "dependency properties". Fortunately, the attributes in most visual elements are of this type. The content of dependency properties will be discussed in Chapter 18 "Cocktails". Similar to judging whether an event is a "routed event" (routed event), we can determine whether the property is a dependency property by checking a static field of type DependencyProperty named PropertyNameProperty contained in the class. If the class contains such a field, such as the StrokeThicknessProperty field in the ellipse class, then it is a dependency property.
In order to achieve the desired effect, think about which attributes of an element to animate, which needs to be determined through some experiments. For example, if we want an element to appear in a gradient, then it doesn't make any sense to animate its Visibility property, because there is no intermediate value between Collapsed and Visible. Instead, we should animate its Opacity property, and its value is of type double, ranging from 0 to 1.
It is important to recognize the following: By default, DoubleAnimation uses linear interpolation on the timeline to smoothly change the attribute value of the double type. In other words, within one second, if the value increases from 50 to 100, then, at the moment of 0.1 second, its value is 55 (time increases by 10%, and its value also linearly increases by 10%), at 0.5 At the moment of second, its value is 75 (time increases by 50%, and its value increases linearly by 50%), and so on. This is why the median value of the StrokeThickness property is 85 in Figure 12.2.
However, most of the animations used in Windows Phone applications are non-linear. Moreover, they are more inclined to change from one value to another by sudden acceleration or sudden deceleration. This approach makes the animation fun. We can achieve this non-linear animation effect by applying a easing function.
This transition function is responsible for the custom interpolation of the attribute value from the start to the final value. Pupil Storyboard uses a function called ElasticEase to achieve this behavior. Figure 12.3 shows the difference between using the default linear transformation and the elastic transformation when the attribute value is reduced from 100 to 70. In this case, the intermediate value of 85 is not reached at the intermediate point in time, it is actually reached when it is closer to the end.
Figure 12.3 The ElasticEase transition function greatly changes the way the double type value is reduced from 100 to 70.
Silverlight provides 11 different transition functions, each of which has three different modes, and some functions provide a deeper level of property behavior customization. For example, ElasticEase has Oscillations and Springiness properties, which are set to 3 by default. In practical applications, if we want to add custom functions to the animation, the possibilities for this custom behavior are endless. Compared with the default linear transition, the transition function used in this application provides a completely different experience for the user.
Appendix D "Animation Easing Reference" shows the behavior of each built-in transition function. I find these functions very useful, because every time I want to design a new animation, I will refer back to these functions.
Another way to produce a non-linear animation The transition function is not the only way to produce a non-linear transition animation effect. The Keyframe animations discussed in Chapter 14 "Love Meter" allow us to use different difference methods to decompose an animation into multiple fragments.
The Iris Storyboard
The Silly Eye application applies the following Storyboard to a canvas control called Iris, which makes the eyeballs appear to move left and right. The points of attention are as follows:
➔ The syntax of TargetProperty is slightly more complicated than its name. When it is set to an attachable property (such as Canvas.Left), it must be enclosed in parentheses.
➔ The animation uses a different transition function to make the boundary of its motion more obvious. For the behavior of BounceEase, please refer to Appendix D.
➔ The animation is missing the From value! This is feasible, and we recommend this treatment. When the From value is not specified, the animation starts from the current value of the target property, regardless of the value. Similarly, an animation can specify the From value, but not the To value! In this case, the animation starts from the specified value of the property and ends at the current value.
In order to achieve a smooth effect, it is important to set From as the default, especially if the animation starts with a repeatable user input. For example, when the user clicks on an interface element, it starts its growth animation, then each quick click will quickly change its size back to the value set by From. However, with the default From value setting, subsequent clicks will also start the animation from the current value, making the animation more smooth and natural.
In the case where the target attribute value cannot be interpolated, we must specify the values of From and To! If we try to animate the width or height of an auto-sized element, and its From and To are not specified, then the animation effect will not appear. When the width or height of an element is set to Double.NaN (not a number), its size is adaptive. Because when there is a non-numeric number in the two values, DoubleAnimation cannot complete the interpolation operation. Moreover, applying animation to ActualWidth or ActualHeight (they are set to the actual width or height value, not NaN), this is not a good choice. Because these properties are read-only and are not dependency properties. On the contrary, in order to have an animation effect, we must explicitly set the width/height of the target element.
For Pupil Storyboard, we must call the Begin method of Storyboard to make it work.
The effect of the animation is shown in Figure 12.4. Iris Canvas contains Pupil ellipse (in fact, its periphery is Iris), but also includes two other Ellipse, they add "gloss" to Iris. Because the animation effect is added to the position of the Canvas control, all the visual elements it contains will move together.
Figure 12.4 IrisStoryboard moves Iris Canvas horizontally, and its value increases from 287 (its initial Canvas.Left value) to 543.
In the animation effect class, there is also a field named By, which can be used to replace the To field. The following animation means "on the basis of the current property value, increase by 256":
<DoubleAnimation By=”256” Duration=”0:0:2”/>
When reducing the current value, a negative value is also supported.
The Eyelid Animation
The last storyboard used in the Silly Eye application is used to imitate the blinking effect of the Eyelid Ellipse with skin color. For Pupil Ellipse, the skin color brush is set in the code behind. The points of attention are as follows:
➔ The two properties of Storyboard.TargetName and Storyboard.TargetProperty are set to attachable because they can be used in a separate animation without having to worry about any settings in Storyboard. This Storyboard targets the Height and Canvas.Top properties in Eyelid Ellipse. Therefore, the name of a single target is marked on the Storyboard, but multiple different target attributes are used to mark multiple different animation effects.
➔ Canvas.Top and Height have synchronized animation effects, so the ellipse keeps in the middle position while shrinking vertically. "Transforms" introduced in the next chapter provides a faster way to achieve this effect.
➔ Both animations use the default linear interpolation method. They move so fast that there is no need to try other more vital methods.
➔ Storyboard is not just a simple container used to animate related objects. The Storyboard has its own continuous behavior and repetitive behavior! These two animations only lasted 0.2 seconds (the current value of the property was reduced from 380 to 50 within 0.1 second, and within 0.1 seconds, the property value was changed back to due to its auto-reverse setting Original value). However, because the duration of the Storyboard is 3 seconds, and it has an auto-reverse setting (not its sub-elements), the animation will remain motionless in the later time, knowing that the last 3 seconds are exhausted. Then, the action that lasted for 0.2 seconds started again, followed by a static time of 2.8 seconds. Therefore, the Storyboard makes the eyes blink very fast, and it happens once in 3 seconds.
The effect of this animation is shown in Figure 12.5 (call the Begin method in C# to start the animation). Because Eyelid Ellipse has the same color as the background (intentionally filling the adjacent area to the left with black), we cannot see Eyelid Ellipse itself. On the contrary, once the height of Ellipse (380) is less than twice its filling thickness (400), we can see that the space inside it shrinks sharply to zero.
Figure 12.5 Eyelid Storyboard Compress the height of Eyelid Ellipse and move it down to keep it centered.
Storyboard and Animation Properties
We have learned about the Duration, AutoReverse and RepeatBehavior properties, which can be applied to a single animation or a complete Storyboard. In general, there are 6 attributes that can be applied to Storyboard and Animation:
➔ Duration: The length of Animation or Storyboard, the default value is 1 second.
Be careful to specify the value of duration! The string format of a duration is the same as that of the TimeSpan.Parse interface function: days.hours:minutes:seconds.fraction is allowed here, so we don't need to specify each string. However, its behavior may not be what you want. The character "2" means 2 days, not 2 seconds. The string "2.5" means 2 days and 5 hours! The string "0:2" means 2 minutes. If most animations cannot exceed the time limit of a few seconds, then the typical format used is hours:minutes:seconds or hours:minutes:seconds.fraction. Therefore, 2 seconds can be expressed as "0:0:2", and half a second can be expressed as "0:0:0.5" or "0:0:.5".
➔ BeginTime: The length of time the animation or Storyboard delay starts, represented by a specific time value, and the default is 0. For the animation of child elements, Storyboard can use a custom BeginTime value, so that they can start the animation one after another, rather than at the same time.
➔ SpeedRatio: the multiplier of the duration (Duration), the default is 1. We can set it to any double value greater than 0. A value less than 1 can slow down the speed of the animation, and a value greater than 1 can speed up the animation. SpeedRatio does not affect BeginTime.
➔ AutoReverse: When this property is set to True, after the animation or Storyboard reaches the end, automatic playback will be realized. The playback takes the same length of time, so SpeedRatio will also affect the playback. It should be noted that the delay specified by BeginTime will not affect the playback. The playback will generally start immediately after the normal animation ends.
➔ RepeatBehavior: It can be set as a time period or as a string, such as "2x", "3x" or "Forever". Therefore, we can use RepeatBehavior to shorten the duration of the animation (or reduce their duration), or make the animation automatically repeat multiple times (even a multiple with a decimal, such as 2.5 times), or repeat the animation forever (This chapter uses this method). If AutoReverse is set to True, then the playback operation will be repeated.
➔ FillBehavior: It can be set to Stop, and its default value is HoldEnd, so that after the related animation is completed, its attribute value is restored to the value before the animation.
The Total Length of an Animation
By using properties such as BeginTime, SpeedRatio, AutoReverse, and RepeatBehavior, you can make various adjustments to the animation. After the animation starts, it is difficult to test the length of its total duration. Its Duration value is certainly not enough to describe the real length of time! In contrast, the following formula describes the true duration of an animation:
Total time = BeginTime + (Duration * (AutoReverse? 2:1) * RepeatBehavior)/SpeedRatio
This can be used when the RepeatBehavior attribute is specified as a double value (or its default value of 1). If RepeatBehavior is set to a time period, then the total length of time is the value of RepeatBehavior plus the value of BeginTime.
The Main Page
The XAML on the Silly Eye main page contains some vector images, an application bar, and three Storyboards. It also contains a "instructions page", which implies that the user clicks on the screen to start the application, as shown in Figure 12.6. Therefore, we can show the application bar at the beginning, but when the application starts running, it is hidden because the buttons displayed on the screen will hinder the effect of the application. The introduction page implies that users can re-call the application bar at any time by tapping the screen.
Figure 12.6 The application bar is only visible when the "introduction page" appears
➔ The application bar contains links to the settings page, description page and about page. The first two pages will be introduced in the next two sections. We have already learned about the page in Chapter 6 "Baby Sign Language". We believe that it is better to place the link of the settings page as a button in the application bar than a menu item, because in this application, it is normal for the user to customize the settings (during the normal operation of the application) , The application bar will not introduce visual clutter, because it is hidden!).
➔ Note that the names of the three Storyboard resources are named "x:Name" instead of "x:Key"! This is a convenient method that allows us to use the code behind it more conveniently. After we name the resource, it can be used as a key in the dictionary or as a field generated by C#.
➔ The explicit From value has been removed from the Pupil Storyboard animation because it is not required. This part of the content has been introduced in this chapter, it helps to understand how animation works.
➔ The IntroTextBlock element is used to monitor the user's click and hide the IntroPanel. Its width value is 700, which is larger than the width of the entire page, because if it is too close to the application bar, users may accidentally click on it when they want to click the application bar (then the application bar will be hidden) ).
➔ Because of the x:Name tag in XAML, three Storyboards are initialized in the constructor with their respective names.
➔ The Clip property of the page is set to a rectangular area the size of the screen. This is to prevent rendering of vector graphics outside the screen during the animation page switching. This not only avoids the appearance of very strange visual elements, but also helps improve the performance of the application. All UI elements with this Clip attribute can be set to an arbitrary geometric figure.
Geometries Used for Clipping
The Clip property can be set to some geometric shapes, these shapes are similar to the shape objects introduced in Chapter 5 "Ruler", but they are different. We can use RectangleGeometry, EllipseGeometry, LineGeometry, PathGeometry or GeometryGroup to combine multiple geometric shapes. These geometric shapes are discussed in Appendix E "Geometry Reference".
➔ We use two storage settings to store the skin and eye color values, which are used in the OnNavigatedTo event processing. In the OnNavigatedFrom event processing, they are not necessary to be stored, because the setting page will be processed. The content of these settings is defined in a separate Settings.cs file. In fact, the default eye color is the accent color of the mobile phone theme, just like the blue shown in the pictures in this chapter.
➔ The visibility of the IntroPanel (and the application bar) is placed in the state of the page, so if the page is hibernated and activated, it looks the same. No matter how much the page has undergone changes, don’t forget to use the page settings. With it, when the application experience is interrupted and reactivated, we can quickly and automatically restore the page state.
➔ The alignment of the IntroTextBlock is adjusted in the OnOrientationChanged event. This is done to maintain the opposite positional relationship between it and the application bar. As mentioned above, when the orientation of the phone is landscape right, the application bar appears on the left side of the screen; when the orientation of the phone is landscape left, the application bar appears on the right side of the screen.
The Settings Page
The settings page is shown in Figure 12.7, which allows users to choose different colors for eyes and skin.
Figure 12.7 The settings page allows the user to select the color of the Silly Eye application.
How to add a setting page to our application in the system's built-in setting program? In the current version of Windows Phone 7.0, we are still unable to do this. Although the settings application includes a list of system settings and a list of application settings, the latter is only for the applications that come with the system. Instead, we need to add a setting page to our application to make its user experience consistent with the system's own setting page. In other words, for our settings page, we should use "SETTINGS" as the name of the application to appear in the standard header, and use the name of the application as the page title, as shown in Figure 12.7.
For the design guidance of the setting page, please refer to the content in Chapter 20 "Alarm Clock".
The attention points of page design are as follows:
➔ The custom header style of this page is obtained from the App.xaml file. For the remaining applications in this book, the App.xaml.cs file also provides custom page transition effects, as described in Chapter 19 "Animation Lab".
➔ The two clickable areas show the current colors, which look a lot like buttons, but in fact they are just rectangular fills. Their MouseLeftButtonUp event handling includes the user's handling of each interface color change.
➔ Although the content fits the screen completely in different orientation modes, the main stack panel control is placed in the scroll viewer. For users, this is very suitable for touch operation, because users can use their fingers to drag the screen to view the content, and make them confident that they have browsed all the content on the screen.
In many pages, such as setting pages, description pages, or about pages, placing the content in the scroll viewer is a good choice, even if all the content can be accommodated on one screen. In that case, users can do a quick drag with their fingers, and visually they can judge that there is no more content to watch (because of the scroll-and-squish feature of the scroll viewer control). This kind of feedback is very satisfying to users. When the screen does not respond to the user's drag, the user will think that he is not exerting enough effort, and may try again.
To enable the user to change each color, the page will navigate the user to a color selection page, as shown in Figure 12.8. This feature page is shared by many applications in this book and is included in the source code of this book. It provides a standard color palette, and it also allows users to customize the hue, saturation and brightness of the color, whether it is through an interactive interface or inputting a hexadecimal value (or anything that can be XAML The parsed string, such as "red", "tan" or "lemonchiffon"). Adjusting the opacity of the color is optional.
Figure 12.8 The color selector page provides a beautiful page for users to choose colors.
The color picker page can accept the following four parameters through the query string:
➔ showOpacity-The default value is True, you can set it to False to hide the opacity slider. It will also remove the transparent color at the top of the palette and prevent the user from entering the transparent color. Therefore, when we set it to False, we can be sure that an opaque color will be selected.
➔ currentColor-When the page is presented, the color to be selected at the beginning. It must be passed in as a string parameter valid for XAML. If specified as a hexadecimal number, "#" must be removed. This is done to confuse with the URI.
➔ defaultColor-In the color selector page, the user can get the color when he clicks the reset button. The string format specified by it is the same as currentColor.
➔ settingName-the name used in the isolated storage space, when returning from the page, the selected color can be found from it. The same name is used when constructing an instance of Setting. In the OnNavigatedTo method of Listing 12.4, when returning from the color picker page, it automatically selects the new color value, just because the ForceRefresh method needs to be called before navigating to the color selection page. Chapter 20 describes this method in detail.
Use the color selector page of this book (or a similar page) to provide users with a simple and efficient method of color selection. In the current version, the main disadvantage of this page is that it only supports portrait orientation. Therefore, in the landscape screen orientation, the user experience of using the hardware keyboard to input a color hexadecimal number is not good.
The Instructions Page
The description page of this application is shown in Figure 12.9, and the points of attention are as follows:
Figure 12.9 The description page in the Silly Eye application
➔ Same as the method used in the setting page, the main content is placed in the scroll viewer to prompt the user that there is no more content to browse.
➔ Like the intro pane in the main page, a single text block uses the LineBreak element to format its text content.
➔ For the code file behind-InstructionsPage.xaml.cs, in its constructor, only the call to the InitializeComponent method is included.