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
zobacz ofertę...

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
zobacz portfolio...
Piotr Marczyszyn

Papervision 3D: karuzela zdjęć

Jeden z poprzednich postów na naszym blogu pokazywał jak można bardzo łatwo stworzyć ciekawą galerią flash przy wykorzystaniu Papervision 3D. Pomimo, że minęło od tamtej pory sporo czasu, zainteresowanie wykorzystaniem efektów 3D w aplikacjach Flash nie zmalało. Tym razem pokażę jak wykorzystując ten sam zestaw narzędzi można napisać aplikację popularnie zwaną "karuzelą". Zasada działania takiej aplikacji polega na zbudowaniu przewijalnej listy zdjęć/obrazków, z której jeden element jest wyeksponowany na tle elementów sąsiadujących.


Nie jest trudno wyobrazić sobie zastosowanie dla tego typu elementu. Może to być zarówno prezentacja portfolio dla fotografa jak i efektowna prezentacja oferty. Przy odrobinie modyfikacji można nawet przerobić prezentowany pomysł na nieszablonowe menu.

Zanim zaczniemy przeglądać kod, zerknijmy na efekt końcowy:

Wgyląda bardzo fajnie. Równie ważna co sam wygląd jest wydajność naszej aplikacji. W sieci można znaleźć kilka stron używających identycznych efektów ale znacznie mniej płynnych. Wydajność to rzecz, o której niestety wielu developerów zapomina.

Kod aplikacji zorganizowany jest tak samo jak w projekcie galerii. Jedyną różnicą są nazwy plików:

Duży fragment kodu jest bardzo podobny do tego użytego w aplikacji galerii. Z tego powodu nie będę opisywał elementów współnych - jeśli coś z nieopisanych fragmentów jest niejasne, zapraszam do postu "Papervision 3D: galeria". Dla wygody udostępniłem pełny kod projektu (tylko do zastosowań niekomercyjnych). Archiwum zawiera wszystkie niezbędne elementy takie jak kod Papervision 3D.

W pliku Main.as nie ma żadnych skomplikowanych fragmentów kodu. Jedyny element, który może budzić wątpliwości to:

if (numOfItems%2 == 0) { if (i == numOfItems/2) { this.cam.x = p.Xpos; p.setYaw(0); } if (i < numOfItems/2) p.setYaw(-60); if (i > numOfItems/2) p.setYaw(60); } else { if (i == Math.floor(numOfItems/2)) { this.cam.x = p.Xpos; p.setYaw(0); } if (i < Math.floor(numOfItems/2)) p.setYaw(-60); if (i > Math.floor(numOfItems/2)) p.setYaw(60); }

Powyższy kod pozwala określić startowy układ każdego elementu na podstawie jego indeksu. Jeśli elementów jest parzysta ilość to na początku jest wybrany element o indeksie (ilość_elementów/2), w przeciwnym wypadku wybieramy element o indeksie (ilość_elementów-1)/2. Wszystkie elementy po lewej stronie od elementu centralnego zawsze obrasamy o 60 stopni lewo, elementy po stronie prawej o 60 stopni w prawo.

Kod kontrolera jest równie trywialny co klasy Main. Najważniejsza metoda to:

public function clicked(index:Number) { Tweener.addTween( this.camera, {x:this.Elements[index].Xpos, time:1, transition:"easeOutExpo"} ); for (var i:uint=0; i<this.Elements.length; i++) { if (index == i) this.Elements[i].setYaw(0); if (index < i) this.Elements[i].setYaw(60); if (index > i) this.Elements[i].setYaw(-60); } }

Metoda clicked() wywoływana jest przez element, gdy zarejestruje on na swojej powierzchni zdarzenie MouseEvent.CLICK. Do metody przekazywany jest indeks element, który został kliknięty. Sama metoda zmienia pozycję kamery, tak aby pokazywała ona element kliknięty, a następnie poprawia wychylenia wszystkich elementów aby pasowały do nowego układu. Metoda zostanie wywołana tylko gdy użytkownik kliknie na elemencie, który nie jest aktualnie wyeksponowany. Kliknięcie elementu środkowego, powoduje wywołanie innej metody klasy Element odpowiedzialnej za jego przybliżenie/oddalenie.

Najciekawsze rzeczy dzisją się w klasie Element:

public function init() { this.mate = new BitmapFileMaterial("photos/"+this.fileName); this.mate.interactive = true; this.mate.doubleSided = true; this.mate.smooth = true; /*create copy of the main texture for reflection rendering*/ this.refMate = new BitmapFileMaterial("photos/"+this.fileName); this.refMate.smooth = true; this.refMate.doubleSided = true; this.refMate.addEventListener(FileLoadEvent.LOAD_COMPLETE, this.materialLoaded); this.plane = new Plane( this.mate, this.eWidth, this.eHeight, this.vVertices, this.hVertices); this.plane.x = this.Xpos; this.plane.useOwnContainer = true; this.refPlane = new Plane( this.refMate, this.eWidth, this.eHeight, this.vVertices, this.hVertices); this.refPlane.x = this.Xpos; this.refPlane.y = -1*this.eHeight; this.refPlane.rotationX = 180; this.refPlane.useOwnContainer = true; this.scene.addChild(this.plane); this.scene.addChild(this.refPlane); }

Metoda init() wywoływana jest tylko podczas tworzenia obiektów klasy Element. Tworzymy nowy obiekt Plane i ładujemy dla niego teksturę. Należy zwrócić uwagę, że tworzony jest również drugi obiekt typu Plane, dla którego ładujemy dokładnie tą samą teksturę - płaszczyzna ta zostanie wkorzystana do utworzenia efektu odbicia pod każdym zdjęciem.

Po załadowaniu tekstury odbicia wywołana zostanie metoda materialLoaded():

private function materialLoaded(evt:FileLoadEvent) { var matrix:Matrix = new Matrix(); matrix.createGradientBox( this.eWidth, this.eHeight/4, Math.PI / 2, 0, 100 ); var linear:String = GradientType.LINEAR; var colors:Array = [0xFFFFFF, 0xFFFFFF]; var alphas:Array = [0.0, .8]; var ratios:Array = [0, 255]; var spread:String = SpreadMethod.PAD; var gradient:Shape = new Shape(); gradient.graphics.beginGradientFill( linear, colors, alphas, ratios, matrix, spread ); gradient.graphics.drawRect(0, 0, this.eWidth, this.eHeight); gradient.graphics.endFill(); var gradientBitmap:BitmapData = new BitmapData( gradient.width, gradient.height, true, 0 ); gradientBitmap.draw( gradient ); var result:BitmapData = new BitmapData( this.eWidth, this.eHeight, true, 0 ); result.copyPixels( this.refMate.bitmap, result.rect, new Point(), gradientBitmap, new Point(), true ); this.refMate.bitmap = result; }

Powyższa metoda jest najtrudniejszym fragmentem naszej karuzeli. Załadowaną teksturę musimy przystosować do roli, którą ma spełniać. Ponieważ nie chcemy, aby dodanie zdjęcia do karuzeli wiązało się z szykowaniem dla niego tekstury odbicia, musimy na załadowany zdjęciu wykonać następujące operacje:

Obrót uzyskaliśmy już podczas tworzenia płaszczyzn. Zamiast obracać teksturę, obróciliśmy całą płaszczyznę. Gradient dodajemy właśnie w metodzie materialLoaded().

Rozpoczynamy od utworzenia nowego obiektu Shape, który posłuży nam jako maska. Następnie rysujemy kwadrat o rozmiarach maski i wypełnienu zdefiniowanym na podstawie macierzy gradientu. Więcej informacji na temat rysowania gradientów można znależć w dokumentacji Adobe.

Kolejny krokiem jest utwordzenie obiektu BitmapData zawierającego odwzorowanie naszego gradientu. Będzie nam on potrzebny żeby wykonać operację nakładania bitmap.

W tym momencie mamy już wszystko co potrzebujemy. Wykorzystując metodę copyPixels() nakładamy bitmapę gradientu na bitmapę tekstury i zapisujemy w nowym obiekcie. Ponieważ gradient posiadał różne wartości kanału alfa na obu końcach, bitmapa wynikowa będzie wyglądała podobnie. wartości kanałów RGB pozostaną bez zmian, jednak kanał alfa będzie systematycznie malał na całej wysokości bitmapy. Tym sposobem tworzymy iluzję zanikania obrazu.

Tak przygotowaną bitmapę nakładamy jako teksturę na płaszczyznę odbicia.

Pozostało nam już tylko przygotowanie metody zmieniającej wychylenie płaszczyzny:

public function setYaw(yaw:Number) { if (this.yaw != yaw) { Tweener.addTween( this.plane, {rotationY:yaw, time:1, transition:"easeOutExpo"} ); Tweener.addTween( this.refPlane, {rotationY:yaw, time:1, transition:"easeOutExpo"} ); } if ((yaw == 0) && (this.yaw != 0)) { Tweener.addTween( this.plane, {z:-150, time:1, transition:"easeOutExpo"} ); Tweener.addTween( this.refPlane, {z:-150, time:1, transition:"easeOutExpo"} ); } else { if (((this.yaw == 0) && (this.yaw != yaw)) || (this.yaw == -1)) { Tweener.addTween( this.plane, {z:150, time:1, transition:"easeOutExpo"} ); Tweener.addTween( this.refPlane, {z:150, time:1, transition:"easeOutExpo"} ); } } if (yaw != 0) this.plane.addEventListener( InteractiveScene3DEvent.OBJECT_PRESS, this.onPressF ); else this.plane.addEventListener( InteractiveScene3DEvent.OBJECT_PRESS, this.onPressFull ); this.yaw = yaw; }

Metoda decyduje na podstawie otrzymanej wartości wychylenia i aktualnej wartości wychylenia jakie operacje wykonać. Należy zauważyć, że jeśli nie następuje zmiana wychylenia nie są tworzone nowe animacje. Metody odpowiadające za przybliżanie/oddalanie są bardzo proste i raczej nie wymagają tłumaczenia.


Jak widać, Papervision 3D to bardzo potężne narzędzie. Pozwala programistom Actionscript szybko i łatwo tworzyć nawet bardzo skomplikowane efekty graficzne. Widać również, że wydajność aplikacji opartych na tym narzędziu jest zadowalająca. W jednym z następnych postów, przekonamy się jak wypada wydajność Papervision 3D w odniesieniu do wydajności innego popularnego silnia 3D, Away 3D. Zapraszam.