How to Drag Clock Hands in a Circle
This one’s on request.
Here’s a variation on How to Constrain Dragging to a Circle that’s different enough it merits its own entry. Interestingly, this question crops up often enough in the forums. Usually, someone is putting together a children’s game in which the child is expected to drag clock hands to the correct time. As it turns out, this requires even less code than constraining dragging to a circle. Let’s take a look.
An answer, short and sweet
The following code assumes two movie clips: the clock face (instance name mcFace) and a minute hand (mcMinute). The registration point — this is important! — the registration of the minute hand should be centered on one of its ends. By this, I mean the minute hand should spin like a real minute hand when you rotate it manually in the authoring environment. The registration point of the clock face should be its upper left corner.
With those two movie clips in place on the Stage (and respective instance names), type the following ActionScript into a frame of your scripts layer.
mcMinute.onPress = function() {
this.onMouseMove = function() {
var angle:Number = Math.atan2(
mcFace._ymouse - mcFace._height / 2,
mcFace._xmouse - mcFace._width / 2
);
this._rotation = (angle * 180 / Math.PI) + 90;
};
};
mcMinute.onRelease = function() {
delete this.onMouseMove;
};
mcMinute.onReleaseOutside = mcMinute.onRelease;
How it works
A function literal is assigned to the MovieClip.onPress event of a movie clip with the instance name mcMinute. When this clip is pressed, therefore, the following occurs: immediately, another function literal is assigned to the same clip’s MovieClip.onMouseMove event. In this second function, a local variable, angle, is declared and set to the return value of the Math.atan2() method. Math.atan2() provides the angle in radians between two coordinates. In this case, the coordinates are derived from two math expressions.
In the first case …
mcFace._ymouse - mcFace._height / 2
… the mouse’s vertical location is taken from the point of view of mcFace. This represents where the mouse is in relation to mcFace, rather than, say, the Stage itself. Then, half the height of mcFace is subtracted from this value. Why? Because this subtraction provides the distance halfway down the clock’s face. In other words, we’re actually taking the location of the mouse in relation to the clock’s center.
Same goes for the mouse’s horizontal position.
Now that we have this angle in radians, we need to convert it to degrees and use the converted value to set the MovieClip._rotation property of mcMinute. Here are the necessary formulae:
Radians to Degrees
degree = radian * 180 / pi
Degrees to Radians
radian = degree pi / 180
In Flash, 0 degrees starts at North, which is a bit different from how these classic formulae work. With those, 0 degrees starts at East, so we add 90 degrees to the radian-to-degree conversion.
And that’s it. The explanation is longer than the actual code. At the very end, we use a MovieClip.onRelease event handler to cancel the dragging, and a MovieClip.onReleaseOutside handler to do the same, as it’s possible the mouse may not be directly over the minute hand when it is released.
November 24th, 2006 at 5:48 pm
This is a good tutorial. But I want to make something a bit different.
I have a filled circle 150 width x 150 height name is mcA. Starting from _x = 0, _y = 0.
I have another movie. Its name mcB, width = 25, height = 25.
I want to attach mcB to mcA random places but mcB must inside the circle.
How am I suppose to do ?
Thanks in advance.
November 27th, 2006 at 2:47 am
Salih,
I would steer more toward the “How to Constrain Dragging to a Circle” tutorial and be sure to keep
mcB’s position within the boundary determined by thedistancevariable. When you measuremcB’s position frommcA’s 0,0 point, calculate a new distance variable just formcB— call itdistanceB, if you like. As long asdistanceBis less than or equal todistance, you’re good (making sure you accommodatemcB’s own width and height).December 18th, 2006 at 1:13 pm
Brilliant, so clear even I can do it.
Now, I would like to generate a number from the angle to make a volume control or a rotary scale thingy
Sorry, if this is a dumb question on your blog.
thanks
December 18th, 2006 at 1:48 pm
It was a dumb question….
I have done it already just needed to think it through
Great blog thanks
December 18th, 2006 at 5:32 pm
Matt,
Awesome! Glad you got it.
December 27th, 2006 at 1:19 am
how would you make the hand do an elastic effect after you let go?.. like it continues to move once you let go and then tweens to a stop
December 28th, 2006 at 10:09 pm
Ben,
That’s a good question, and probably worth a blog entry (in fact, I’ve added it to my list). Off the top of my head, I’d say the
Tweenclass would come in handy here. The thing is, as currently presented, the dragging doesn’t lag behind the mouse — it already is where it’s supposed to end up, whenever you let go — so how do you envision the elastic part happening?January 24th, 2007 at 10:34 pm
cool blog. as for the elasticity, do that in actionscript as well, but set the angle as the target, and have the movie move to it…
change the this._rotation= to this…
targetrotation= (angle * 180 / Math.PI) + 90;
then add this code…
var k=0.9;
var damp=0.2;
this.onEnterFrame=function(){
if((Math.abs(this._rotation-targetrotation)
March 7th, 2007 at 11:52 am
Thanks a ton for this. The hardest part of solving a problem like this is knowing where to start, and I guarantee you that I wouldn’t've thought of figuring out radians in a million years.
March 7th, 2007 at 3:52 pm
Dante,
Sure thing!
May 1st, 2007 at 12:07 pm
could you possibly put the last bit (change the this._rotation= to this…
) of code together cause i can’t seem to get it compiled, cheers
May 1st, 2007 at 12:39 pm
olowekk,
Not sure what “last bit” you mean. What’s your error message, if you’re getting one?
May 1st, 2007 at 5:23 pm
hello, i’m after the previous post concerning ‘making the clock-hand do an elastic effect after you let go’ - I’ve combined 1st part of code (at the very top) with Jb’s snippet and i got 5 errors, then i added some missing braces/round brackets but the clock-hand doesn’t move at
May 2nd, 2007 at 6:16 am
How do I change the code below so as to make it suitable for a clockhand while preserving this slow down/elastic effect of cl.hand following mouse pointer?
var drag:Number =20;
mcMinute.onEnterFrame = function() {
var distX:Number = this._x - this._parent._xmouse;
var distY:Number = this._y - this._parent._ymouse;
var radians:Number = Math.atan2(distY, distX);
targetRotation = ((radians / Math.PI) * 180);
rotateAmt = (targetRotation - this._rotation) / drag;
this._rotation += rotateAmt;
};
May 3rd, 2007 at 8:45 am
anyone please?
May 3rd, 2007 at 9:30 pm
olowekk,
Can you explain better what you’re after? I need some clarification.
I can see that jb’s code must have gotten cut off, somewhat — so that sample is incomplete. You haven’t stated what your five errors were earlier, and now we’ve got this latest code. It works to some extent, but I’m not sure what you’re ultimately aiming for. I’ll ask you the same question I asked Ben: how do you envision the elastic part happening?
If the user grabs the minute hand and drags it, should the minute hand already be lagging behind? When should it start to jiggle elastically?
May 4th, 2007 at 5:05 am
Certainly:), the kind of clock hand motion I want to achieve may be best observed in code I placed above (var drag:Number =20…) I want to slow down the clock hand movement while it approaches a mouse pointer. I don’t want the clock hand to follow the cursor immediately it changes its position.
(as if the clock hand was sleepy and could not catch up with the mouse pointer). When there’s no mouse movement the clock hand should keep rotating.
The word ‘elastic’ may not have suggested my point best last time I mentioned it. Here’s code I used and want to change:
mcMinute.onEnterFrame = function() {
this._rotation += 0.1;
this.onMouseMove = function() {
var angle:Number = Math.atan2(
mcFace._ymouse - mcFace._height / 2,
mcFace._xmouse - mcFace._width / 2
);
this._rotation = (angle * 180 / Math.PI) + 90;
};
};
mcMinute.onRelease = function() {
delete this.onMouseMove;
};
mcMinute.onReleaseOutside = mcMinute.onRelease;
May 6th, 2007 at 5:01 pm
Hi David!
Thanks for the code. I used it in my project in which clock’s hand follows mouse. I would like to add little note on it:
my movie is quite small (185×185px) so clock’s hand do not follow mouse correctly - if I resize movie to allow mouse to go far away clock’s face everything is OK.
The dirty hack for it is to add some extra values to both mouse’s cooordinates:
var angle:Number = Math.atan2(
(mcFace._ymouse + 100) - mcFace._height / 2,
(mcFace._xmouse + 100) - mcFace._width / 2
);
It works because function “thinks” that mouse is 100px further from clock.
Thanks again, you are doing great job!
May 8th, 2007 at 9:29 pm
paczor,
Cool tweak! Good stuff.
August 2nd, 2007 at 9:24 am
David:
Great blog! First time visiting and I was looking for something exactly like this. However, I have a question.
1. The direction of the hands seem to be quite “loose”. How would I go tightening up the direction of the hands so if I move to 12:00 on the clock, my hands point to there? I am designing this for children, so the movements have to be a little tighter. The way it is currently written, the movement would be quite frustrating.
Thanks.
August 2nd, 2007 at 9:36 am
Steve,
I’m thinking there must be something off with the registration points of your
mcMinuteandmcFaceclips. I rebuilt this just now and the hand rotates to exactly where I drag it.Can you verify that each clip is on its own layer and the face clip’s registration is in its upper left, while the hand clip’s registration is centered on its pivot end?
August 20th, 2007 at 6:42 pm
David,
thank you! Your blog is awesome. I have been looking for exactly this explanation to rotate two disks.
Best,
Ingmar
August 21st, 2007 at 1:32 pm
Ingmar,
Hey, glad to hear it. Thanks!
January 9th, 2008 at 11:00 am
David:
First of all thanks!!
Me too i am making a clock for children, but haven’t found out how I can make an “mcHour” to properly follow the mcMinute. Actually I feel this is really tricky, because Flash doesn’t use 360 degrees on the circle, but starts counting from -179,999… at the bottom of the circle.. Any tips?
January 14th, 2008 at 4:48 pm
Cathrine,
Your question is a good one!
It sounds like you’re asking how to make the Hour hand follow the Minute hand as the Minute hand gets dragged around the face — just like a real clock — is that right? If so, the tricky part, to my thinking, is how to keep track of the Hour hand and have it “remember” its place. Drag the Minute hand around once, the Hour hand moves from 1 to 2. Drag the Minute hand around again, the Hour hand moves from 2 to 3, and so on.
I’ll really have to think about this one! I’d added your question to my Get-To list, but there are quite a few in there right now, so I’m not sure when I’ll be able to answer this one specifically.
January 16th, 2008 at 3:57 am
Thank you for answering David! You understood me perfectly - and I see that this one is complicated - having in mind that the code has to “know” how many times the Minute has passed “12 o’clock”. I am happy to be on your Get-To list, and thank you for very good explanations in this forum! I will follow this blog and check to see if you ever get the time to look in to it, but In the meantime I will look into some trigonometry-lessons to try to get better in understanding angles, degrees and so on…