Horizontal fake scroll bar (mobile)

Hi community. I would appreciate if you can help me, I am not advanced in Axure. What I need to do:

  • 2 cards that are horizontally scrollable on mobile, one of the cards is cut with a viewport. I did it with the “Dragged” interaction for a Dynamic Panel (=cards), screen attached. The interaction works fine.
  • now I would like the scrollbar indicator to move from the left part to the right part once the position of the dynamic panel (=cards mentioned above) is moved to the right. Also, when scrolling to the left, the bar would move back accordingly like a gallery slider indicator. The interaction doesn’t have to be smooth and perfect (no overengineering) but it should be at least somehow visible for a quick user test. That’s why I thought about creating a dynamic panel with 2 states for the scrollbar - with the indicator left (state 1) and right (state 2) but where I’m stuck is - what kind of action exactly do I need to make it work? By attaching it to the x position of the dynamic panel (=cards)? Sorry if the solution is obvious but all what I tried didn’t work and I can’t find the answer so far.
    Thank you.

Here is one thread from a few months ago:

There are two basic approaches I know of for this. Here is a demo of both approaches, with a page for Vertical and a page for Horizontal (if needed, both can be combined for a Panning experience.)
scroll vs drag a list.rp (129.6 KB)

  1. Use the built-in scrollbar support. This may/not show in mobile based on how the mobile browser handles scrollable areas. Much easier, as the scrollbar is automatically handled.

  2. Use the Dragged event and Move events (as you’ve done) to move the contents of a dynamic panel (with Drag X for horizontal) and move a scrollbar widget the opposite direction. I’ve had to work out all the details to prototype custom embedded UIs and custom-styled scrollbars, and it works well for mobile UIs. The tricky parts are calculating the size of the scrollbar and how much to move it.

With the second approach there are some details to take care of:

  • The scrollbar should be sized based on the relative size of the contents to the viewport
    • As this ratio grows (contents are increasingly greater than viewport) the scrollbar shrinks
    • For horizontal scrolling, if the width of the contents is less than or equal to the viewport, no scrolling is needed, so no need to show the scrollbar or move the contents.
  • The scrollbar needs to move in the opposite direction of the contents, and a fraction of this movement
    • The size of the contents inversely affects the size of the scrollbar, and the movement of the contents along with the size ratio of Contents/Viewport affects how much to move the scrollbar. So, the size and motion are tied together.
  • Movement of the contents and movement of the scrollbar need some boundary limits, so things aren’t “scrolled” too far.
    • Optionally, some effects can be added to allow “over scrolling” with a spring- or slide-back experience.
  • If any content widgets are clickable, you need to differentiate between a drag release and an actual click.
    • Otherwise, something can be inadvertently clicked when it really should just be dragged/scrolled, and attempting to click a widget only results in a tiny bit of dragging/scrolling movement.
    • In my experience, what actually happens by default is usually the opposite of the user intent: a drag results in a click when the drag stops, and a click results only in a drag–especially in a finger touch UI like mobile.

On the Horizontal page, for the “Drag method” section, I have the following structure:

  • List Mask is a fixed dynamic panel (no Fit to Content) wrapping the contents. This provides the viewport area.
    • List Contents is a variable-size dynamic panel (Fit to Content checked on)
      • List Repeater is used for the actual contents.
      • Note this could be any widget or set of widgets. You can delete or hide List Repeater and add in your own widgets and it should all work without changing any of the Interaction code.
    • scrollbarHoriz is a dynamic panel used as a scrollbar widget.
      • There is nothing in this dynamic panel, but it is styled with a fill (partial opacity so it works well if it needs to float in front of content) and rounded corners. The corner radius is preserved when this is resized, which is nice.
      • The default size is arbitrary. Its width will not change, but its height will get automatically set based on the ratio of List Content height / List Mask height.
      • Any widget could be used instead of a dynamic panel (or placed inside this dynamic panel) as long as it can be “safely” (properly) resized.
    • Two widgets used to give the viewport “fading edges”. Totally optional–delete if not needed.
      • gradientLeft
      • gradientRight
    • A set of widgets used as “local page variables” to help handle “content clicks” vs dragging/scrolling, help with “remote scrolling” (e.g., maybe buttons to “scroll up”, “scroll to top”, “scroll to bottom”, “scroll to section”, etc.), and also for demonstration purposes. If you don’t have clickable or interactive content you can ignore these.
      • dragHold : set to “false” by default; “true” during an active drag
      • dragSlop : used to account for “sloppy clicks” in which the mouse/finger is moved a little bit after press but prior to release. This is practically guaranteed in mobile UIs.
        • Even a 1 px movement will trigger a Dragged event which can conflict with a Click or Tap event.
        • I set this to 10 so that dragging the contents 10 px or less then releasing results in a click instead of only a “scroll”.
        • Higher number means more slop allowed when clicking.
      • dragLoc : the position of List Contents within List Mask
      • itemSelect : which repeater item is selected (has been clicked)

List Mask has:

  • a Mouse Button Down event that sets dragHold to “false” (so assumes a click is intended)
  • a Dragged event that moves List Content.
    • It only fires if the content is wider than the mask (viewport)–otherwise, no need to move/scroll horizontally.
    • Additionally, a case tests how much horizontal drag has occurred (how many pixels the cursor has moved left/right since touching down.) If greater than dragSlop then dragHold is set to “true” --indicating a real scroll is now active. This is used to ignore any Click or Tap and Mouse Button Up events; otherwise if you happen to drag on clickable content (like a button) and release, it can fire that widget’s click event, and the user will be confused or frustrated.

List Content has:

  • a Moved event that moves the scrollbar widget in the opposite direction, taking into account the relative size of List Content to List Mask, and with boundary limits (maintaining a 10 px margin from top and bottom of the List Mask viewport.) It also stores its current location (mostly just for this demo.)
    • Move scrollbarHoriz by [[ -dragX * M.width / This.width ]]
      • ‘dragX’ is a built-in cursor property indicating how many pixels in x-dimenison the cursor has moved during the drag.
      • ‘M’ is a local variable I created, pointing to the List Mask dynamic panel.
      • ‘This’ is a built-in variable pointing to the same widget that has this action–so is a shorthand for the List Content dynamic panel in this case.
    • Set Text dragLoc to "[[This.x]] , [[This.y]]"
    • There are a set of cases to handle showing/hiding the “edge gradients” as well as moving the scrollbar all the way left or all the way right when List Content is located at its limits. This provides a way to clean up any errors in the position of the scrollbar.
  • a Resized event to change the size of the scrollbar.
    • Set Size scrollbarHoriz to [[(M.width / This.width) * M.width]]w x Heighth anchor left
      • ‘M’ is a pointer to List Mask
      • the height field is left blank so it won’t change its height value
      • ‘anchor left’ means the widget will grow to the right
    • Also a case to hide the scrollbar and gradients if no scrolling is needed, and show it if it does.
    • The Resized event should get called automatically when loaded due to the repeater contents.
      • If Resized doesn’t get triggered at load, you can add a Loaded event with:
        Fire Event Resized of This so the scrollbar gets sized correctly initially.

In my demo prototype you can click ‘Show Variables’ to see how these work dynamically. You can also change the width of List Contents by removing and adding items. The scrollbar should resize and hide/show accordingly.

If you are interested in handling clicks as well as drag-to-scroll, I’ve found that using the Mouse Button Up event is more reliable than Click or Tap when used in conjunction with drag events (Dragged, Drag Started, Drag Stopped). Basically in Axure, Mouse Button Up will always get fired after a drag event, but sometimes a Click or Tap gets ignored after a drag event. Furthermore, as I explained above, you’ll want to clearly and reliably differentiate between the user intent of “click this” versus “just scroll.” So to handle all this, I set up a series of events. The pseudo-logic goes like this:

  1. The very first thing to happen in any click or drag is the mouse or finger/stylus touches the screen. In Axure this can be detected with the Mouse Button Down event. So the List Mask has one that sets dragHold to “false”. This assumes a click will be made.
  2. If there is any mouse/finger/stylus movement prior to releasing the touch, the Drag Started and Dragged events will be fired. So the List Mask has a Dragged event with a conditional case testing for 10 px or greater movement and dragHold = “false” --so it will fire the first time there is 10 px of movement during a drag–and if so, dragHold gets set to “true”. Thus, if the mouse/finger/stylus moves more than the dragSlop amount of 10 px, the user action is no longer considered to be a click and is treated as a “drag-to-scroll” action.
  3. I use the Mouse Button Up event on any clickable widget in the List Contents dynamic panel to handle potential clicks. A conditional case tests if dragHold equals “false”–thus not a “drag-to-scroll” thing–and if so, processes the “click.” Otherwise, nothing happens–the drag-to-scroll motion should be the only result.

You should be able to use my List Mask dynamic panel in any prototype without needing to change the interaction code. Place whatever widgets you need within List Content and set the default size of List Mask as you need. The scrollbar should get sized automatically and move properly with dragging.