Strona wykorzystuje ciasteczka by usprawnić komfort z jej korzystania. Korzystając ze strony akceptujesz naszą Politykę Ciasteczek. X

Pasek Wyszukiwania we Flash'u [podstawowy]

Szczęśliwego Nowego Roku 2012!

Często w Internecie widuje pytanie o to jak stworzyć pasek wyszukiwania w filmiku (podobnie jak ma to większość standardowych odtwarzaczy wideo, np. na YouTube) jednak ostatecznie niewiele z tych osób go implementuje - dlaczego tak jest? Okazuje się, że problem leży w sposobie obsługi MovieClip'ów przez Flash'a, dlatego za nim przejdę dalej, poświęcę parę słów właśnie nim.
Może trochę trudno to sobie wyobrazić, ale każdy MovieClip w animacje to tak naprawdę osobny filmik który ma swoją linię czasu (i ustawienia) i kontrolowany jest całkowicie nie zależnie od pozostałych, co najlepiej zobrazuje poniższy przykład (wybierz dowolny moment, naciśnij prawy klawisz myszki i odhacz "odtwarzaj"):

Jak można byłoby się tego spodziewać, cała animacja zostanie natychmiast zatrzymana... poza piłką która jest MovieClip'em. Nie ma co się zagłębiać dlaczego tak jest - po prostu jest i nigdy się nie zmieni. Pozostaje nam jedynie szukać alternatywnych rozwiązań, a ponieważ poradzenie sobie z zatrzymywaniem MovieClip'ów bezpośrednio pomoże nam przy tworzeniu paska wyszukiwania dobrze jest przyjrzeć dwóm możliwym obejściom tego problemu:
1. Pozbycie się wszystkich MovieClip'ów w animacji.
2. Ręczne zatrzymanie każdego z MóvieClip'ów za pomocą ActionScript.
Pierwsze rozwiązanie jest idealna, o ile oczywiście dopiero zaczynasz tworzyć swoją animacje, bo wtedy łatwiej jest ją odpowiednio przygotować niż później konwertować wszystkie MovieClipy na obiekty Graphics. A mając wszystko w graphics można zwyczajnie wywołać metodę gotoAndPlay by przenieść się w dowolne miejsce w animacji, co jest dokładnie tym czego potrzebujemy w pasku wyszukiwania. Drugie rozwiązanie ma sens tylko przy niewielkiej ilości MovieClip'ów, ponieważ wyszukanie i zatrzymanie dużej ich liczby może być zbyt obciążające dla procesora, szczególnie, że w przypadku paska wyszukiwania oznaczało by to przeszukanie CAŁEGO filmiku. Ale cóż, czasem po prostu nie ma innego wyjścia.
Kod odpowiedzialny za zatrzymanie wszystkich MovieClip'ów jest właściwie całkiem prosty:
stopMovies(root as MovieClip);
function stopMovies(mc:MovieClip):void {
	for(var i:int = 0; i<mc.numChildren; i++) {
		var d:DisplayObject = mc.getChildAt(i);
		if(d is MovieClip) {
			(d as MovieClip).stop();
			stopMovies(d as MovieClip);
		}
	}
}
Teraz mogłoby się wydawać, że wystarczy jeszcze dopisać gotoAndPlay i będziemy ustawieni. Niestety, nie ma tak łatwo, ponieważ MovieClip'y mają swoją własną linię czasu, w ogóle nie będą reagować na zmianę położenia głównej linii czasu (w przeciwieństwie do obiektów grafik). Z kolei zamiana wszystkich stop'ów na gotoAndPlay mija się z celem bo chęć przejścia na 10 klatkę głównej animacji nie oznacza, że wszystkie MovieClip'y również mają zostać ustawione na 10 klatce - w końcu jeśli MovieClip we Flash'u pojawi się na 6 klatce, na 10 klatce filmiku będzie dopiero w 4 klatce własnej linii czasu. Najlepiej będzie to pokazać na przykładzie gdzie główna linia czasu ma 30 klatek, a MovieClip pojawiający się na 10 klatce ma ich 20 (wszystkie ponumerowane):
Jak widać przy przewijaniu MovieClip'y zdają się żyć własnym życiem.
Dobra, ale dlaczego by nie odjąć od docelowej pozycji klatki w której pojawia się MovieClip? Jeśli chcemy przejść do 15 klatki animacji a MovieClip pojawia się na 10 to odejmujemy te 10 od 15 i wywołujemy gotoAndPlay(5) - główna linia czasu byłaby na 15 klace a MovieClip na 5-tej. Zgadza się, takie rozwiązanie byłoby idealne, ale haczyk w tym, że nie mamy skąd wziąć początkowej pozycji MovieClip'a. Tutaj właśnie pojawia się owe wyzwanie dla procesora o którym wspomniałem wcześniej, ponieważ kolejnym krokiem byłoby wyszukanie i spisanie tych wszystkich początkowych pozycji MovieClip'ów; tylko problem w tym, że przy standardowej animacji trwającej minutę i odtwarzanej przy prędkości 20 klatek na sekundę, do sprawdzenia będzie 1200 klatek! A jeśli do tego jeszcze dodamy np. 10 MovieClip'ów na klatkę... Dość powiedzieć, że jest to mało praktyczne rozwiązanie, dlatego w dalszej części artykułu zakładam, że animacje są w całości przygotowane na obiektach grafik.
Tyle teorii, czas stworzyć pasek wyszukiwania - zakładając absolutne minimum, będziemy potrzebować dwa MovieClipy: pasek reprezentujący długość animacji oraz wskaźnik aktualnej pozycji. Dla ułatwienia oba połóż na głównej linii czasu osobnego MovieClip'a (cały pasek będzie w jednym miejscu co ułatwi np. jego kopiowanie między projektami) i nazwij je mcBar oraz mcMarker (nazwy instancji). Pozostaje jeszcze tylko wstawić poniższy kod na główną linię czasu MovieClip'a przechowującego nasz pasek wyszukiwania:
import flash.events.MouseEvent;
import flash.events.Event;
import flash.display.MovieClip;

var bSeek:Boolean = false;
var nLength:Number = mcBar.width-mcMarker.width;
mcMarker.mouseEnabled = false;
mcMarker.mouseChildren = false;

if( !mcBar.hasEventListener(MouseEvent.MOUSE_DOWN) ) {
	//by nie dodawac zbednie event'ow gdy animacja zrobi petle
	mcBar.addEventListener(MouseEvent.MOUSE_DOWN,onSeekStart);
	mcBar.stage.addEventListener(MouseEvent.MOUSE_MOVE,onSeekDrag);
	mcBar.stage.addEventListener(MouseEvent.MOUSE_UP,onSeekStop);
	mcBar.stage.addEventListener(Event.ENTER_FRAME,onRender);
}

function onRender(e:Event):void {
	if(bSeek) return;
	mcMarker.x = ((root as MovieClip).currentFrame/(root as MovieClip).totalFrames)*nLength;
}
function onSeekStart(e:MouseEvent):void {
	bSeek = true;
	onSeekDrag(e);
}
function onSeekDrag(e:MouseEvent):void {
	if(!bSeek) return;
	
	mcMarker.x = mcBar.mouseX;
	if(mcMarker.x > nLength) {
		mcMarker.x = nLength;
	} else if(mcMarker.x<0) {
		mcMarker.x = 0;
	}
	
	var frame:int = int((mcMarker.x/nLength)*(root as MovieClip).totalFrames);
	
	(root as MovieClip).gotoAndStop(frame);
	mcMarker.x = (frame/(root as MovieClip).totalFrames)*nLength;
}
function onSeekStop(e:MouseEvent):void {
	bSeek = false;
	(root as MovieClip).play();
}
Nie jest to szczególnie skomplikowane - kliknięcie myszka na pasek przenosi w to miejsce znacznik i tłumaczy położenie X na pozycje w animacji.
Całość można pobrać stąd: seekbar.zip
A dla tych wszystkich którzy chcą spróbować rozwiązania z przeszukiwaniem MovieClip'ów przygotowałem specjalną klasę oraz przykład jak ją wykorzystać: seekbarEX.zip. W tym wypadku, żeby nie wpłynąć na filmik, MovieClip w którym umieściliśmy pasek i znacznik będzie eksportowany do ActionScript'u klasą ForusSeekBar.

Imię:
Komentarz:
Potwierdz kod z obrazka:confirm image

 
nazar pisze:
18.01.2012 08:22
plz upload the sample file seebarEX.zip for flash cs5 version.

4as pisze:
22.01.2012 23:03
Sure, you can now re-download seekbarEX.zip file and it will have CS4 version (so more people can open it).

jason pisze:
08.05.2013 19:46
i just tried this and it works except the marker is not synced with the bar properly, the marker starts at half way across the screen and runs off the screen. i am using cs5 action script 3.0, does it matter as 3.0 or 2.0? i'm on a macbookpro 10.6.8

jason pisze:
09.05.2013 05:10
I'm having no luck. Can you please explain in better detail, clearly, exactly where everything goes.

jason pisze:
09.05.2013 18:17
ok, got it to work again, sort of. my marker starts at the far left of the screen--it is not synced with my bar-- is only grabbed way to the right of the marker, it can't be grabbed exactly on the marker

Naresh pisze:
04.07.2013 09:22
Plz upload the sample file seekbar.zip for cs3 version

Kapode pisze:
28.03.2014 03:27
Thank you so mush

Jamal pisze:
27.12.2016 11:17
On seekbarEX sometimes I got an error "angeError: Error #2006: The supplied index is out of bounds." Plz help. thnks in aadvance

Surya pisze:
25.02.2017 14:00
plz upload the sample file seebar.zip for flash cs6 version