Скин для IconButton в стиле Windows 7
Этот пост продолжение предыдущего.
Как только появилась Windows 7, мне очень понравился дизайн кнопок на панели задач и возникло желание сделать подобные кнопки для флеша. Но руки как-то до этого не доходили. Но вот на днях выдалось свободное время и, в итоге, родилась эта реализация.
И так начнем.
Анатомия скина.
Скин будет состоять из двух полупрозрачны обводок (белая и черная)
Код:
<s:Rect id="blackBorder" top="0" bottom="0" left="0" right="0" radiusX="4"> <s:stroke> <s:SolidColorStroke color="0" alpha=".3"/> </s:stroke> </s:Rect> <s:Rect id="whiteBorder" top="1" bottom="1" left="1" right="1" radiusX="2"> <s:stroke> <s:SolidColorStroke color="0xFFFFFF" alpha=".3"/> </s:stroke> </s:Rect>
Код:
<s:Rect id="fill" top="1" bottom="1" left="1" right="1" radiusX="2"> <s:fill> <s:LinearGradient rotation="90"> <s:GradientEntry color="0xFFFFFF" alpha="0.3" alpha.down=".8" ratio="0"/> <s:GradientEntry color="0xFFFFFF" alpha="0.05" alpha.down=".3" ratio="0.6"/> <s:GradientEntry color="0xFFFFFF" alpha="0.01" alpha.down=".8" ratio="1"/> </s:LinearGradient> </s:fill> </s:Rect>
Код:
<s:Path id="highlight" top="1" bottom="1" left="1" right="1" data="L 20 0 L 20 5 Q 10 2 0 15 L 0 0"> <s:fill> <s:RadialGradient id="highlightGradient" x="30" y="0"> <s:GradientEntry color="0xFFFFFF" alpha="0.5"/> <s:GradientEntry color="0xFFFFFF" alpha="0"/> </s:RadialGradient> </s:fill> </s:Path>
Код:
<s:Rect id="mouseHighlight" top="1" bottom="1" left="1" right="1" radiusX="2"> <s:fill> <s:RadialGradient id="mouseHighlightGradient"> <s:GradientEntry color="{_averageColor}" alpha="1"/> <s:GradientEntry color="{_averageColor}" alpha="0"/> </s:RadialGradient> </s:fill> </s:Rect>
Тут есть один момент. Градиенты надо отмасштабировать, но так как масштаб задается в пикселях, придется при изменении размеров вносить коррективы.
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void { if (mouseHighlightGradient) { mouseHighlightGradient.y = unscaledHeight; mouseHighlightGradient.scaleX = unscaledWidth * 3; mouseHighlightGradient.scaleY = unscaledHeight * 2; } highlightGradient.scaleX = unscaledWidth * 2 - unscaledWidth * .2; highlightGradient.scaleY = unscaledHeight; super.updateDisplayList(unscaledWidth, unscaledHeight); }
Непосредственно скин, не реагирует на события мыши, поэтому подписываться будем у hostComponent. Ну и так как mouseHighlight появляется только тогда, когда курсор находится над кнопкой, делать это будем при смене состояния.
override protected function stateChanged(oldState:String, newState:String, recursive:Boolean):void { super.stateChanged(oldState, newState, recursive); if (newState == "over" || newState == "down") { hostComponent.addEventListener(MouseEvent.MOUSE_MOVE, hostComponent_mouseMoveHandler); } else { hostComponent.removeEventListener(MouseEvent.MOUSE_MOVE, hostComponent_mouseMoveHandler); } } private function hostComponent_mouseMoveHandler(event:MouseEvent):void { mouseHighlightGradient.x = hostComponent.mouseX; }
Для начала определим момент, когда иконка будет добавлена в скин, что бы получить к ней доступ.
override protected function createChildren():void { super.createChildren(); iconGroup.addEventListener(ElementExistenceEvent.ELEMENT_ADD, iconGroup_ElementExistenceHandler); iconGroup.addEventListener(ElementExistenceEvent.ELEMENT_REMOVE, iconGroup_ElementExistenceHandler); }
Слава Цифре есть событие UPDATE_COMPLETE, которое в данном случае идеально подходит.
private function iconGroup_ElementExistenceHandler(event:ElementExistenceEvent):void { switch (event.type) { case ElementExistenceEvent.ELEMENT_ADD: event.element.addEventListener(FlexEvent.UPDATE_COMPLETE, icon_updateComplete); break case ElementExistenceEvent.ELEMENT_REMOVE: event.element.removeEventListener(FlexEvent.UPDATE_COMPLETE, icon_updateComplete); break } }
private function icon_updateComplete(event:FlexEvent):void { _averageColor = calculateAverageColor(event.currentTarget as DisplayObject); } [Bindable] private var _averageColor:uint = 0xFFFFFF; private function calculateAverageColor(source:DisplayObject):uint { var averageColorBmd:BitmapData = new BitmapData(source.width, source.height); averageColorBmd.draw(source); var red:uint = 0; var green:uint = 0; var blue:uint = 0; var count:int = 0; var pixel:uint; for (var x:int = 0; x < averageColorBmd.width; x++) { for (var y:int = 0; y < averageColorBmd.height; y++) { pixel = averageColorBmd.getPixel(x, y); red += pixel >> 16 & 0xFF; green += pixel >> 8 & 0xFF; blue += pixel & 0xFF; count++ } } red /= count; green /= count; blue /= count; return red << 16 | green << 8 | blue; }
P.S. Я внес изменения в IconButton, она теперь наследуется от ButtonBase для совместимости с будущими версиями фреймворка. Если вдруг она кому нибудь понадобится, берите исходники отсюда.
Всего комментариев 2
Комментарии
02.02.2011 14:39 | |
Отличная штука. Тоже очень нравятся такие кнопки.
И очень же захотелось украсть =D Надо будет на досуге переписать на чистый as и сделать компонент. |
04.08.2011 17:06 | |
Респект ...
Описал классно ... Тоже на досуге компонент сделаю и в swc откомпиляю ... Кому надо обращайтесь ... |
Последние записи от alatar
- Трансформации вокруг произвольной точки (05.02.2015)
- Декомпозиция матрицы. (25.12.2014)
- Баги TextField в iOS (29.07.2013)
- [Spark] Стрелочный индикатор. (06.12.2012)
- RSLs Monkey Patching (23.01.2012)