How to Build a Basic Slider Widget (AS2)
In reply to a request from kweku, here’s another look at the basic slider used in the two-part “How to Build a Flash Video (FLV) Progress Bar” series. This time, the slider widget, comprised again of two movie clips, will be used to adjust the volume of an FLV video file. The ActionScript 2.0 involved is fairly straightforward and, in fact, can be cobbled together from existing articles on this blog. Let’s break it down.
Building the slider itself
Refer to part 1 of the other how-to for slightly more specific detail, but in a super-quick nutshell, you’ll want two movie clips to associate with the following code: a horizontal, thin rectangle with the instance name track (registration point in the upper left) and a small circle or almond shape with the instance name knob (registration point in the center). Arrange those on the Stage in separate layers with the knob above the track, and positioned at track’s left edge. Type the following into your scripts layer:
knob.onPress = function():Void {
var vertical:Number = track._y + (track._height / 2);
this.startDrag(
true,
track._x,
vertical,
track._x + track._width,
vertical
);
this.onMouseMove = updateKnobOutput;
}
knob.onRelease = function():Void {
this.stopDrag();
delete this.onMouseMove;
}
knob.onReleaseOutside = knob.onRelease;
The mechanics are explained in the earlier article — but again, to recap very quickly, we’re assigning two event handlers to the knob movie clip. When the user presses, a dragging session is begun by way of the MovieClip.startDrag() method, as invoked on knob. Because the dragging is supposed to be constrained, five optional parameters are provided in the method. Just prior to that, an arbitrary variable, vertical, is declared and set to the location of the track movie clip plus half its own height. Why? Because the expression that refers to that location is used twice in the startDrag() method, and the variable saves a bit of typing now and calculation later.
One interesting aspect, at the end of this first event handler, is that the onPress function actually defines a new event handler, MovieClip.onMouseMove. The custom function, updateKnobOutput (yet to be written), is associated with knob by way of the global this property, which here refers to knob itself, because it appears within the function assigned to one of knob’s event handlers. (Event handling syntax differs quite a bit in AS3, but the basic idea is essentially the same.)
A function assigned to the MovieClip.onRelease event of the knob object means that the dragging stops when the user lets go — and that onMouseMove event handler is also stopped. The onReleaseOutside event handler is a duplicate and ensures that the onRelease function occurs even if the user releases the knob while no longer over it with the mouse.
Hold up, a sec!
If this is moving too fast, don’t lose heart.
I normally go over practically every line of code, but in this particular blog entry, I’d like to show a concept, more than anything else. Creative problem solving is always the true puzzle to wrestle. In this case, the concept is that of referencing one movie clip’s position in relation to another, then applying a touch of math to shoehorn that relationship into whatever use you need. As with the progress bar in the other article, this example also uses an arbitrarily named variable, ratio, to accomplish the key ingredient. Put the following line above the ActionScript already shown:
var ratio:Number = track._width / 100;
The ratio variable now records the quotient (division result) of the width of the track movie clip and the number 100. Why 100? Well, in ActionScript 2.0, audio volume runs from zero to one hundred. Hold that thought. Add the following code at the bottom of the existing ActionScript:
function updateKnobOutput():Void {
trace((knob._x - track._x) / ratio);
}
That’s the previously alluded to updateKnobOutput() function. All it does, so far, is take the current position of knob and subtract the position of track, which effectively zeroes out knob._x when knob is on the left edge of track. The difference of this subtraction is divided against ratio. The result? When knob is on the left side of track, the full expression amounts to zero. When knob is on the right side of track, the expression amounts to approximately 100. Currently, this result is traced to the Output panel, so you can test even without a video or audio file.
Putting it to work
At this point … well, it’s up to you. I’m betting kweku will use the slider to adjust the volume of an FLV file. This can be used, in its current configuration, to set the value of any object’s property that needs to fall between the range discussed. If you play with the math a bit, you can use ratio to convert the result to a range between -100 and 100, which would allow you to use the slider to pan audio.
For panning, you might, for example, change the opening line from what it is to this:
var ratio:Number = track._width / 200;
… and change the function from what it is to this:
function updateKnobOutput():Void {
trace(((knob._x - track._x) / ratio) - 100);
}
Sky’s the limit. Going back to the original implementation, let’s use the math forumula to adjust the volume of an FLV file. I’m going to breeze through this one, too, by the way, because a fuller account is spelled out in “How to Adjust the Audio Portion of Flash Video.” The new code below expects a Video object on the Stage with the instance name videoPlayer.
var ratio:Number = track._width / 100;
knob.onPress = function():Void {
var vertical:Number = track._y + (track._height / 2);
this.startDrag(
true,
track._x,
vertical,
track._x + track._width,
vertical
);
this.onMouseMove = updateKnobOutput;
}
knob.onRelease = function():Void {
this.stopDrag();
delete this.onMouseMove;
}
knob.onReleaseOutside = knob.onRelease;
var nc:NetConnection = new NetConnection();
nc.connect(null);
var ns:NetStream = new NetStream(nc);
videoPlayer.attachVideo(ns);
ns.play("externalVideo.flv");
this.createEmptyMovieClip("videoAudioContainer", this.getNextHighestDepth());
videoAudioContainer.attachAudio(ns);
var videoVolume:Sound = new Sound(videoAudioContainer);
function updateKnobOutput():Void {
videoVolume.setVolume((knob._x - track._x) / ratio);
}
In the “How to Adjust Audio” article, the newest lines in the above sample appear almost unchanged from how you see them here. The slider widget comes into play in that the expression (knob._x - track._x) / ratio has now been passed to Sound.setVolume(), as invoked on the videoVolume instance, as a parameter.
kewku, if you want to use one slider for volume and another as your video’s progress bar, you’ll have to give each pair of track/knob movie clips their own instance names and update the ActionScript to match. You’ll also have to create unique ratio variables for each implementation, otherwise they’ll interfere with each other. You might, for example, name the volume ratio ratioVolume and the other one ratioProgress.
July 25th, 2007 at 4:43 am
David,
thanks a lot for this great tutorial.
July 25th, 2007 at 6:35 am
hi David,
i forgot to ask you this. is it possible to add mute/unmute buttons along side this volume slider to control the video’s sound?
many thanks.
July 25th, 2007 at 7:57 am
kweku,
Sure thing! I know you’re interested in learning how to make a toggle button, and I have that on my to-do list for the blog as well, but for the time being, consider two buttons. Each button would have to do two things: a) set the volume appropriately and b) move the
knobmovie clip. Using the suggested instance names above, you might try …Ah, but actually, that’s not quite good enough, yet. The user may have dragged the slider somewhere other than 100 before using the buttons. If the user had dragged the slider to 50%, for example, the above unMute button would raise the volume to 100% again. To remedy that, you might create a new variable altogether, volumeLevel, and have the
updateKnobOutput()function update that in addition to what it currently does. That way, your new unMute code could be updated like this:July 26th, 2007 at 5:12 am
THANKS a lot David, you’ve always been my savior. Wishing you the best of luck as a Jugde in the upcoming Flash Forward Festival.
July 26th, 2007 at 8:12 am
kweku,
Hey, thanks! If you’re at Flashforward this year, make sure to say hi.
July 30th, 2007 at 12:19 am
Youre the man! Thanks!
July 31st, 2007 at 8:49 pm
Oscar,
Thanks!
August 1st, 2007 at 3:50 am
wow, wish i could be there but i live in Ghana and will be kind of hard for me to come over. thanks for everything David. All the best.
August 2nd, 2007 at 8:29 am
Hi David,
Please help me with this. In Flash, is there a way of embedding titles and graphics in FLV, while stilling using NetStream? I believe you understand what I’m trying to explain.
Or if you could help me with an apprioprate software that can import FLV and work on them. I understand After Effects CS3 can but is too expensive.
Thanks.
August 5th, 2007 at 8:43 pm
kweku,
Wow, Ghana is certainly far from Virginia Beach (or anywhere else in this country)! The closest I’ve been to your country was Tunisia, just over a year ago. I loved it. Who knows, maybe we’ll meet some day after all.
Flash CS3 makes what you’re describing very easy (titles, subtitles, closed captions, and the like), but it’s certainly possible in previous versions of Flash. Your best bet is to use cue points — with either the FLVPlayback Component or the Video object/
NetStreamapproach — and include the desired text as a parameter of each cue point (empty strings to indicate the end of a span of text), then populate theTextField.textproperty of a dynamic text field in response to your cue point handler.My next blog entry will be the pause/play button you’ve been asking for, and I’ll add a titles tutorial to the list. Let me know if I’m misunderstood what you’re asking for.
August 6th, 2007 at 7:15 am
Hi David,
If this approach will help embed the titles in my FLV files, then that’s exactly what I’m refering to. Anyway I’ll give it a try and see what happens. I’ll also wait for the upcomming blogs.
Thanks for everything.
August 6th, 2007 at 7:26 am
kweku,
Aha. Well, if you want to actually embed titles (or any other visuals) directly into your FLV files, you’ll have to do that with a video editor during the original processing of your video. That would mean Premiere, After Affects, Final Cut Pro, etc. That said, because of its layered nature, and because videos are manipulable in Flash like movie clips, Flash can superimpose graphics and text over FLVs in a synchronized manner, thanks to cue points.
August 7th, 2007 at 7:13 am
ok David,
I’ll be working on that and many thanks. best regards.
August 27th, 2007 at 3:22 am
Hi David
Just like to say that your blog has really helped me out, many thanks.
Kevin
August 27th, 2007 at 6:41 am
Kevin,
Glad to hear it.
September 17th, 2007 at 12:09 pm
hi David,
it’s been a while, I’ve been a bit busy with my flash studies. I just want to know if you could make the volume slider behave like YouTubes’s. When someone mutes and unmutes the volume, the knob moves to where it was before it was muted.
But in our case it kind of moves to a fixed point. Maybe you could compare this to youtube and you’ll understand what I’m trying to get at. If only your coding support that, then please help me out.
Many Thanks.
September 26th, 2007 at 12:43 pm
kewku,
To achieve what you’re aiming for, you’ll have to create another couple variables: one to store the fact that volume either is or isn’t currently muted and another to store the last position of the knob. In addition to the existing code above, add something like the following:
The
isMutedvariable is a Boolean (true/false) and starts out asfalse— after all, volume isn’t muted yet. TheprevVolumevariable is initialized to zero. Assuming the instance name of your mute button ismute, you’ll see that itsButton.onReleaseevent is handled to perform the following routine: a) check if volume is currently muted; b) if so, setisMutedback tofalseand setknob’s horizontal position back to what it was before, which is stored in theprevVolumevariable; c) otherwise, setisMutedtotrue, set the value ofprevVolumeto whateverknob’s horizontal position is at that moment, and setknob’s current position to the left edge (or wherever) oftrack.In addition to the above snippet you will, of course, have to add the appropriate
Soundmethods to adjust the volume.October 1st, 2007 at 8:35 am
ok david thanks a lot for your time and everything. i’ll give a try.
many thanks!!
November 28th, 2007 at 3:04 pm
Hey David,
I am trying to build a basic volume slider just like the one you’re talking about here in your blog, but it is a vertical slider. With all the _x and _y and startDrag stuff you have in here, it’s not as easy as it looks to convert this to a vertical slider bar.
Would you be willing to help me a little with making the actionscript work with a vertical bar?
November 29th, 2007 at 2:13 pm
Chad,
The constraint parameters of the
MovieClip.startDrag()method are: left, top, right, and bottom, so for a vertical slider, the dragging portion might look like this:And to suit, the
updateKnobOutput()might look like this:Notice that the
ratiovariable is not based on height rather than width, and the sampletrace()statement notes vertical position rather than horizontal. As shown, that slider would be 100% when the knob is at the top and 0% when the knob is at the bottom. If you wanted to reverse that, you could remove the expression100 -immediately inside thetrace()statement.December 7th, 2007 at 8:49 am
Awesome, thanks for the response!
December 11th, 2007 at 2:37 pm
Hello David! I LOVE your blog, thanks for all the great tips you’ve supplied. I have a volume question
I have a SWF that plays a component-based FLV. I also have a SWF with a volume slider based on your sample provided. What I need to do is keep the SWFs separate files, and have the volume slider SWF control the volume of the FLV SWF. Any thoughts? I just can’t get it working.
thanks! Chris
December 11th, 2007 at 3:19 pm
Chris,
Thanks for the kind words!
To control audio in AS2, you’ll need to associate your
Soundinstance with a particularMovieClipinstance (there isn’t a formalSoundChannelclass until ActionScript 3.0, so movie clips are your channels until then). If you use the main timeline as yourMovieClipinstance, you can control the volume for the whole SWF — but it sounds like you want to control the video separately. If you’re using the FLVPlayback component, you can even use the component as your movie clip, because FLVPlayback inherits fromMovieClip.I see you’ve already checked out “How to Adjust the Audio Portion of Flash Video,” but that’s not the way to do it for FLVPlayback. For your component use the ideas covered in “Understanding the Sound Constructor” and use the FLVPlayback instance as your movie clip parameter in the
Soundconstructor.See if that gets you started, and come back if you need additional help.
December 12th, 2007 at 9:52 am
Hi Dave,
Sorry I’m stuck…I am a graphic designer by trade and so far haven’t been able to wrap my head around the concept yet. The way my file is built, I have a video component playing my flv files, but it is called from a variable set in my HTML.
In your code example, it is looking for a specific flv name in the NetConnection command. I guess I am wondering also what the code would be for establishing my sound object on frame 1, and for my volume slider on frame 20. I just can’t seem to make that connection!
December 12th, 2007 at 10:37 am
Hi again Dave-
I was able to make the connection between the slider and the FLV component’s audio. My final (hopefully!) task is now to make the slider function with selectable volume “zones” of 0%, 25%, 50%, 75% and 100%. I’m sure that won’t be covered here but thanks for helping me get this far!
chris
December 12th, 2007 at 12:25 pm
Chris,
Hey, good for you!
That’s a good question, Chris. That posed a fun challenge to solve, actually.
In order to make your slider snap to increments, you’ll need to do away with the automatic
startDrag()behavior. Instead, you can handle themouseMoveevent to update the_x(or_yfor vertical) property of the knob. Here’s a revision of the above code — without snapping yet — that shows how to “drag” without usingMovieClip.startDrag().This makes use of a custom function
drag(). Pressing on knob with the mouse activates this function as the mouse moves; releasing unassigns the function. Theifstatement in thedrag()function checks if the mouse is within bounds, based on theMovieClip._xmouseproperty for the track. If the mouse is over the left edge of the track,track’s_xmousevalue is 0; if the mouse is over the right edge, the_xmousevalue is whatevertrack’s width is.So … if that’s true,
knob’s_xproperty is set to whatevertrack’s_xmousevalue is. With me so far? Now, in order to snap to increments, I chose to resolve the issue with another custom function,snapTo(), which accepts three parameters: the current value of_x, the total span of the draggable area, and the percentage desired. I’m going to just paste the code for now, but this would probably make a good blog article on its own.The third parameter, 25, means your slider will snap to five places: 0, 25, 50, 75, and 100 percent of the width of your track. A value of 33 would snap to four places: 0, 33, 66, and 99 (actually, 100) percent of the width. Any value higher than 50 will turn the slider into a switch: 0 or 100 percent of the width.
December 12th, 2007 at 1:58 pm
Wow great job! Thanks again for the explanations, it really helps (vs. just posting a block of code).
December 12th, 2007 at 2:02 pm
Chris,
Sure thing!
When I was starting, I had a hard time with the block-of-code answers, too. Mostly, I just try to answer questions the way I would have wanted them answered myself.
December 14th, 2007 at 1:58 am
Hi there Dave,
Awesome site!
I am a total beginner in Flash and have just about completed my first project…My problem is that I just can’t seem to figure out how to make play and stop buttons control a movie clip symbol.
What is the best way to bring in wmv files and be able to control them (with like play and stop buttons,etc.)? [AS2/CS3]
What action script should be applied to the play/stop buttons and the video itself (I currently have my imported/embedded video created as a movie clip symbol, and for the moment have it playing when the scene starts).
Thanks for your time!
December 14th, 2007 at 9:30 am
Melissa,
For videos of any substantial length (for example, longer than a few seconds), I definitely recommend that you use either the FLVPlayback component or the Video object/
NetConnection/NetStreamapproach suggested elsewhere on this blog.That said, there’s certainly nothing wrong with importing WMVs and putting them in movie clip timelines. It’s just that you may run into timing issues between the framerate of the video and the framerate of your FLA — and videos displayed in this way fall completely under the auspices of the
MovieClipclass. In a sense, that makes things easier, because movie clip timelines are less complicated than theNetStreamclass.The movie clip that holds your video will need an instance name. Select the movie clip on the Stage and look to the Property inspector to do that. This allows that movie clip to be addressed by name and told what to do. The available instructions are listed in the
MovieClipclass entry of the ActionScript 2.0 Language Reference. TheMovieClip.play()method, for example, tells a movie clip to play.myVideoHolderClip.play();You’ll see other methods that allow you to stop and jump around. The trick is, classes define objects, so class entries are a kind of Owner’s Manual for the object at hand (in this case, movie clips). Characteristics of the object are called properties, things it can do are called methods, and things it can react to are called events.
Give you buttons instance names as well, and they can also be programmed. Your code will go in a keyframe, and you’ll look to the
Buttonclass to see what events are available for button symbols.December 14th, 2007 at 1:14 pm
Thank you, David, for the quick response!
Great information.
I had attempted something like this before to no avail, thanks for clarifying I was on the right track; I think I may just be adding the buttons in the wrong area.
My movie clip instsance name is everydayMovie, and my stop and play button instances are named playEveryday and stopEveryday.
Where do I drag the buttons in from the library? Do I double click the movie clip and drag them in during its edit mode? Or do I need to add them to the main stage of my scene?
What action script needs to be within the movie clip?
Does the action script for the buttons need to be in the first and last keyframes of that layer so that they are available throughout the movie playing?
Can a seek bar work the same way?
Sorry for the barrage, I’ve just been working on this for 6 days straight and feel like I am so close.
Thank you a million times over for your time and your expert advice!
:)
December 14th, 2007 at 10:41 pm
Melissa,
Flash is very flexible about this. The key is to give each button an instance name (again, see the Property inspector while you have a button selected), then use that instance name in a valid object reference in order to tell it what to do. Think of object references like folders on your hard drive. If you were in the root of your computer (usually
C:\on Windows machines), how would you have to travel in order to get to a particular file?If your code is in a keyframe of the main timeline and your buttons are also in the main timeline, the object reference would simply be the instance name of a given button. If those buttons were inside a movie clip symbol, that movie clip would also have to be given an instance name — and the your object reference would be the movie clip’s instance name, then a dot, then the button’s instance name. Make sense?
Wherever you decide to put them, just make sure you use the correct object reference from the point of view of the ActionScript that speaks to them. I personally tend to put 99% of my ActionScript in a layer of its own (named “scripts”) in the main timeline.
None. You could put your code in the movie clip, but that would the “point of view” of that code and might affect your object references.
Think of your timeline like a grid with rows (horizontal) and columns (vertical). Keyframe ActionScript needs to be in the same column as the object(s) it’s trying to talk to. If your buttons span from frame 5 to frame 50, you would put your button event handlers into a keyframe of your scripts layer on frame 5 (the first frame that “meets” the buttons on a vertical column).
The slider widget described in the blog entry works by the same principles of code “point of view” (that is, object references and where each object is in relation to other objects it needs).
Does that help?
December 17th, 2007 at 7:15 pm
David,
Thanks again….we are so fortunate to have access to brilliant people miles away….
Thank you for all of this detailed information, it all certainly helps!
Unfortunately I did not get my buttons to work yet.
I used the script you posted for me above for each button (substituting of course the correct instance names for the button and the clip), but I keep getting a compiler error msg about ‘expecting the on event handler’.
I think I will try creating a brand new project and brand new buttons and going from there with what you have given me.
It may just come down to something I am overlooking in my current project. I will come back crying to you if I can’t get it to work in a brand new one!
December 18th, 2007 at 2:48 pm
Melissa,
If you’re seeing something about the
onevent handler, my guess is that you’ve placed your code on the buttons themselves, rather than in a timeline keyframe. The Actions panel updates its focus depending on what you’ve selected when you start typing. In pre-AS3 documents, you can select objects (buttons, movie clips, components) and add code directly to the object — in which case you don’t need instance names because Flash already knows which object(s) you mean.Because you’re using instance names (the more modern, and Adobe-recommended, approach), you’ll need to remove that code from the buttons. See if my museum pieces article helps you out. Chin up!
January 11th, 2008 at 1:05 pm
Hi again Dave-
I have successfully implemented the volume slider on my page with your suggestions, thanks again! Works great on a single HTML page.
I’ve encountered a situation in my application where my SWF needs to be loaded into a sequence of HTML pages, and the volume slider value is getting reset to 100% with each new load. I currently have a single SWF that has an FLV component (plus the slider code), the FLV file name itself is set via a variable so the name of the file is called inside the HTML page.
I need to find a way to have the slider value carry from page to page as the SWF is loaded and show the slider in that “variable” position.
I’ve looked into using a shared object but I am unable to have it affect the volume slider. It keeps setting the volume to 100% when it’s loaded. Any suggestions, or is the shared object not the way to go?
Thanks!! Chris
I will need to carry over the current volume setting and slider position
January 14th, 2008 at 9:10 pm
Chris,
The
Have you made a few small tests first, just to make sure you’ve got the hang of how it works? You can use
SharedObjectclass is exactly the solution I was going to suggest!SharedObjectto carry over whatever data you want, but because you’re also dealing with FLVPlayback and other ActionScript, there might simply be too much going on for you to see clearly what you’re dealing with. (I do this all the time! Then I have to remind myself to slow down, isolate the new idea in a completely new FLA [or set of FLAs] and take small steps until I understand exactly what the new code does.)You might very well be carrying over the volume, but not retrieving it correctly from the next page — or maybe you’re not carrying it over, and the 100% winds up occurring because it’s default.
For every page that has this slider, you’re going to want to: a) use
ShareObject.getLocal()to check for the existence of your “Flash cookie”; b) if a cookie exists, use the volume setting from that or an arbitrary default; and c) in addition, use the samegetLocal()method whenever the slider sets the volume, then follow that withflush(), to make sure the cookie does get created and set immediately.February 18th, 2008 at 6:10 am
Hi David,
I am beginner in flash, i am working on one flash video player. In that i want to run 10 flv on one same seek bar continually like one FLV. Is there any solution?
can i join the 10 flv to produce one flv as a result, if yes please help me
February 22nd, 2008 at 9:35 pm
shrikant,
Someone else recently asked me the same question, and it’s a good one. I told the other reader I would add this question to my to-do list (and I have!), but I don’t know when I’ll get the chance to write about that particular one. I’m in the middle of a new book for O’Reilly (as of Feb 2008) and have been falling behind on everything else!