W kolejnej części kursu mootools zapoznamy się z plikiem fx.styles.js - ma on podobne zadanie jak plik fx.style.js - służy do animowania elementów. Różnica polega na tym, że za pomocą klasy Fx.Styles tworzymy efekty oparte o zmianę kilku właściwości CSS naraz. W tej części kursu zajmiemy się realizacją dwóch zadań - dodania jeszcze jednego efektu do menu z poprzedniej części kursu oraz stworzymy menu a'la fisheye znane z MacOS :)

Najpierw jednak mała dawka teorii. Klasa Fx.Styles zawiera tylko jedną metodę - start, jednak jej budowa znacznie odbiega od składni metody start w klasie Fx.Style. Argumentem tej metody jest obiekt złożony z par :

"nazwa_właściwości" : "wartość_właściwości"

Przy czym jest to uproszczony zapis, który spowoduje zmianę danej właściwości ze stanu początkowego do danej wartości. Możemy też określić początek przedziału. W tym wypadku składnia pary wygląda następująco:

"nazwa_właściwosci" : ["wartość_początkowa","wartość_końcowa"]

Sam efekt tworzymy następująco:

var efekty = new Fx.Styles("element",{opcje});

Jak widać w porównaniu do klasy Fx.Style znikł nam parametr określający zmienianą właściwość. Został on zastąpiony wspomnianymi parami i występuje przy metodzie start:

efekty.start({
   "właściwość1" : ['2px','10px'],
   "właściwość2" : ['#FFF','#000']
});

Widzimy więc że składnia metody start się lekko skomplikowała, ale możemy przecież stosować prostszy zapis par właściwość-wartość.

Podobnie jak plik fx.style.js, także plik fx.styles.js posiada alternatywny sposób tworzenia animacji poprzez klasę Element - używana jest do tego celu metoda effects. Jej składnia wygląda następująco:

$('element').effects({opcje});

Użycie polega oczywiście na użyciu metody start. Na przykład:

var efekty = $('element').effects({opcje});
efekty.start({
   "właściwość1" : ['2px','10px'],
   "właściwość2" : ['#FFF','#000']
});

Myślę, że całość nie jest trudna do zrozumienia, bo zmiany w stosunku do klasy Fx.Style są dość logiczne. Przejdźmy do praktyki :)

Pamiętacie zapewne menu z poprzedniej części kursu. Miało ono następujący kod JavaScript:

window.addEvent("domready",function(){
   $$("#menu li").each(function(element){
      var efekt1 = new Fx.Style(element,'background-color', {duration:600, wait:false});
      var efekt2 = new Fx.Style($E('a',element),'color', {duration:600, wait:false});

      element.addEvent('mouseenter', function(){
         efekt1.start('#FFF','#000');
         efekt2.start('#000','#FFF');
      });
      element.addEvent('mouseleave', function(){
         efekt1.start('#000','#FFF');
         efekt2.start('#FFF','#000');
      });
   });
});

Teraz chciałbym dodać do niego jeszcze jeden efekt - przesuwanie się w górę linka po najechaniu na opcję menu i jego opadanie po odjechaniu kursora z obszaru opcji. Chodzi tutaj o zmianę dwóch wartości - padding-top i padding-bottom. Ponieważ zmiany tych wartości dotyczą elementu listy więc odnoszą się do tego samego elementu co efekt przypisany zmiennej efekt1. Z użyciem klasy Fx.Styles stworzymy zmianę trzech wartości naraz - wspomnianych paddingów i koloru tła.

Przy okazji pozwolę sobie zmienić też nazewnictwo - zmiana elementu listy otrzyma nazwę efekty, a zmiana linka dostanie nazwę efekt.

Zmieniamy zatem kod :

var efekty = new Fx.Styles(element,{duration:600, wait:false});
var efekt = new Fx.Style($E('a',element),'color', {duration:600, wait:false});

Dzięki temu zyskujemy możliwość zastosowania zmian wielu właściwości jednym efektem przy wystąpieniu zdarzeń mouseenter i mouseleave:

element.addEvent('mouseenter', function(){
   efekty.start({
      'background-color': ['#FFF','#000'],
      'padding-top': [12,4],
      'padding-bottom': [12,20]
   });
   efekt.start('#000','#FFF');
});

element.addEvent('mouseleave', function(){
   efekty.start({
      'background-color': ['#000','#FFF'],
      'padding-top': [4,12],
      'padding-bottom': [20,12]
   });
   efekt.start('#FFF','#000');
});

jak widzicie zastosowałem dłuższy zapis par właściwość-wartości, a zmiana wysokości będzie wynosiła 8 pikseli.

I po tych zmianach możemy obejrzeć nasze ulepszone menu:

PRZYKŁAD 1

Myślę, że wygląda to już całkiem ciekawie. Może kolory nie są idealne, ale dobrałem kontrastowe by widać było wyraźnie "smugę" przy przesuwaniu kursora nad kolejnymi opcjami menu :)

Pora na coś bardziej skomplikowanego, ale opartego na podobnej zasadzie działania. Stworzymy sobie menu typu fisheye - dla niewtajemniczonych podpowiem, że chodzi o powiększanie się danego obrazka do określonego rozmiaru po najechaniu nań kursorem. Dodatkowo powiększeniu (choć mniejszemu) ulegają też sąsiednie obrazki - w naszym wypadku powiększać będziemy obrazek nad którym znajduje się kursor i dwa sąsiednie obrazki.

Zacznijmy od podstaw - aby uzyskać ładny efekt potrzebujemy zmieniać tak rozmiar obrazka by powiększał się on rozszerzając w dół - dlatego trzeba ustalić sztywny rozmiar elementu listy nieuporządkowanej - w naszym wypadku 128x128px. Dodatkowo trzeba ustawić podstawowy rozmiar obrazka - 48x48px. Przy powiększaniu obrazek będzie zmieniał rozmiar do 128x128px, a dwa sąsiednie obrazki do rozmiaru 72x72px.

Działanie skryptu będzie opierało się w sporym stopniu na tablicy przechowującej efekty - przy dodawaniu za pomocą funkcji forEach zdarzeń, każde zdarzenie zostanie umieszczone także w tablicy efekty. Dlaczego ? Żeby móc łatwo wywołać efekt przypisany do poprzedniego i następnego z kolei elementu (w odniesieniu do elementu nad którym aktualnie znajduje się kursor).

Tworzymy zatem tablicę, która przechowa nasze uporządkowane efekty:

var efekty = [];

Podstawa animacji będzie oparta na takiej samej metodzie jak w poprzednim przykładzie:

$$("#menu li").each(function(element,index){
   // tutaj będą dodawane eventy
}

Każdy obieg funkcji each będzie się składał z dodania dwóch zdarzeń - mouseenter i mouseleave oraz z umieszczenia efektu w tablicy.

Efekt przypisany do danego elementu listy umieszczamy w tablicy następująco:

efekty[index] = new Fx.Styles($E('img',element),{duration:400, wait:false});

Jak widać korzystamy przy tym z automatycznie generowanej zmiennej index i z funkcji selekcji elementu - gdybyśmy od razu odwołali się do elementu img zamiast elementu li to bardzo ograniczylibyśmy obszar nad którym trzeba umieścić kursor by uruchomić animację.

Po dodaniu efektu do tablicy czas na zdarzenie mouseenter:

element.addEvent('mouseenter', function(){
   efekty[index].start({
       'width': 128,
       'height': 128
   });

    if(efekty[index-1]){
      efekty[index-1].start({
          'width': 72,
         'height': 72
      });
   }

    if(efekty[index+1]){
       efekty[index+1].start({
          'width': 72,
         'height': 72
      });
   }
});

Kod może się wydać długi, ale jest prosty - działa w następujący sposób:

  • uruchamiany jest efekt powiększenia obrazka nad którym znajduje się kursor,
  • sprawdzane jest czy istnieje efekt dla poprzedniego elementu (jeśli nie istnieje efekt to nie ma też elementu),
  • jeżeli efekt istnieje to poprzedni element jest animowany,
  • sprawdzane jest czy istnieje efekt dla następnego elementu (jeżeli tego efektu nie ma to znaczy, że dany element jest ostatnim).

Bardzo ważne jest użycie w metodzie start pojedynczych wartości, a nie tablic - gdybyśmy użyli tablic to animacja by wykonywała się w sztywnym przedziale wartości, a może się zdarzyć, że użytkownik przejedzie z jednego elementu na sąsiedni (powiększony) i spowoduje to powiększenie od mniejszej wartości niż aktualna dla elementu - jakby to wyglądało nie muszę chyba opisywać :)

Na koniec tworzymy zdarzenie mouseleave:

element.addEvent('mouseleave', function(){
   efekty[index].start({
       'width': 48,
       'height': 48
   });

    if(efekty[index-1]){
      efekty[index-1].start({
         'width': 48,
         'height': 48
      });
    }

   if(efekty[index+1]){
      efekty[index+1].start({
          'width': 48,
         'height': 48
      });
    }
});

Działa ono na podobnej zasadzie co poprzednie zdarzenie - zmieniają się tylko wartości do jakich dążą właściwości w animacji.

Cały kod umieszczamy oczywiście w zdarzeniu onload i gotowe :

PRZYKŁAD 2

Oczywiście jest to bardzo podstawowa wersja tego menu. Można ją oczywiście samemu ją rozbudować i osobiście to polecam. Moje propozycje rozbudowy skryptu:

  • dodać do linków atrybut title i podczas dodawanie eventów, wykonać generowanie na ich podstawie etykiet tekstowych, które pokażą się przy animacji (przy towarzyszącej animacji właściwości opacity i font-size). Potrzeba przy tym oczywiście paru zmian kodu CSS ;)
  • dodać możliwość dynamicznej zmiany rozmiarów końcowych animowanych elementów (czyli jak ja to nazywam - możliwość zmiany "wyrazistości animacji")

Jeżeli ktoś to przypadkiem zrobi to niech pochwali się w komentarzach do tego wpisu :)

W następnej części kursu zajmiemy się plikiem fx.elements.js.

Komentarze do wpisu "Mootools 1.1 - Fx.Styles.js":

1. Bigismall napisał(a):
30 lipca 2007, 18:10:09

W sumie masz już materiał na e-booka. Pomyśl nad tym żeby to na koniec zebrać wszystko w e-book. Zawsze to pare złotych…

2. Dziudek napisał(a):
30 lipca 2007, 18:14:32

@Bigismall – pomyślę o tym przy okazji mootools 1.2 ;] Choć nie wiem czy będzie to ebook płatny ;) Generalnie kurs robię dla funu, a nie pieniędzy ;) A poza tym mootools 1.2 wyjdzie imho za miesiąc, góra dwa – po co wyzyskiwać pieniądze od ludzi ;]

3. Pizzadude napisał(a):
30 lipca 2007, 21:02:37

Drugi przykład – za to właśnie uwielbiam Mootools. To całe kombinowane krótkim i prostym kodem. Prawie każdy dziwny pomysł można tchnąć w życie.

4. Dziudek napisał(a):
30 lipca 2007, 21:05:00

@Pizzadude – Riddle by pewnie napisał, że w jQuery da się jeszcze krócej ;] Ale nie wiem czy tak czytelnie :) Swoją drogą zarówno pierwszy jak i drugi przykład można łatwo rozszerzyć, bo wystarczy dodać kolejny element listy według wzoru… Także metoda z pętlą jest według mnie najbardziej elastyczna :)

5. Bigismall napisał(a):
31 lipca 2007, 12:20:44

Temat szybkości się już pojawił, ale jeszcze raz zapraszam do http://blog.olicio.us/2007/07/19/mootools-vs-jquery-speedtest/

Co do e-booka, to twoja sprawa, ale pare złotych jestem w stanie zapłacić, tak chociażby po to aby mieć to wszystko w kupie.

Życzę wytrwałości w pisaniu.

6. maciek napisał(a):
22 maja 2010, 17:04:31

A ja wziąłem się za zadania domowe :D
function fisheye (elements, images, titles, countAnimated, thumbSize, maxSize)
elements - tablica elementów do których dodajemy zdarzenia (linki bądź elementy listy)
images - tablica powiększanych obrazków
titles - tablica elementów do umieszczenia tytułów aplikacji
count Animated - ile elementów wokół ma być powiększanych według równania funkcji kwadratowej (zaczerpnąłem kształt krzywej)
thumbSize - współczynnik powiększenia rozmiaru małego od dużego (liczba od 0 do 1)
maxSize - rozmiar w pikselach maksymalnego obrazka

Dodatkowo dodałem obsługę pojedynczego powiększanego obrazka z tablic elementów oraz przypadek, gdy dostarczane dane nie są tablicami, a pojedynczymi elementami (elements i images), z racji tego kod wyszedł dosyć długi, sama animacja tablicy to około 30 linijek, a razem z przypadkami które wymieniłem wyżej niestety aż 80 :(

Dodaj komentarz:

Textile Lite włączony ( szczegółowy opis znaczników ):
*strong* | # lista numerowana | * lista wypunktowana | _em_ | __italic__ | "link":http:// | bq. cytat.