Articles Archive
Articles Search
Director Wiki

Curved motion scroller

January 3, 1999
by Pat McClellan

Dear Multimedia Handyman,

I am trying to create a text scroller that has a curved path. All the examples I have seen use a staight scroller... I dont know if this is possible but it would be very nice to have.


Dear Christian,

I love a nice challenge like this! We'll be combining two distinct concepts here. The first is to create basic scroll functionality. The second task is to create a sprite that can be dragged only along the arc of a circle. This is the example graphic you sent:

Imagine the little red scroller ball moving up and down along the curved side of the text window. Now, picture a large circle superimposed over that motion. I've illustrated it with a blue line below.

Once we've pictured the circle, then it's easy to see the radius of that circle. I've drawn it at a purple line. Now, as you move the red scroller ball, it always represents one end of that radius, so we always know exactly how far it is from the origin (center of the circle.)

Now, picture a right triangle in which the radius line is the hypotenuse. We know that the locV of the scroller ball sprite will stay the same as the mouseV (the vertical location of the mouse). If we know the locV of the origin, then the vertical side of our triangle will simply be the origin minus the mouseV.

That means that we now know the lengths of 2 sides of a right triangle. What we really need to know is that third side length, because that will give us the locH of the scroller ball sprite. Luckily, Pythagoras -- a geometry pioneer and Lingo devotee -- came up with a simple formula, as follows:

a2 + b2 = c2

This formula tells us that the length of the hypotenuse is the square root of the sum of the square of each side. In our case, we know the hypotenuse length (same as the radius), so we'll swap the formula around.

a2 = c2 - b2 

Using this formula and the diagram above, think of a as the green line, b as the gold line, and c as the purple line.

Now that you have the concept under your belt, let's look at a demo and make some behaviors. Typically, in describing the components of a scroller, the part you drag up and down is called the "thumb". So, that's what we'll start calling the red scroller ball in our example.

We'll need to have certain bits of data input when the behavior is dropped on the thumb. Thinking back to our geometry diagram, those include:

The code for generating this dialog box is the getPropertyDescriptionList handler below.

-- scrollThumb behavior (curved)
-- copyright © 1999, ZZP Online, LLC
property pOriginH, pOriginV, pRadius, pMySprite, ¬
  pTop, pBottom, pFieldMem, pFieldSpriteNum,¬
  pScrollFactor,  pThumbMargin
on getPropertyDescriptionList me
  set pdlist to [:]
  addprop pdlist, #pOriginH, [#comment:"Origin ¬
    locH", #format:#integer, #default:0]
  addprop pdlist, #pOriginV, [#comment:"Origin ¬
    locV", #format:#integer, #default:0]
  addprop pdlist, #pRadius, [#comment:"Radius ¬
    (pixels)",#format:#integer, #default:0]
  addprop pdlist, #pTop, [#comment:"Top limit ¬
    locV", #format:#integer, #default:0]
  addprop pdlist, #pBottom, [#comment:"Bottom ¬
    limit locV", #format:#integer, #default:0]
  addprop pdlist, #pFieldMem, [#comment:"Field ¬
    Member Name", #format:#string, #default:""]
  addprop pdlist, #pFieldSpriteNum, [#comment:¬
    "Field spriteNum", #format:#integer, #default:10]
  return pdlist
end getPropertyDescriptionList

Based on that information, the behavior can quickly calculate some of the other data we'll need to use. Specifically, we'll need to calculate the height of the text or field member and the height of the text displayed on stage. This will tell us the scroll range for the text. Then, we need to compare that scroll range to the range of motion for the thumb (bottom limit - top limit). By dividing the scroll range by the thumb's range, we'll get a "scrollFactor". For example, if the scrollFactor is 3, then that means that the text will need to scroll 3 pixels for every pixel of thumb motion. Here's the handler that does that calculation.

on beginSprite me
  set pMySprite = the spriteNum of me
  set displayHeight = the height of sprite ¬
  set scrollRange = the height of member pFieldMem ¬
    - displayHeight
  set thumbRange = (pBottom - pTop) * 1.00
  set pScrollFactor = scrollRange/thumbRange 
  set pThumbMargin = (the height of sprite pMySprite)/2
  set the scrollTop of member pFieldMem to 1

When the user mouses down on the thumb, and for the duration of the mouse being held down, we need to repeatedly set the locV of the sprite to the mouseV (within the top and bottom limits of its range). We also need to use the pythagorean theorum to calculate the locH. That will take care of moving the thumb along the arc of our circle. Let's not forget to scroll the text. To do that, I'll call a separate handler called "scroll". This handler will simply look at how far down the scroller is from the top. Multiplying this distance by our (previously caluculated) scrollFactor will tell us what the scrollTop should be for the text.

on mouseDown me
  repeat while the stillDown
    if the mouseV > pTop and the mouseV ¬
          < pBottom then
      set b = the mouseV - pOriginV
      pythag me, b
    end if
  end repeat
on pythag me, b
  set c = pRadius -- for Pythagorean clarity
  set a = sqrt((c * c) - (b * b)) 
  set the locH of sprite pMySprite = pOriginH + a
  set the locV of sprite pMySprite = pOriginV + b
  scroll me
on scroll me
  set thumbPos = the locV of sprite pMySprite - pTop
  set the scrollTop of member pFieldMem = ¬
    thumbPos * pScrollFactor

Finally, we'll need to deal with our up and down scroller arrows. This handler is still part of our thumb behavior. It will receive a message from the arrows telling it which way to scroll and how much.

on arrowScroll me, scrollincr
  set currentV = the locV of sprite pMySprite
  set b = currentV - pOriginV
  set b = b + scrollincr
  set newV = pOriginV + b
  if (newV < pTop + pThumbMargin) and scrollincr ¬
    < 0 then
    set newV = pTop
  else if (newV > pBottom - pThumbMargin) and ¬
    scrollincr > 0 then
    set newV = pBottom
  end if
  set b = newV - pOriginV
  pythag me b
-- end of scroll thumb behavior

And here's the behavior that you'll need to put on the arrows in order to send the message to the scroll thumb. You'll need to specify the speed at which the arrows will move the thumb up and down.

-- up/down arrow scroll behavior (curved)
-- copyright © 1999, ZZP Online, LLC
property pWhichArrow, pSpeed, pMouseFlag, ¬
  pThumbSprite, pScrollincr
on getPropertyDescriptionList me
  set arrowList = [#UP, #DOWN]
  set pdlist to [:]
  addprop pdlist, #pThumbSprite, [#comment:¬
    "Thumb spriteNum:", #format:#integer, #default:0]
  addprop pdlist, #pWhichArrow, [#comment:"Which ¬
    arrow?", #format:#symbol, #default:#UP, ¬
        #range: arrowList]
  addprop pdlist, #pSpeed, [#comment:"Scroll speed",¬
    #format:#integer, #default:5, #range:[#max:10, ¬
  return pdlist
end getPropertyDescriptionList
on beginSprite me
  if pWhichArrow = #UP then
    set pScrollincr =  pSpeed * -1
    set pScrollincr = pSpeed
  end if
on mouseDown me
  set pMouseFlag = #clicked
on mouseUp me
  set pMouseFlag = #notClicked
on mouseUpOutside me
  set pMouseFlag = #notClicked
end mouseUpOutside
on exitFrame me
  if pMouseFlag = #clicked then
    sendSprite(pThumbSprite, #arrowScroll,¬
  end if
-- end arrows behavior

That's the whole thing. Note that this behavior works for motion along the right half of a circular arc. For motion along the left side, you'd need to subtract the pythagorean values from the origin -- instead of adding them as I did in this behavior. You'd also want to change things if you need motion in the 180 degree arc representing the top or bottom of the circle.

Good luck with your project.

A Director movie for this behavior is available for download in Mac or PC format.

Once you understand these concepts, you should read Darryl Plant's work on Bezier curves. Eric Woods also has some cool examples.

Patrick McClellan is Director Online's co-founder. Pat is Vice President, Managing Director for Jack Morton Worldwide, a global experiential marketing company. He is responsible for the San Francisco office, which helps major technology clients to develop marketing communications programs to reach enterprise and consumer audiences.

Copyright 1997-2017, Director Online. Article content copyright by respective authors.