How to Drag Clock Hands in a Circle

ActionScript 2.0

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.

26 Responses to “How to Drag Clock Hands in a Circle”

  1. Salih Says:

    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.

  2. David Stiller Says:

    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 the distance variable. When you measure mcB’s position from mcA’s 0,0 point, calculate a new distance variable just for mcB — call it distanceB, if you like. As long as distanceB is less than or equal to distance, you’re good (making sure you accommodate mcB’s own width and height).

  3. Matt Says:

    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

  4. Matt Says:

    It was a dumb question….

    I have done it already just needed to think it through

    Great blog thanks

  5. David Stiller Says:

    Matt,

    Awesome! Glad you got it. :)

  6. Ben Faubion Says:

    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

  7. David Stiller Says:

    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 Tween class 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?

  8. jb Says:

    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)

  9. Dante Says:

    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.

  10. David Stiller Says:

    Dante,

    Sure thing! :)

  11. olowekk Says:

    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

  12. David Stiller Says:

    olowekk,

    Not sure what “last bit” you mean. What’s your error message, if you’re getting one?

  13. olowekk Says:

    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

  14. olowekk Says:

    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;
    };

  15. olowekk Says:

    anyone please?

  16. David Stiller Says:

    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?

  17. olowekk Says:

    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;

  18. paczor Says:

    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!

  19. David Stiller Says:

    paczor,

    Cool tweak! Good stuff. :)

  20. Steve Says:

    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.

  21. David Stiller Says:

    Steve,

    I’m thinking there must be something off with the registration points of your mcMinute and mcFace clips. 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?

  22. Ingmar Zahorsky Says:

    David,

    thank you! Your blog is awesome. I have been looking for exactly this explanation to rotate two disks.

    Best,
    Ingmar

  23. David Stiller Says:

    Ingmar,

    Hey, glad to hear it. Thanks!

  24. Cathrine Says:

    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?

  25. David Stiller Says:

    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.

  26. Cathrine Says:

    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… :-)

Leave a Reply