Planowanie projektów
Firma CrystalPoint jest do Państwa dyspozycji w zaplanowaniu działalności internetowej, analizie potrzeb i wymagań, opracowaniu funkcjonalności czy grafiki. Prosimy o kontakt z nami.
Napisz do nas...Oferta
CrystalPoint specjalizuje się w tworzeniu zaawansowanych aplikacji internetowych, tworzonych z użyciem sprawdzonych technologii.
- Aplikacje internetowe
- Strony i wizytówki WWW
- Hosting
- Serwery VPS
Portfolio
Nasze usługi mają potwierdzenie w zrealizowanych i utrzymywanych projektach. Projektowaliśmy:
- Sklepy internetowe
- Aplikacje dedykowane
- Serwisy społecznościowe
- Narzędzia dla programistów
Menu Flash w stylu MacOS Dock Bar
Firma Apple znana jest w branży IT ze swoich ciekawych projektów graficznych. Projektanci spod znaku nadgryzionego jabłuszka przyzwyczaili nas już do tego, że wszystkie ich projekty są bardzo wygodne w użytkowaniu i co bardzo ważne - przyjemne dla oka. Dokładnie te same cechy powinny posiadać elementy Flash na stronach www. Zerknijmy więc na jeden z najbardziej popularnych elementów systemu Mac OS, pasek Dock Bbar, i wzorując się na jego projekcie wykonajmy kontrolkę, bez której żadna strona www nie może istnieć - menu nawigacyjne.
Nawigacja jest jednym z najistotniejszych elementów stron www. Jest to element, z którego każdy użytkownik musi wielokrotnie korzystać a co za tym idzie musi on wywżeć jak najlepsze wrażenie. Nawigacja powinna być wygodna i łatwa w użyciu a także przyjemna graficznie. Bazując na projekcie znanym z systemów Mac OS można, wykrozystując Actionscript 3, przygotować bardzo fajne menu.
Efekt końcowy wygląda następująco:
Rzućmy teraz okiem na implementację. Cały projekt składa się z 3 plików: pliku .FLA, pliku Main.as, z klasą która tradycyjnie pełni rolę kontrolera, i pliku element.as - klasy reprezentującej każdy element menu.
Analizę rozpoczniemy od omówienia kodu klasy Main:
/* flash generic imports */
import flash.display.MovieClip;
import flash.events.MouseEvent;
/* package specific classes */
import macTaskBar.element;
public class Main extends MovieClip
{
/* config variables */
private var numOfElements:int = 9;
private var listOfURLs:Array;
private var captions:Array;
/* support variables */
private var stageMask:MovieClip;
public var elements:Array;
Importujemy niezbędne biblioteki standardowe, a także klasę element. Deklarujemy klasę kontrolera z następującymi atrybutami:
- numOfElements - zmienna definiująca ile elementów menu ma zostać utworzonych
- listOfURLs - tablica zawierająca listę adresów URL, na które użytkownik zostanie przekierowany po kliknięciu na elemencie menu.
- captions - tablica zawierająca listę etykiet dla każdego z elementów menu.
- stageMask - zmienna reprezentująca niewidoczny element pokrywający całą scenę - zapewni przechwytywanie zdarzeń myszki na całym projekcie.
- elements - tablica w której przechowywana jest lista wszystkich utworzonych elementów.
Przejdźmy do konstruktora:
public function Main()
{
this.listOfURLs = new Array();
this.listOfURLs.push("http://crystalpoint.pl");
this.listOfURLs.push("http://crystalpoint.pl");
this.listOfURLs.push("http://crystalpoint.pl");
this.listOfURLs.push("http://crystalpoint.pl");
this.listOfURLs.push("http://crystalpoint.pl");
this.listOfURLs.push("http://crystalpoint.pl");
this.listOfURLs.push("http://crystalpoint.pl");
this.listOfURLs.push("http://crystalpoint.pl");
this.listOfURLs.push("http://crystalpoint.pl");
this.captions = new Array();
this.captions.push("Lorem ipsum - 1");
this.captions.push("Lorem ipsum - 2");
this.captions.push("Lorem ipsum - 3");
this.captions.push("Lorem ipsum - 4");
this.captions.push("Lorem ipsum - 5");
this.captions.push("Lorem ipsum - 6");
this.captions.push("Lorem ipsum - 7");
this.captions.push("Lorem ipsum - 8");
this.captions.push("Lorem ipsum - 9");
/* allow for entire stage mouse events */
this.stageMask = new MovieClip();
this.stageMask.x = 0;
this.stageMask.y = 0;
this.stageMask.graphics.beginFill(0xff00ff);
this.stageMask.graphics.drawRect(0,0, stage.stageWidth, stage.stageHeight);
this.stageMask.graphics.endFill();
this.stageMask.alpha = 0;
this.addChild(this.stageMask);
/* calculate starting position */
var startPos:int =
(stage.stageWidth-this.numOfElements*32-(this.numOfElements-1)*10)/2;
/* create and position taskbar elements */
this.elements = new Array();
for(var i:uint=0; i < this.numOfElements; i++)
{
var e = new element(
startPos + i*42,
75,
i,
this,
stage.stageWidth,
this.listOfURLs[i],
this.captions[i]
);
e.y = 75;
e.x = startPos + i*42;
this.addChild(e);
this.elements.push(e);
}
this.addEventListener(MouseEvent.MOUSE_MOVE, this.onMouseMotion)
}
Wypełniamy listę adresów URL i etykiet. Mechanika aplikacji tworząc element pobiera kolejno po jednym elemencie z obu tabel, tak więc 1 element menu będzie prowadził do adresu pod indeksem 0 w tablicy listOfURLs i miał przypisaną etykietę pod indeksem 0 z tablicy captions. Na potrzeby demonstracji wszystkie linki są jednakowe a etykiety są wypełnione popularnym "Lorem ipsum".
Tworzymy nowy obiekt typu MovieClip. W aplikacjach Flash / Actionscript rejestrowanie zdarzeń związanych z myszką, np ruch czy kliknięcie, działa tylko na tych fragmentach sceny gdzie znajduje się jakiś obiekt - nawet wtedy gdy rejstracja zdarzenia jest dodana na obiekcie sceny. Aby zapewnić, że cała aplikacja reaguje na akcje użytkownika, także tam gdzie nie znajdują się elementy menu, dodajemy do sceny nowy obiekt, który rozmniarem pokrywa całą scenę ale jest całkowicie niewidoczny. Obiekt ustawiamy w punkcie 0,0 a następnie rysujemy prostokąt o wymiarach sceny i dowolnym kolorze - kolor nie ma znaczenia ponieważ własność alpha całego obiektu zostaje ustawiona na 0.
Kolejnym krokiem jest ustalenie pozycji startowej elementów. Ponieważ aplikacja jest dostosowana do działania z dowolną ilością elementów, konieczne jest wyznaczenie punktu, w którym zostanie ustawiony pierwszy element - tak, aby wszystkie były wyśrodkowane. Punkt startowy wyznaczamy na podstawie rozmiaru elementów, ich ilości a także przestrzeni pomiędzy nimi. Wiemy, że domyślnie elementy mają rozmiar 32px, odstęp pomiędzy nimi wynosi 10px a ich ilość znajduje się w zmiennej numOfElements. Wyliczamy ile miejsca potrzebne jest na wyświetlenie całego menu na podstawie formuły:
szerokość menu = ilość elementów * ich rozmiar + (ilość elementów-1) * odstęp pomiędzy elementami
Mamy już wyliczoną szerokość, którą będą zajmowały wszystkie elementy. Znamy również rozmiar sceny. Chcemy wyznaczyć odległość pierwszego elementu od lewej granicy sceny, a ponieważ wiemy że elementy mają być wyśrodkowane wiemy rówież że odległość od lewej granicy ma być taka sama jak odległość od prawej granicy. Obliczamy więc różnicę pomiędzy szerokością menu a szerokością całej sceny i otrzymany wynik dzielimy przez 2:
punkt startowy = (szerokość sceny - szerokość menu)/2
W kodzie aplikacji zostało to oczywiście sprowadzone do jednego równania. Przechodzimy teraz do tworzenia samych elementów. Parametry konstruktora elementu opiszę przy okazji omawiania kodu samego konstruktora. Nowe elemnety zostaną dodane do listy a następnie ustawione w odpowiednim miejscu. W przypadku własności x, wyznaczamy ją na podstawie formuły:
x = punkt startowy menu + numer elementu * (rozmiar elementów + odstęp pomiędzy elementami)
Każdy taki element jest następnie dodawany do sceny. Ostatnim krokiem przygotowania aplikacji do działania jest zarejestrowanie obserwatora na zdarzenie ruchu myszki. Dzięki temu będzie można wykonać logikę odpowiedzialną za modyfikację rozmiaru elementów w zależności od położenia kursora. Kod funkcji wykonywanej przy zmianie pozycji kursora znajduję się poniżej:
private function onMouseMotion(evt:MouseEvent)
{
for(var i:uint=0; i < this.elements.length; i++)
{
this.elements[i].recalculate(evt.stageX, evt.stageY);
}
for(var j:uint=0; j < this.elements.length; j++)
{
this.elements[j].reposition();
}
}
Sama funkcja jest bardzo prosta, ze względu na to, że cała logika jest przeniesiona na stronę elementów. Aplikacja przechodzi przez wszystkie elementy i wywołuje metodą recalculate, która pozwala na podstawie pozycji kursora przeliczyć aktualny rozmiar danego elementu, a następnie wywołuje na każdym elemencie metodę reposition. Metody wywoływane są w oddzielnych pętlach z bardzo prostego powodu, pozycjonowanie elementu zależy od rozmiaru wszystkich elementów znajdujących się przed nim dlatego najpierw przeliczamy rozmiary wszystkich elementów a dopiero potem ustawiamy aktualny element.
Wiemy już jak działa kontroler, możemy więc przejść do ciekawszej części kodu a mianowicie klasy element.
import flash.display.MovieClip
import flash.display.Loader;
import flash.net.URLRequest;
import flash.net.navigateToURL;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.text.Font;
import flash.text.TextField;
import flash.text.TextFormat;
import flash.text.TextFieldAutoSize;
import fl.transitions.Tween;
import fl.transitions.easing.*;
Importujemy wszystkie niezbędne biblioteki.
public class element extends MovieClip
{
private var graphic:MovieClip;
public var xPos:int;
private var stageW:int;
private var yPos:int;
private var index:int;
private var url:String;
private var controller;
private var loader:Loader;
private var capBox:TextField;
private var aTw:Tween;
private var yTw:Tween;
Deklarujemy klasę z następującymi atrybutami:
- graphic - obiekt który wykorzystamy do zmiany kursora myszki nad elementem
- xPos, yPos - zmienne przechowujące pozycję elementu na scenie
- index - numer elementu
- url - adres URL na który element wskazuje
- controller - obiekt kontrolera
- loader - obiekt, którym załadujemy obrazek wyświetlany przez ten element
- capBox - obiekt wyświetlający etykietę elementu
- aTw, yTw - obiekty Tween odpowiadające z pokazanie i usunięcie etykiety elementu
public function element(x:int, y:int, i:int, c, s:int, URL:String, cap:String)
{
this.xPos = x;
this.yPos = y;
this.index = i;
this.stageW = s;
this.url = URL;
this.controller = c;
this.loader = new Loader();
this.loader.contentLoaderInfo.addEventListener(
Event.COMPLETE, this.loadComplete
);
this.loader.load(new URLRequest( "icos/"+this.index+".png" ));
var tF:TextFormat = new TextFormat();
tF.font = "Verdana";
tF.size = 16;
tF.color = 0x000000;
tF.align = "center";
this.capBox = new TextField();
this.capBox.text = cap;
this.capBox.width = this.stageW;
this.capBox.x = 0;
this.capBox.y = y + 30;
this.capBox.antiAliasType = "advanced";
this.capBox.embedFonts = true;
this.capBox.setTextFormat(tF);
this.capBox.defaultTextFormat = tF;
this.capBox.alpha = 0;
this.controller.addChild(this.capBox);
this.addEventListener(MouseEvent.MOUSE_OVER, this.showCaption);
this.addEventListener(MouseEvent.MOUSE_OUT, this.hideCaption);
}
Konstruktor przyjmuje 7 atrybutów: x - pozycja elementu na osi X, y - pozycja elementu na osi Y, i - numer elementu, c - obiekt kontrolera, s - szerokość sceny, URL - adres url na który obiekt wskazuje, cap - treść etykiety elementu. Przekazane parametry są następnie przypisywane do odpowiednich atrybutów klasy. Posiadając już wszystkie niezbędne dane przystępujemy do budowania elementu. Tworzymy obiekt ładujący zewnętrzny obrazek, rejestrujemy handler na zdarzenie zakończenia ładowania a następnie rozpoczynamy ładowanie podając adres obrazka. Na potrzeby przykładu obrazki nazywają się tak samo jak numer elementu jednak nic nie stoi na przeszkodzie aby nazwę również przekazywać w postaci parametru.
Kolejnym krokiem jest utworzenie obiektu etykiety. Aby w pełni wykorzystać możliwości stylowania tekstu tworzony jest również obiekt TextFormat. Ze względu na wykorzystywanie w procesie pokazywania/ukrywania etykiety atrybutu alpha konieczne jest ustawienie atrybutu embedFonts na true, co z kolei wymusza dołączenie czcionki do projektu - w naszym przypadku Verdany. Gotowy obiekt reprezentujący etykietę dołączamy do sceny.
Konstruktor kończymy zarejestrowaniem obsługi zdarzeń najechania na element i opuszczenia elementu kursorem myszki.
private function loadComplete(evt:Event)
{
this.loader.width = 32;
this.loader.height = 32;
this.addChild(this.loader);
this.graphic = new MovieClip();
this.graphic.graphics.beginFill(0xffffff);
this.graphic.graphics.drawRect(0, 0, 32, 32);
this.graphic.graphics.endFill();
this.graphic.alpha = 0;
this.graphic.buttonMode = true;
this.addChild(this.graphic);
this.graphic.addEventListener(MouseEvent.CLICK, this.clicked)
}
Metoda loadComplete zostaje wywołana automatycznie po zakończeniu ładowania obrazka. Ustawiamy tutaj rozmiar ikonki, podłączamy ją do sceny i dodajemy dodatkowy, całkowicie niewidoczny element pozwający nam zmienić kursor myszki gdy znajduje się on nad elementem.
private function clicked(evt:MouseEvent)
{
var req:URLRequest = new URLRequest(this.url);
navigateToURL(req, '_self');
}
Jak sama nazwa wskazuje, metoda ta zostanie wywołana gdy użytkownik kliknie na element. Nie dzieje się tutaj nic ponad przekierowaniem użytkownika na przekazany do konstruktora adres URL.
public function recalculate(x:int, y:int)
{
var distX:int = Math.abs(this.x + 16 - x);
if (distX < 100)
{
this.scaleX = 1 + (100 - distX)/100;
this.scaleY = 1 + (100 - distX)/100;
this.y = this.yPos - (100 - distX)/100*48;
}
else
{
this.y = this.yPos;
this.scaleX = 1;
this.scaleY = 1;
}
}
Metoda recalculate jest jednym z najważniejszych fragmentów kodu naszej aplikacji. Przyjmuje ona dwa atrybuty reprezentujące aktualną pozycję kursora myszki względem całej sceny. Następnie na podstawie pozycji X określany jest aktualna odległość kursora od środka elementu. Używamy wartości absolutnej ponieważ interesuje nas odległość bez względu na to czy kursor jest z prawej czy lewej strony elementu. Jeśli odległość przekracza 100px rozmiar elementu nie ulega zmiania. W przypadku gdy kursor jest bliżej niż 100px określamy na podstawie odległości o ile powiększyć element. Patrząc na warunki graniczne otrzymujemy:
- skala ikonki = 1 + (100-100)/100 = 1 - dla odległości równejj 100px
- skala ikonki = 1 + (100-0)/100 = 2 - dla odległości równejj 0px
Oznacza to, że ikonka jest normalnej wielkości dla 100px i jest dwókrotnie większa jeśli odległość wynosi 0. Musimy jeszcze zmienić pozycję Y ikonki aby uzyskać efekt podskoku. Należy pamiętać że korzystanie z atrybutów scaleX i scaleY powoduje zmianę rozmiaru obiektu przy zachowaniu jego pozycji. Z tego powodu musimy jeszcze pozycję przesunąć o dodatkowy parametr - w tym przypadku 48. Jeśli zamienimy go na 32 to ikonki będą rosły proporcjonalnie w góre i w dól ale bez efektu podskoku.
public function reposition()
{
var dL:int = -10;
for(var i:uint=0; i < this.controller.elements.length; i++)
dL += this.controller.elements[i].width + 10;
dL = (this.stageW - dL)/2;
if (this.index == 0)
this.x = dL;
else
this.x =
this.controller.elements[this.index-1].x
+
this.controller.elements[this.index-1].width
+
10;
}
Metoda reposition jest równie istotna co recalculate. Jej zadaniem jest wyznaczenie wartości X dla aktualnego elementu, tak aby zachować wyśrodkowanie wszystkich elementów i ich stały odstęp uwzględniając ich zmienny rozmiar. Pierwszym krokiem jest ustalenie pozycji startowej całego menu. Używamy tutaj dokładnie tej samej mechaniki co w przypadku konstruktora klasy Main. Jedna różnica polega na tym że nie wiemy jaki jest rozmiar elementów, więc musimy przejść przez wszystkie i policzyć ich rozmiary. Punkt startowy przechowywany jest w zmiennej dL a jej początkowa wartość jest ustlona na -10 aby wyrównać ilość elementów i ich odstępów - jak pamiętamy z konstruktora Main aby policzyć poprawną odległość powinniśmy policzyć (ilość elementów -1) odstępów.
Gdy mamy już obliczoną pozycję startową całego menu możemy przejść do określenia pozycji aktualnego elementu. Jeśli element jest pierwszym elementem na liście to wiemy że jego pozycja startowa będzie równa pozycji startowej całego menu. W przeciwnym wypadku oczywiste jest, że pozycja aktualnego elementu będzie równa pozycji elementu poprzedniego + jego szerokość + 10 (stały odstęp pomiędzy elementami).
private function showCaption(evt:MouseEvent)
{
if (aTw is Tween)
aTw.stop();
if (yTw is Tween)
yTw.stop();
this.aTw = new Tween(
this.capBox, "alpha", Regular.easeIn, this.capBox.alpha, 1, 9, false
);
this.aTw.FPS = 45;
this.yTw = new Tween(
this.capBox, "y", Regular.easeIn, this.capBox.y, this.yPos+40, 9, false
);
this.yTw.FPS = 45;
}
private function hideCaption(evt:MouseEvent)
{
if (aTw is Tween)
aTw.stop();
if (yTw is Tween)
yTw.stop();
this.aTw = new Tween(
this.capBox, "alpha", Regular.easeOut, this.capBox.alpha, 0, 9, false
);
this.aTw.FPS = 45;
this.yTw = new Tween(
this.capBox, "y", Regular.easeOut, this.capBox.y, this.yPos+30, 9, false
);
this.yTw.FPS = 45;
}
Metody showCaption i hideCaption są używane aby pokazać i ukryć etykietę aktualnego elementu. Są wywoływane automatycznie gdy kursor znajdzie się nad lub opuści element. Ich Mechanika nie wymaga żadnego tłumaczenia.
W artykule pokazałem jak w bardzo łatwy sposób uzyskać przy pomocy Flash / Actionscript efekt znany z popularnego systemu operacyjnego. Jak widać fajne animacje nie są już zarezerwowane tylko dla aplikacji systemowych - strony www, również mogą zaskakiwać bardzo przyjemnymi efektami graficznymi.