Gestures Glamour: Setting Up a Touch Menu
“The rise of touch and gesture-driven devices has dramatically changed the way we think about interaction. Gestures are more than merely entertaining, they are very useful and feel familiar” — Nick Babich, In-App Gestures And Mobile App User Experience.
Mobile touch gestures have become a vital part of every single app, a part that most users don’t even realize is there. Who doesn’t love (smooth) interactive apps?
And yet, it is incredibly easy to hate the mess and math that might come with it. A shocker, I know, especially when you are not the one who did the first piece of the code or if you are there only to maintain it.
It can be, at times, a thankless job. The kind that leaves you staring at the screen with one hand on your forehead while the other is resting on the mouse, scrolling time away.
Having — dare I say it? — silky smooth mobile touch gestures and their animations can be a challenge and one that becomes more and more prominent as the days go by. But that is a battle for another day. Or another article. Or both.
Today, we want to show you how to create a native-like menu with gestures.
Let the alpacas take the stage!
So, before I move on to the actual code, there are a few things I want to go through, so bear with me.
- touchstart: Triggered when you touch a DOM element.
- touchmove: Triggered when you drag your finger along a DOM element.
- touchend: Triggered when you remove your finger from a DOM element.
In these events, I’ll be using the touches property (there are two other properties but this is the one I care about for now). The touches property lists all fingers currently on the screen:
- PageX: Returns the x coordinate of where the finger is placed in the DOM. It’s measured from the left edge and considers horizontal scroll, if applicable.
- PageY: Returns the y coordinate of where the finger is placed in the DOM. It’s measured from the top edge and considers vertical scroll, if applicable.
And You Need To Know About requestAnimationFrame, Too
The requestAnimationFrame function tells the browser that you want to perform an animation. It asks the browser to call a specified function that will update an animation before the next repaint. Here’s what’s good about it:
- The browser will try to match the display refresh to allow for smooth animations.
- Animations in inactive tabs will stop (so less effort on the CPU).
- It doesn’t drain your battery life.
Drag, Tap, and Flick: Additional Stuff to Consider for Mobile Touch Gestures
These events need to be able to detect and distinguish between drags, taps, and flicks and do different things accordingly. So, when you’re playing with mobile touch gestures, think about:
- Limits: Where do you want the element to stop? How far do you want it to move with each drag?
- The direction of the gesture: Do you want it to be able to only move horizontally, or vertically? Maybe both?
- What do you want to happen when the drag finishes? Does it go back to the beginning or to the end, depending on where it finishes? Does it take speed into account?
- Details: Are we keeping speed in mind with this gesture? Do you want an overlay behind the menu that will grow darker as you open it?
In my case, I want the direction of the gesture to be horizontal only because I want the scroll to function normally. I have limits, and I want it to go back to the beginning or end. It depends on how much the user has dragged and the speed of the finger against the screen.
The Boring Part You Didn’t Know You Wanted to Know About — and is Super Important
I know you want to get to the interesting part of mobile touch gestures, but I have to cover this first because it affects your code. Yes, it’s time to discuss variables. The good news is I’m going to also explain why they have to be set and their values. These functions are going to make the code look cleaner.
Global Variables and Setting Defaults
Ahhh, so much fun! Look at the number of variables needed; just what most people tend to skip. (Don’t. You’ll regret it if you do.)
Very straightforward, really. In this order, the code is less cluttered, less scary and easier to digest.
Function in the Functions
These functions are called by the EventListener even if they are not the ones that do the actual animations or the calculations necessary to make the menu work.
All these variables are used for the math involved in the animation. For the sake of readability, and so there aren’t too many lines of code in a function, I separated them all into smaller ones.
The Interesting Part of This Mobile Touch Gestures Stuff — Finally!
Now that my explanations of touch events, variables and functions are out of the way, it’s time for me to focus on describing how to create the animation. This is exactly what makes the menu move and all the math and algorithms behind that.
Start of the Animation
This code will run every time you touch the screen. This function will be used as a reset to defaults depending on what happens to the menu since you last lifted your finger.
Middle of the Animation
The first thing you want to know is the direction of the gesture.
In a menu, vertical scroll really isn’t something to care about. Meaning, in terms of gesture-related code, the behavior itself should be the default scroll. Therefore, identifying when it’s one or the other is what is needed here:
Without preventDefault, this is what would happen:
This is definitely not something that you want to happen with your mobile touch gestures, so consider this: Are you interested in reading what is hidden by the scroll when you are opening/closing the menu? If your dragDirection is Horizontal, you can’t scroll.
We Need Some Boundaries Here! (Setting the Limits)
So, remember when I said I had limits? In this example, the menu is hidden on the left part of the screen. So, if the menu is closed, the variable moveX starts off as -menuWidth — and I want it to be dragged to the right until it is fully shown.
You could call this the moving interval. This is what tells the script exactly where the menu is in the window. I used moveX because of the way I do the actual animation. Go to the updateUI function — the function called by the requestAnimationFrame — and this is what you have:
I want the animation to be seamless and smooth. For that, the smaller the intervals the script can detect and use for translateX, the better. The goal is not to see the jump caused by using translateX instead:
Now that this is done, the next step is to calculate how the fade of the overlay is going to work.
The goal is:
- when moveX = -menuWidth, opacity = 0
- when moveX = 0, opacity = 0,5
However, the calculations are not quite so linear. The problem is always the zero which breaks the three-way rule usually applied in these cases.
Here, I am ensuring that the menuWidth corresponds to 100% and the current position (moveX) corresponds to a percentage. The percentage that follows what I’m looking for in this calculation:
This calculation is needed because opacity doesn’t really work unless it’s between 0 and 0.5 (as defined in the variables). If 0.5 opacity relates to 100%, the percentage will be the desired opacity.
End of the Animation
The first thing to remember is that someone can simply just click, which the events recognize as a touchstart and touchend. And if it is a click, nothing should happen to the menu.
What is meant to happen when the drag finishes?
- When the menu is open, it can either close or remain open — with the animation — while it returns to where it was before.
- If it’s closed, then it can either open or remain closed — also, with the animation — while it returns to where it was before.
So what is considered enough to open the menu? Five pixels moved? Well, this menu can open or close based on the distance. That is, if you drag it past the middle of its width, and if the speed of the drag is greater than the defined velocity.
And that’s it, you have a working touch-based menu! Stay tuned, because we have more examples of creating mobile touch gestures coming soon.
Further Reading and References
Menu App (remember to activate device simulation for mobile!)
Smooth as Butter: Achieving 60 FPS Animations with CSS3, José Rosário
In-App Gestures And Mobile App User Experience, Nick Babich
Touch Gesture Reference Guide, Luke Wroblewski
Multi-touch Web Development, Boris Smus
Using requestAnimationFrame, Chris Coyier
Rita Dias | Mobile and Front-End Expert @ OutSystems