Jak zrobić skalowalny film z nieskalowanymi elementami w środku
Na początku można stworzyć 2 movieClipy. Jeden będzie naszym silnikiem i będzie składał się z kilku klatek (co najmniej 2). Drugi będzie zawierał fotkę, której (nie)chcemy skalować i nadamy mu instanceName fotka.
Na naszym "silniku" umieścimy te akcje:
onClipEvent(load){
//nie może być użyty listener "onResize" bo film ma domyslnie
//ustawione Stage.scalemode("showAll");
lsw=0;//last width
lsh=0;//last height
dys=0;//default y shift
dxs=0;//default x shift
}
a w jego pierwszej klatce:
dsw=Stage.width;
dsh=Stage.height;
r=dsw/dsh;
Stage.scaleMode = "noScale";
sw=Stage.width;
sh=Stage.height;
Stage.scaleMode = "showAll";
if (sw != lsw || sh != lsh){
if (r < sw/sh){
_parent.fotka._xscale = (dsh/sh)*100;
_parent.fotka._yscale = (dsh/sh)*100;
}
if (r > sw/sh){
_parent.fotka._xscale = (dsw/sw)*100;
_parent.fotka._yscale = (dsw/sw)*100;
}
lsw=sw;
lsh=sh;
}
r oznacza współczynnik początkowej szerokości strony (dsw) do jej wysokości (dsh). Co jest istotne: wymiary strony (sceny, stage) nie są tym samym co wymiary filmu (obszaru roboczego), dlatego czasami widać rzeczy poza obszarem roboczym - scena jest od niego po prostu w takich przypadkach większa. Trochę to zagmatwane, ale rozróżnienie tych pojęć w tym artykule jest bardzo ważne. Dlatego zalecam równoległe testowanie we flashu, rzeczy tutaj opisanych.
Niestety nie da się pobrać wymiarów obszaru roboczego ale wymiary sceny, z tąd te komplikacje. Otóą obrazki należy skalować albo na podstawie szerokości strony albo na podstawie wysokości - nigdy na podstawię obu jednocześnie, co wyniknie z dalszego tekstu.
Łatwiej to zrozumieć na podstawie przykładu. Dlaczego tak musi być? Załóżmy, że mamy film o początkowej wysokości i szerokości 100px, co daje nam współczynnik r równy 1. Film oczywiście się skaluje do pełnego okna, ale na początku otworzył się w okienku o wysokości 100px i szerokości 50px. Wewnątrz filmu jest obrazek, który ma być "nieskalowany". Co się dzieje gdy ustalimy, że przy zmianie szerokości filmu wymiary obrazka nieskalowalnego będą obliczane na podstawie szerokości, a przy zmianie wysokości na podstawie wysokości? Na początku wszystko będzie ok. Zmieniając szerokość strony będą zwiększać się wymiary filmu. Ale gdy wysokość filmu będzie równa wysokości okna, mimo dalszego poszerzania okna film nie będzie się skalował. Oczywiście film nie będzie, ale scena będzie się skalowała wraz ze zmianami szerokości okna w wyniku czego do obrazka nieskalowalnego będą przesyłane błędne informacje na temat wymiarów filmu i obrazek będzie pomniejszany mimo nie zmieniających się wymiarów filmu. I tutaj z pomocą przychodzi nam wspomniany wcześniej współczynnik r (ratio). Przy domyślnych rozmiarach naszego filmu (100x100) wynosi on 1. I tyle wynosi stosunek szerokości do wysokości (sw/sh) przy proporcjonalnej zmianie wymiarów filmu. Jeśli ten stosunek jest wyższy od r (1 w naszym przypadku) oznacza to, że film się nie skaluje ponieważ jego wymiary są ograniczone przez wysokość okna i film się nie powiększy mimo zwiększania szerokości! W tym przypadku wymiary nieskalowalnego obrazu powinny być obliczane na podstawie wysokości sceny, ponieważ osiągnęła ona swoją maksymalną wielkość i to ona determinuje wymiary filmu. Gdy sw/sh < r zachodzi sytuacja odwrotna i wymiary powinny być liczone na podstawie szerokości sceny.
Wady
Niestety takie rozwiązanie ma wady i nadaje się tylko do małych animacji. Fragment kodu:
Stage.scaleMode = "noScale";
sw=Stage.width;
sh=Stage.height;
Stage.scaleMode = "showAll";
powoduje, że flash w ułamku sekundy zmienia zasadę skalowania sceny. Dzieje się to tak szybko, że nie ma to żadnych skutków na ekranie monitora, ale obserwując obciążenie procesora można zblednąć. Dlaczego taki kod musi być zastosowany? Ponieważ chcemy mieć prawdziwe wymiary sceny. Takie wymiary uzyskamy tylko wtedy gdy ustawimy Stage.scaleMode = "noScale";
(w każdym innym przypadku otrzymamy wymiary filmu, takie jakie zostały zdefiniowane w ustawieniach). Jednak po zastosowaniu takiego kodu skaluje się scena, ale nie skaluje się film (czyli nie skalują się elementy umieszczone na scenie). Można oczywiście rozwiązać to w ten sposób, że stosujemy kod Stage.scaleMode = "noScale";
co pozwala nam skalować scenę i pobierać jej prawdziwe wymiary, a następnie na podstawie tych wymiarów skalujemy odpowiednio wszystkie elementy na scenie umieszczone. Ale jest to rozwiązanie bezużyteczne, jeśli chcemy zastosować je do jednego lub dwóch obrazków w gotowym już projekcie.
Inna metoda pobierania wymiarów (2)
Drugą metodą, nie do końca skuteczną we wszystkich przeglądarkach jest zastosowanie JavaScript do odczytywania wielkości okna przeglądarki i przesyłaniu tych zmiennych do Flasha przy każdym skalowaniu okna. Taki kod może wyglądać tak:
function newWH(){
document.index.SetVariable("sw", document.body.clientWidth);
document.index.SetVariable("sh", document.body.clientHeight);
}
gdzie index to id obiektu flash. Wywołujemy tą funkcję za pomocą onload
i onresize
umieszczonych w znaczniku body
.
Metoda 3
w trakcie opisywania.
Metoda ta wykorzystuje fakt, że istnieje możliwość pobrania wymiarów sceny jeśli film jest nieskalowany. W takim przypadku zawartość, która ma się skalować powinna być umieszczona w osobnym movieClipie - i to jego rozmiary będą zmieniane zależnie od rozmiarów sceny. W przykładowym kodzie tym movieClipem jest container
.
Dodatkowym elementem jest zastosowanie listenera. Jest to specjalny "obiekt" reagujący na jakieś zdarzenie - w tym przypadku, do sceny przypisujemy listenera typu onResize
, czyli reagującego na zmianę wymiarów. Gdy taka zmiana nastąpi, listener wywoła funkcję scale()
, zadeklarowaną wcześniej.
dsw=Stage.width;
dsh=Stage.height;
r=dsw/dsh;
Stage.scaleMode="noScale";
Stage.align="TL";
function scale(){
sw=Stage.width;
sh=Stage.height;
if (r < sw/sh){
content._xscale = (sh/dsh)*100;
content._yscale = (sh/dsh)*100;
}
if (r > sw/sh){
content._xscale = (sw/dsw)*100;
content._yscale = (sw/dsw)*100;
}
}
listener=new Object();
listener.onResize=function(){
scale();
}
scale();
Stage.addListener(listener);
Metoda 4
Kolejny sposób wykorzystuje to, że w każdej przeglądarce działają zmienne załadowane przy pomocy FlashVars np: <param name="FlashVars" value="zmienna=wartosc" />
. Niestety te zmienne są oczywiście ładowane tylko przy wywoływaniu obiektu Flash, dlatego niemożliwa jest ich ciągła aktualizacja (wymagałoby to ciągłego odświeżania strony). Ta metoda dynamicznie umieszcza tymczasowy obiekt flash na stronie (X)HTML i przekazuje do niego zmienne za pomocą FlashVars
. Następnie za pomoca połączenia LocalConnection
te zmienne przekazywane są do właściwego filmu. Gdy zachodzi potrzeba przesłania kolejnych zmiennych obiekt tymczasowy jest usuwany i tworzony od nowa, a za pomocą FlashVars
są przekazywane do niego nowe wartości zmiennych. Na stronie mustardlab można zobaczyć dokładny opis (eng.) z przykładami i plikami źródłowymi.