I've been a lazy bastich, but here's the promised post on skinning with Degrafa. Note: this is based on Degrafa Beta 2. Beta 3 is expected rather soon, and there'll be a couple of changes such as the new namespace etc.
OK, for this example, we're going to skin a simple little <mx:Box> to dress it up some. Here's our MXML for the component we'd like to skin:
<mx:Canvas styleName="skinnedBox" width="250" horizontalCenter="0" verticalCenter="0">
<mx:Text width="100%">
<mx:text>
Aliquam et nisl vel ligula consectetuer suscipit. Morbi euismod enim eget neque. Donec sagittis
massa. Vestibulum quis augue sit amet ipsum laoreet pretium. Nulla facilisi. Duis tincidunt,
felis et luctus placerat, ipsum libero vestibulum sem, vitae elementum wisi ipsum a metus.
Nulla a enim sed dui hendrerit lobortis. Donec lacinia vulputate magna. Vivamus suscipit lectus
consectetuer adipiscing elit.
</mx:text>
</mx:Text>
</mx:Canvas>
And a simple "before" CSS:
/* CSS file */
.skinnedBox
{
border-style:solid;
border-thickness:1;
border-color:black;
background-color:#cccccc;
}
Giving us this:
And we're going to skin it so it looks like this:
If we were skinning a Button, we'd use things like up-skin, down-skin, etc. But, we're just skinning a box, so we'll use border-skin:
/* CSS file */
.skinnedBox
{
border-skin:ClassReference("skinningInDegrafa.ShinySkin");
}
We want to specify the borderMetrics of our skin (to add the padding), so we'll subclass <degrafa:GraphicRectangularBorderSkin>:
<?xml version="1.0" encoding="utf-8"?>
<degrafa:GraphicRectangularBorderSkin xmlns:mx="http://www.adobe.com/2006/mxml" width="400" height="300" xmlns:degrafa="http://www.degrafa.com/2007">
<mx:Script>
<![CDATA[
import mx.core.EdgeMetrics;
We need to keep a copy of the width and height which is set by the framework. The actual height and width are set on updateDisplayList, which is as good a place as any to take note of them, and we'll have less unnecessary updates that way:
[Bindable]
private var skinWidth : Number = 0;
[Bindable]
private var skinHeight : Number = 0;
private var edges : EdgeMetrics = new EdgeMetrics(20, 5, 20, 5);
override protected function updateDisplayList(unscaledWidth : Number, unscaledHeight : Number) : void
{
skinWidth = unscaledWidth;
skinHeight = unscaledHeight;
super.updateDisplayList(unscaledWidth, unscaledHeight);
}
override public function get borderMetrics() : EdgeMetrics
{
return edges;
}
]]>
</mx:Script>
Below is the exciting part that defines the shapes and fills! A how-to on Degrafa in general is out of scope for this post, so see the Degrafa site for documentation and more examples.
<degrafa:fills>
<degrafa:LinearGradientFill angle="90" id="f">
<degrafa:GradientStop ratio="0" color="#dde4ee"/>
<degrafa:GradientStop ratio="1" color="#aaa0bb"/>
</degrafa:LinearGradientFill>
<degrafa:LinearGradientFill angle="90" id="f2">
<degrafa:GradientStop ratio="0.3" alpha="0.02" color="#ffffff"/>
<degrafa:GradientStop ratio="1" alpha="0.3" color="#ffffff"/>
</degrafa:LinearGradientFill>
</degrafa:fills>
<degrafa:geometry>
<degrafa:Path fill="{f}">
<degrafa:MoveTo x="0" y="{ skinHeight / 2 }"/>
<degrafa:CubicBezierTo x="20" y="0" cx="0" cy="{skinHeight * 0.25}" cx1="1" cy1="1"/>
<degrafa:LineTo x="{ skinWidth - 20}" y="0"/>
<degrafa:CubicBezierTo x="{ skinWidth }" y="{ skinHeight / 2 }" cx="{ skinWidth - 1}" cy="1" cx1="{ skinWidth }" cy1="{ skinHeight * 0.25 }"/>
<degrafa:CubicBezierTo x="{ skinWidth - 20 }" y="{ skinHeight }" cx="{ skinWidth }" cy="{ skinHeight * 0.75 }" cx1="{ skinWidth - 1 }" cy1="{ skinHeight - 1 }"/>
<degrafa:LineTo x="20" y="{ skinHeight }"/>
<degrafa:CubicBezierTo x="0" y="{ skinHeight / 2 }" cx="1" cy="{ skinHeight - 1 }" cx1="0" cy1="{ skinHeight * 0.75 }"/>
<degrafa:ClosePath/>
</degrafa:Path>
<degrafa:Path fill="{f2}">
<degrafa:MoveTo x="10" y="{ skinHeight / 2 }"/>
<degrafa:CubicBezierTo x="20" y="0" cx="0" cy="{skinHeight * 0.25}" cx1="1" cy1="1"/>
<degrafa:LineTo x="{ skinWidth - 20}" y="0"/>
<degrafa:CubicBezierTo x="{ skinWidth - 10 }" y="{ skinHeight / 2 }" cx="{ skinWidth - 1}" cy="1" cx1="{ skinWidth }" cy1="{ skinHeight * 0.25 }"/>
<degrafa:CubicBezierTo x="10" y="{ skinHeight / 2 }" cx="{ skinWidth * 0.75 }" cy="{ skinHeight * 0.4}" cx1="{ skinWidth * 0.25 }" cy1="{ skinHeight * 0.4}"/>
<degrafa:ClosePath/>
</degrafa:Path>
</degrafa:geometry>
<degrafa:filters>
<mx:GlowFilter inner="true" color="#000000" blurX="3" blurY="3" alpha="0.3"/>
<mx:DropShadowFilter alpha="0.3" angle="90" distance="5" blurX="8" blurY="8"/>
</degrafa:filters>
</degrafa:GraphicRectangularBorderSkin>
And that's a pretty simple example of skinning with Degrafa! Here's the complete example (with view-source enabled).


7 comments:
Degrafa is amazing. Thanks for the interesing post. But how did you compose that gradients? Manual work?
creationcomplete.com
Note that this article is linked to from the Degrafa Samples page as 'Skinning a TextArea', which is not the same as skinning a Box with a Text component within it.
Mico: This was something I cooked up custom for the blog, so it was just manual tweaking, and a natural feel for what I was trying to get across. Sometimes I'll mock things up in Photoshop, other times I'll just go from my mind's eye into code. Via several dozen tweaks and recompiles, and two or three "chuck it and re-start" phases, of course! ;-)
Hello,
I cannot get this to work with Degrafa_Beta3_Flex3.swc - any ideas? I did change the private vars for the width and height to remove some syntax errors so the project compiles without error but the style is not applied.
Thanks
The code listed here is for Beta 2. Beta 3 gives you skinHeight and skinWidth variables (just coincidently named as in my example). However, there's a few bugs in the released B3 of Degrafa that don't agree with my code, but the version in svn at http://degrafa.googlecode.com/svn/branches/Origin/Degrafa works for me.
Josh,
The link to the final example gives a 404 error.
Sorry Mate, recently switched hosting providers. I will get this and any other links fixed up this evening.
Post a Comment