Articles Archive
Articles Search
Director Wiki
 

Scrolling behaviours

January 1, 1998
by Pat McClellan

Dear Multimedia Handyman,

I want to incorporate a historical timeline with interactive buttons. How can I scroll the image horizontally across the stage.

Many Thanks,
Hayden Evans,
H.Evans / at / chester.ac.uk


Dear Hayden,

There are many approaches to creating custom scrolling controls. For example, you could create an parent script which births an object which would control all of the sprites which make up the scroll bar graphics, and then applying the user input to a list of sprites to be used. Frankly, I'm fairly new to Object Oriented Programming, so I prefer to stick with writing some custom Behaviors. They're very powerful and achieve many of the same objectives as a strictly OOP approach.

Here's what we need to do:

  1. Create (or swipe) the graphics which will make up your left & right scroll arrow buttons, the "thumb" (that's the slider that indicates where you are on the page), and the track for the thumb.
  2. Create a behavior for the arrow buttons -- preferably a single behavior which can be used for either button. This behavior needs to send out the message to the thumb and scrollable sprites that they need to move.
  3. Create a behavior for the thumb which "listens" to the arrows, and moves itself accordingly. This behavior also needs to deal with the user dragging the thumb directly.
  4. Create a behavior for the thumb track (the graphic behind the thumb). In standard user interfaces, you can click in this track to move the thumb in large increments. We want to copy this capability.
  5. Finally, create a behavior which will be applied to every sprite that will be scrolled. This behavior will "listen" to the scroll arrows, the thumb and the thumb track, moving appropriately to user input.

OK, there's a lot to do, but I've already done it and tested it... so it'll go a lot quicker for you!

You need <a href='http://www.macromedia.com/shockwave/'>ShockWave</a> 6 to view this movie!

I started with the graphics which come in the button library with Director 6. There are all the scroll bar graphics, but they're for a verticle scroll bar. I just flipped them all 90 degrees. The up and down state graphics are all there too. I laid the graphics out and applied the cursor change on rollover and push button behaviors from the Behavior Library. If you're not using the Behaviors which came with Director, you're probably wasting a lot of time.

All of these behaviors are about twice as long as they need to be for our example. I wrote them so that they can be applied to Vertical or Horizontal Scroll bars, and the button behavior can be applied to any of the buttons. You can specify which arrow you're applying it to when you drag it onto a sprite. NOTE: all of these behaviors are posted in Ren's Lingo Behavior Database, located in the Resources section of Director Online.

Some of this behavior won't make any sense until you see the other behaviors, so for now, focus on what happens in the mouseDown script. As long as the user holds the mouseDown, a repeat loop increments a global variable -- called gScrollH for our example. If the scroll bar were vertical, gScrollV would be incremented.



property pMyDirection 
-- options are UP, DOWN, LEFT & RIGHT
property pThumbSprite  
-- sprite number of the corresponding scroll thumb
property pThumbRange 
-- range of motion for the scroll thumb sprite
property pMyIncrement 
-- converts pMyDirection to an integer value
global gScrollV  
-- amount scroll has been incremented 
--Vertically from initial value
global gScrollH  
-- amount scroll has been incremented 
--Horizontally from initial value
on getPropertyDescriptionList
  set directionList = [#UP, #DOWN, #LEFT, #RIGHT]
  set p_list = [ #pMyDirection: [ #comment:   ¬
    "Direction of Button Arrow", ¬
    #format:   #symbol, #range:    directionList,¬
    #default:   #UP]]
  return p_list 
end
on beginSprite me
  case pMyDirection of     
    -- converts string direction 
    -- into integer increment
    #UP: set pMyIncrement = -1
    #DOWN: set pMyIncrement = 1
    #LEFT: set pMyIncrement = -1
    #RIGHT: set pMyIncrement = 1
  end case
end

on setThumbV me, range, thumbSprite
  if pMyDirection = #UP or pMyDirection ¬
    = #DOWN then
    set pThumbRange = range
  end if
  set pThumbSprite = thumbSprite
end
on setThumbH me, range, thumbsprite
  if pMyDirection = #LEFT or pMyDirection ¬
    = #RIGHT then
    set pThumbRange = range
  end if
  set pThumbSprite = thumbSprite
end

on mouseDown me
  case pMyDirection of
    #UP: 
      repeat while the stillDown and gScrollV > 0
        set gScrollV = gScrollV + pMyIncrement
        sendSprite(pThumbSprite, #ScrollThumbV)
        sendAllSprites(#ScrollV)
        updateStage
      end repeat 
    #DOWN: 
      repeat while the stillDown and ¬
           gScrollV < pThumbRange
        set gScrollV = gScrollV + pMyIncrement
        sendSprite(pThumbSprite, #ScrollThumbV)
        sendAllSprites(#ScrollV)
        updateStage
      end repeat
    #LEFT: 
      repeat while the stillDown and gScrollH > 0
        set gScrollH = gScrollH + pMyIncrement
        sendSprite(pThumbSprite, #ScrollThumbH)
        sendAllSprites(#ScrollH)
        updateStage
      end repeat 
    #RIGHT: 
      repeat while the stillDown and ¬
           gScrollH < pThumbRange
        set gScrollH = gScrollH + pMyIncrement
        sendSprite(pThumbSprite, #ScrollThumbH)
        sendAllSprites(#ScrollH)
        updateStage
      end repeat
  end case
end


Next, the behavior for the thumb. You'll need to calculate the range of motion for the thumb sprite. This is the number of pixels between the far left and far right positions for the thumb. Or, if your scroll bar is vertical, top & bottom. When this sprite is initialized, the beginSprite handler broadcasts to the other sprites the spriteNum of the thumb, as well as its range. This is used by the Arrow Button behavior so that the thumb is not instructed to scroll beyond its boundary. The range will also be used by the scrollable sprite behaviors.

Note the scrollThumbV me handler. This is the part of the behavior which is called by the Arrow Button behavior and is responsible for actually moving the thumb to respond to the arrows.

At the end of this behavior, look through the mouseDown handler. This portion of the script allow users to drag the thumb, within the limits of the thumb's range of motion. This script also increments the global variable gScrollV (or gScrollH) and sends out the message to the scrollable sprites to move themselves.



property pMyDirection    
-- options are Horizontal and Vertical
property pMyRange        
-- number of pixels that the thumb sprite may move
property pMyStartV       
-- initial locV of the thumb sprite
property pMyStartH       
-- initial locH of the thumb sprite
global gScrollV          
-- amount scroll has been incremented 
--Vertically from initial value
global gScrollH          
-- amount scroll has been incremented 
--Horizontally from initial value
on getPropertyDescriptionList
  set directionList = [#HORIZONTAL, #VERTICAL]
  set p_list = [ ¬
     #pMyDirection: [ #comment:   "Direction ¬
     of Button   Arrow", #format:   #symbol, ¬
     #range:    directionList,¬#default:   ¬
     #VERTICAL], #pMyRange:    [ #comment:¬
     "Range of motion (in pixels)", #format:   ¬
     #integer,#default:   100]]
  return p_list 
end
on beginSprite me
  if pMyDirection = #HORIZONTAL then
    sendAllSprites (#setThumbH, pMyRange, ¬
      the spriteNum of me)
    set pMyStartH = the locH of sprite the ¬
        spriteNum of me
    if gScrollH < pMyRange then
      set the locH of sprite the spriteNum ¬
         of me = pMyStartH + gScrollH
    else
      set the locH of sprite the spriteNum of me =¬
         pMyStartH + pMyRange
    end if
  else
    sendAllSprites (#setThumbV, pMyRange, ¬
      the spriteNum of me)
    beep
    set pMyStartV = the locV of sprite ¬
       the spriteNum of me
    if gScrollV < pMyRange then
      set the locV of sprite the spriteNum of me = ¬
       pMyStartV + gScrollV
    else
      set the locV of sprite the spriteNum of me = ¬
         pMyStartV + pMyRange
    end if
  end if
end
on scrollThumbH me
  if pMyDirection = #HORIZONTAL then
    set the locH of sprite the spriteNum of me = ¬
      pMyStartH + gScrollH
  end if
end
on scrollThumbV me
  if pMyDirection = #VERTICAL then
    set the locV of sprite the spriteNum of me = ¬
          pMyStartV + gScrollV
  end if
end
on mouseDown me
  if pMyDirection = #VERTICAL then
    set startMouse = the mouseV
    set startScroll = gScrollV
    repeat while the stillDown and gScrollV >= 0 ¬
          and gScrollV <= pMyRange
      set gScrollV = min(pMyRange, startScroll + ¬
            (the mouseV - startMouse))
      set gScrollV = max (gScrollV, 0)
      set the locV of sprite the spriteNum of me = ¬
            pMyStartV + gScrollV
      sendAllSprites(#ScrollV)
      updateStage
    end repeat 
  else
    set startMouse = the mouseH
    set startScroll = gScrollH
    repeat while the stillDown and gScrollH >= 0 ¬
          and gScrollH <= pMyRange
      set gScrollH = min (pMyRange,startScroll + ¬
            (the mouseH - startMouse))
      set gScrollH = max(gScrollH, 0)
      set the locH of sprite the spriteNum of me = ¬
            pMyStartH + gScrollH
      sendAllSprites(#ScrollH)
      updateStage
    end repeat
  end if
end


The behavior for the thumb track allows you to set how large an increment you want the thumb to move when the user clicks in the track. For the example movie, I used a value of 80. If you click in the thumb track, the thumb moves 80 pixels in the direction of the mouse. I use the max and min functions to limit the thumb motion to its boundary. Most importantly, the behavior sets the global variable gScrollV and broadcasts the message for all scrollable sprites to update their location.


property pMyDirection    
-- options are Horizontal and Vertical
property pMyIncrement    
-- number of pixels that the thumb sprite 
-- will advance per click
property pThumbSprite    
-- the sprite number for the scroll thumb
property pThumbRange     
-- the range of motion for the Thumb sprite
global gScrollV          
-- amount scroll has been incremented 
-- Vertically from initial value
global gScrollH          
-- amount scroll has been incremented 
-- Horizontally from initial value
on getPropertyDescriptionList
  set directionList = [#HORIZONTAL, #VERTICAL]
  set p_list = [ ¬
   #pMyDirection:   [ #comment:   "Direction ¬
   of Slider Track", #format:   #symbol, #range: ¬
   directionList, #default:   #VERTICAL], ¬
   #pMyIncrement:   [ #comment:   "Incremental ¬
   change per click (in pixels)", #format: ¬
   #integer,#default:   100]]
  return p_list 
end
on setThumbV me, range, thumbSprite
  set pThumbRange = range
  set pThumbSprite = thumbSprite
end
on setThumbH me, range, thumbsprite  
  set pThumbRange = range
  set pThumbSprite = thumbSprite
end
on mouseDown me
  if pMyDirection = #VERTICAL then
    if the locV of sprite pThumbSprite ¬
       < the mouseV then
      set gScrollV = min (pThumbRange, ¬
       gScrollV + pMyIncrement)
    else
      set gScrollV = max (0, gScrollV - ¬
        pMyIncrement)
    end if
    sendSprite (pThumbSprite, #scrollThumbV)
    sendAllSprites (#scrollV)
  else
    if the locH of sprite pThumbSprite ¬
     < the mouseH then
      set gScrollH = min (pThumbRange, ¬
        gScrollH + pMyIncrement)
    else
      set gScrollH = max (0, gScrollH - ¬
         pMyIncrement)
    end if
    sendSprite (pThumbSprite, #scrollThumbH)
    sendAllSprites (#scrollH)
  end if
end


Finally, the behavior for the scrollable sprites epitomizes the concept of Object Oriented Programming. This behavior allows each sprite to monitor the global variable gScrollV (or gScrollH) and adjust its own location accordingly. The key to this behavior is understanding 2 concepts. First, the sprites will be moving the opposite direction from the thumb. So, when you push the right arrow, the sprites move to the left.

Second, the amount that the sprite moves is dependent on its size. In our example, the ruler graphic which is 853 pixels wide and the stage is 352 pixels wide, so the minimum range of motion necessary is 853 - 352 = 501 pixels. In our example, the graphic doesn't start flush left, so I actually used 508 for the range. Now the complicating factor is that the thumb's range of motion is only 298 pixels. That means that my ruler graphic has to move approximately 5 pixels to the left for every 3 pixels that the thumb moves to the right.

As long as you input the initial range figure (508), the behavior will check the range of the thumb and make the necessary calculations to come up with its property pMyIncrement. Since I wanted the other sprites which appear to scroll in synchonization with the ruler, I input a value of 508 for all scrolling sprites. (Entering different values would give you some interesting results -- suggesting depth.)


property pMyRange        
-- number of pixels that the sprite may move
property pMyIncrement    
-- number of pixels that the thumb sprite 
-- will advance per click
property pMyStartV       
-- initial locV of the thumb sprite
property pMyStartH       
-- initial locH of the thumb sprite
global gScrollV          
-- amount scroll has been incremented 
-- Vertically from initial value
global gScrollH          
-- amount scroll has been incremented 
-- Horizontally from initial value
on getPropertyDescriptionList
  set directionList = [#HORIZONTAL, #VERTICAL]
  set p_list = [ #pMyRange:[ #comment:   ¬
    "Range of motion (in pixels)", ¬
    #format:   #integer,#default:   100]]
  return p_list 
end
on beginSprite me
  set pMyStartH = the locH of sprite ¬
   the spriteNum of me
  set pMyStartV = the locV of sprite ¬
   the spriteNum of me  
end
on setThumbH me, thumbRange, thumbSprite
  set pMyIncrement = pMyRange * 1.00/thumbRange
  set the locH of sprite the spriteNum of me = ¬
    MyStartH - integer(gScrollH * pMyIncrement)
end
on setThumbV me, thumbRange, thumbSprite
  set pMyIncrement = pMyRange * 1.00/thumbRange
  set the locV of sprite the spriteNum of me = ¬
    pMyStartV - integer(gScrollV * pMyIncrement)
end
on scrollH me
  set the locH of sprite the spriteNum of me = ¬
    pMyStartH - integer(gScrollH * pMyIncrement)
end
on scrollV me
  set the locV of sprite the spriteNum of me = ¬
    pMyStartV - integer(gScrollV * pMyIncrement)
end


NOTE: One thing frustrated me when working out this example. I couldn't figure out why some of the scrollable sprites weren't working. Turns out that they weren't getting the thumb range information which the thumb behavior sends out in its beginSprite handler (and without that info, they couldn't calculate their own pMyIncrement.) The reason is that the thumb sprite (and its behavior) were in a higher (lower number) sprite channel than the scrollable sprites. The scrollable sprite objects didn't exist yet when the thumb sent out its information. So, the fix was simply to move the thumb sprite to a channel number greater than all of the scrollable sprites.

Well, sorry there's so much code to this one, but this gets pretty complicated. You can't really skimp on the features, because everybody out there knows how a scroll bar is suppose to function.

Finally, I want to talk about repeat loops. Many people have heard that repeat loops are "dangerous", particularly in Shockwave. They don't really know why, but they just don't use them. Nonsense. The "danger" of repeat loops is also why they are powerful. While your Director or Shockwave movie is executing a repeat loop, the computer's full processing attention is focused on executing that code. That means that all other functions -- such as browser buttons -- are temporarily non-functioning. This is perfect for creating the smoothest motion/animation in Shockwave and Director.

In these behaviors, the repeat loops are only functioning while the mouse button is held down. So, the user couldn't be trying to do anything else anyway. Repeat loops ARE problematic if you set them to work in the background. So, don't shy away from repeat loops, just use them appropriately.

As I mentioned earlier, I've uploaded these behaviors to Ren Feinstein's Lingo Behavior Database, which is available in the Resources section of Director Online. A Director 6 movie with the behaviors is also available for download (HQX or ZIP).

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.