You can use transitions to add visual appeal to your Spark
skins. Transitions are triggered off of state changes, which are
explicitly supported in Spark skins. All the visuals for a transition
should be defined in the skin class and not on the component.
You can use transitions in Spark skins in the same way you would
use them in your application. You add a <s:transitions> tag
as a child tag of the skin’s root tag. You then define the transitions
and which states they apply to with the toState and fromState on
the <s:Transition> child tags. Because the skin
is notified of state changes from the host component, you do not
have to add any logic to support state changes to the skin.
Typically, you specify the values for transitions in states and
not in the effect itself.
The following example uses the
Resize transition
to grow and shrink the Button control’s label and border. The transitions
are triggered when the user rolls the pointer over the Button control
and when the user rolls the pointer off of the Button control.
<?xml version="1.0" encoding="utf-8"?>
<!-- SparkSkinning/ButtonTransitionExample.mxml -->
<s:Application
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:s="library://ns.adobe.com/flex/spark"
height="200" width="200">
<s:layout>
<s:HorizontalLayout/>
</s:layout>
<s:Button label="Click Me"
skinClass="mySkins.ButtonTransitionSkin"/>
</s:Application>The executing SWF file for the previous example is shown below:
The custom skin class for this example is as follows:
<?xml version="1.0" encoding="utf-8"?>
<!-- SparkSkinning\mySkins\ButtonTransitionSkin.mxml -->
<s:Skin
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:s="library://ns.adobe.com/flex/spark"
minWidth="21" minHeight="21">
<s:transitions>
<s:Transition fromState="up" toState="over">
<s:Resize target="{buttonBorder}"/>
</s:Transition>
<s:Transition fromState="over" toState="up">
<s:Resize target="{buttonBorder}"/>
</s:Transition>
</s:transitions>
<fx:Metadata>
[HostComponent("spark.components.Button")]
</fx:Metadata>
<s:states>
<s:State name="up"/>
<s:State name="over"/>
<s:State name="down"/>
<s:State name="disabled"/>
</s:states>
<s:Rect id="buttonBorder" width="69" width.over="79" height="20" radiusX="2" radiusY="2">
<s:stroke>
<s:SolidColorStroke color="0x000000" weight="1"/>
</s:stroke>
</s:Rect>
<!-- layer 8: text -->
<s:Label id="labelDisplay"
horizontalCenter="0" verticalCenter="1">
</s:Label>
</s:Skin>
You typically declare the state values in the object or skin,
and the effects use these values. The previous example defines the width.over property
on the Rect rather than using the widthBy property
of the effect to set the target width.
When using transitions inside Spark skins, you might have to
assign IDs to elements that do not by default have IDs. This is
because transitions require a target when you define them, and without
an ID, you cannot target a specific element.
As with standard Flex controls, you can use transitions with
multiple effects that are playing in parallel on a Spark skin. The
following example resizes and changes the color of the button’s
label when the user moves their mouse over or out of the Button:
<?xml version="1.0" encoding="utf-8"?>
<!-- SparkSkinning/ButtonParallelTransitionExample.mxml -->
<s:Application
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:s="library://ns.adobe.com/flex/spark"
height="200" width="200">
<s:layout>
<s:HorizontalLayout/>
</s:layout>
<s:Button label="Click It or Ticket"
skinClass="mySkins.ButtonParallelTransitionSkin"/>
</s:Application>The executing SWF file for the previous example is shown below:
The custom skin class for this example is as follows:
<?xml version="1.0" encoding="utf-8"?>
<!-- SparkSkinning/mySkins/ButtonParallelTransitionSkin.mxml -->
<s:SparkSkin
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
minWidth="21" minHeight="21"
alpha.disabled="0.5">
<s:transitions>
<s:Transition>
<s:Parallel target="{labelDisplay}">
<s:Animate>
<s:SimpleMotionPath property="fontSize"/>
</s:Animate>
<s:AnimateColor/>
</s:Parallel>
</s:Transition>
</s:transitions>
<fx:Metadata>
[HostComponent("spark.components.Button")]
</fx:Metadata>
<fx:Script>
static private const exclusions:Array = ["labelDisplay"];
override public function get colorizeExclusions():Array {return exclusions;}
</fx:Script>
<s:states>
<s:State name="up" />
<s:State name="over" />
<s:State name="down" />
<s:State name="disabled" />
</s:states>
<!-- The following values are negative because they define a border or drop shadow.
The negative values separate them from the Button's bounds. -->
<s:Rect left="-1" right="-1" top="-1" bottom="-1" radiusX="2" radiusY="2">
<s:fill>
<s:LinearGradient rotation="90">
<s:GradientEntry color="0x000000"
color.down="0xFFFFFF"
alpha="0.01"
alpha.down="0" />
<s:GradientEntry color="0x000000"
color.down="0xFFFFFF"
alpha="0.07"
alpha.down="0.5" />
</s:LinearGradient>
</s:fill>
</s:Rect>
<s:Rect left="1" right="1" top="1" bottom="1" radiusX="2" radiusY="2">
<s:fill>
<s:LinearGradient rotation="90">
<s:GradientEntry color="0xFFFFFF"
color.over="0xBBBDBD"
color.down="0xAAAAAA"
alpha="0.85" />
<s:GradientEntry color="0xD8D8D8"
color.over="0x9FA0A1"
color.down="0x929496"
alpha="0.85" />
</s:LinearGradient>
</s:fill>
</s:Rect>
<s:Rect left="1" right="1" bottom="1" height="9" radiusX="2" radiusY="2">
<s:fill>
<s:LinearGradient rotation="90">
<s:GradientEntry color="0x000000" alpha="0.0099" />
<s:GradientEntry color="0x000000" alpha="0.0627" />
</s:LinearGradient>
</s:fill>
</s:Rect>
<s:Rect left="1" right="1" top="1" height="9" radiusX="2" radiusY="2">
<s:fill>
<s:SolidColor color="0xFFFFFF"
alpha="0.33"
alpha.over="0.22"
alpha.down="0.12" />
</s:fill>
</s:Rect>
<s:Rect left="1" right="1" top="1" bottom="1"
radiusX="2" radiusY="2" excludeFrom="down">
<s:stroke>
<s:LinearGradientStroke rotation="90" weight="1">
<s:GradientEntry color="0xFFFFFF" alpha.over="0.22" />
<s:GradientEntry color="0xD8D8D8" alpha.over="0.22" />
</s:LinearGradientStroke>
</s:stroke>
</s:Rect>
<s:Rect left="1" top="1" bottom="1" width="1" includeIn="down">
<s:fill>
<s:SolidColor color="0x000000" alpha="0.07" />
</s:fill>
</s:Rect>
<s:Rect right="1" top="1" bottom="1" width="1" includeIn="down">
<s:fill>
<s:SolidColor color="0x000000" alpha="0.07" />
</s:fill>
</s:Rect>
<s:Rect left="2" top="1" right="2" height="1" includeIn="down">
<s:fill>
<s:SolidColor color="0x000000" alpha="0.25" />
</s:fill>
</s:Rect>
<s:Rect left="1" top="2" right="1" height="1" includeIn="down">
<s:fill>
<s:SolidColor color="0x000000" alpha="0.09" />
</s:fill>
</s:Rect>
<s:Rect left="0" right="0" top="0" bottom="0"
width="69" height="20"
radiusX="2" radiusY="2">
<s:stroke>
<s:LinearGradientStroke rotation="90" weight="1">
<s:GradientEntry color="0x000000"
alpha="0.5625"
alpha.down="0.6375" />
<s:GradientEntry color="0x000000"
alpha="0.75"
alpha.down="0.85" />
</s:LinearGradientStroke>
</s:stroke>
</s:Rect>
<s:Label id="labelDisplay"
fontSize="{hostComponent.getStyle('fontSize')}"
fontSize.over="{hostComponent.getStyle('fontSize') + 4}"
color.over="0xFF0000"
textAlign="center"
verticalAlign="middle"
lineBreak="toFit"
maxDisplayedLines="1"
horizontalCenter="0" verticalCenter="1"
left="10" right="10" top="2" bottom="2">
</s:Label>
</s:SparkSkin>
The previous example uses the SparkSkin class
rather than the Skin class
as its root because it defines the colorExclusions() getter.
You can use transitions and states to effectively add and remove
elements from the skin. For example, if you want an image to appear
on a Button control during a mouse over, you can change the value
of a property height from 0 to a new value in the over state.
The following example displays an image of a butterfly when you
move the mouse over the Button control, and removes the image when
you move the mouse out:
<?xml version="1.0" encoding="utf-8"?>
<!-- SparkSkinning/ButterflySkinExample.mxml -->
<s:Application
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:s="library://ns.adobe.com/flex/spark"
backgroundColor="0x999999">
<s:Button id="myButton"
fontWeight="bold"
color="0xFFFFFF"
label="Bug of the Day"
skinClass="mySkins.ButterflySkin"/>
</s:Application>The executing SWF file for the previous example is shown below:
The custom skin class for this example is as follows:
<?xml version="1.0" encoding="utf-8"?>
<!-- SparkSkinning/mySkins/ButterflySkin.mxml -->
<s:SparkSkin
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:s="library://ns.adobe.com/flex/spark"
minWidth="21" minHeight="21" alpha.disabled="0.5">
<!-- host component -->
<fx:Metadata>
<![CDATA[
[HostComponent("spark.components.Button")]
]]>
</fx:Metadata>
<s:transitions>
<mx:Transition fromState="up" toState="over">
<mx:Parallel duration="350">
<s:Resize target="{ myImage }"/>
<s:Fade targets="{ [myImage,labelDisplay] }"/>
</mx:Parallel>
</mx:Transition>
<mx:Transition fromState="over" toState="up">
<mx:Parallel duration="200">
<s:Resize target="{ myImage }"/>
<s:Fade targets="{ [myImage,labelDisplay] }"/>
</mx:Parallel>
</mx:Transition>
</s:transitions>
<fx:Script>
<![CDATA[
static private const exclusions:Array = ["labelDisplay"];
override public function get colorizeExclusions():Array {return exclusions;}
override protected function initializationComplete():void {
useChromeColor = true;
super.initializationComplete();
}
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number) : void {
var cr:Number = getStyle("cornerRadius");
super.updateDisplayList(unscaledWidth, unscaledHeight);
}
private var cornerRadius:Number = 2;
]]>
</fx:Script>
<!-- states -->
<s:states>
<s:State name="up" />
<s:State name="over" />
<s:State name="down" />
<s:State name="disabled" />
</s:states>
<s:Rect id="blueRect" radiusX="8" radiusY="8" top="0" right="0" bottom="0" left="0" minHeight="30">
<s:fill>
<s:LinearGradient x="0" y="0" scaleX="44" rotation="90">
<s:GradientEntry color="#3399ff" ratio="0" color.over="#66CCFF"/>
<s:GradientEntry color="#3366cc" ratio="1" color.over="#3399CC"/>
</s:LinearGradient>
</s:fill>
<s:stroke>
<s:SolidColorStroke color="#ffffff" weight="2"/>
</s:stroke>
</s:Rect>
<!-- Border -->
<s:Rect radiusX="6" radiusY="6"
top="2" right="2" height="15" left="2">
<s:fill>
<s:LinearGradient x="0" y="0" scaleX="23" rotation="90">
<s:GradientEntry color="#ffffff" ratio="0" alpha=".3"/>
<s:GradientEntry color="#ffffff" ratio="1" alpha=".1"/>
</s:LinearGradient>
</s:fill>
</s:Rect>
<s:Image id="myImage"
height="0" height.over="90"
source="@Embed(source='../../assets/butterfly.png')"
left="20"/>
<s:Label id="labelDisplay"
visible.over="false"
textAlign="center" verticalAlign="middle"
maxDisplayedLines="1"
horizontalCenter="0" verticalCenter="1"
left="10" right="10" top="2" bottom="2"/>
</s:SparkSkin>