Build your own touch slider with hammer.js
I ran into this problem developing Real Thread’s mobile interface. Within that box where the shirt image sits, you’ll find:
- Angular code that swaps background images out with the shirt color you’ve selected
- A file uploader handled by DropzoneJS
- Touch events that allow you to move, pinch–scale, and delete the artwork you’ve uploaded
Is HammerJS right for me?
still get the expected stuff like
clientY to track current position, but you get
access to new events like
rotate, along with new properties like
scale, to name a few.
Before I waste any more of your time, ask yourself: which sounds more like me?
Check out their examples to play around with it.
It’s a basic requirement of sliders to actually slide (i.e., not be stacked layers that simply
fade opacity). The easiest way to accomplish this is to have one container that is as wide as all
the slides, side-by-side (
.slider), within a container that crops everything to
.has-slider) so no scrolling happens. The markup should be something similar to:
<div class="has-slider"> <div class="slider"> <div class="slider-panel"></div> <div class="slider-panel"></div> <div class="slider-panel"></div> </div> </div>
And the important CSS looks like (Sass notation):
.has-slider overflow: hidden width: 100% .slider width: 300% // 100% * 3 slides
are our most performant way to move something around the page, we’ll take advantage of
transform: translateX() to do the heavy lifting for us. Since
.slider is the entire width of all
3 slides (
300%), we’ll apply
.slider-panel we want to show within the viewport.
Working with Hammer
I prefer working with managers. Compared to the
new Hammer() syntax, managers give you the flexibility of tying multiple events
together (such as
pan) on the same element. For example, if you wanted to recreate the
pinch + rotate gesture in the latest iOS Photos app, you could do that in HammerJS using a manager.
Managers don’t have any advantages if you’re only listening for one event (like in our example), but
I do this by default because it allows me to track additional events later if I need to without
rearranging my code.
Step 1: Adding the listener
If we add the following code to our project:
We don’t have a working slider yet, but in your console you’ll get an object with lots of useful
scale, etc. One way to visualize that output is like
Since we’re building a horizontal slider,
deltaX will be the most useful. This tracks the net
horizontal distance between where the gesture started and where the last registered touch event was.
To illustrate how handy this is, let’s compare what we’d have to do without HammerJS:
- Set up different event listeners for
- Store the values for the initial
clientYfor the first pointer in
touchstartin a variable outside the scope of your event listeners
touchmoveevent, calculate the stored values minus the current
clientYto get the net distance
- Find a workaround for IE & Edge (OSX Safari and Firefox don’t support it either, but effectively none of your users will need touch support for those browsers)
- Make one
- Net distance already done for you with
It’s your choice, but I’m sticking with HammerJS.
Step 2: Applying the transform
Pressing forward, let’s apply a CSS transform to our element (and save it in a variable so we don’t repeat ourselves #DRYlife):
Step 3: Keeping track of which slide we’re on
Things will get a little more complicated here, but this is the last major addition. Let’s add an
activeSlide variable to keep track of which slide we’re on, and then have it snap into place when
we’re done with the event with a new function:
Now this changes slides on release, but it has 2 key problems: it jumps between slides rather than
animating smoothly, and it’s also too sensitive — it changes slides if you drag it even
1 pixel in
The only things remaining for this to be a full-fledged slider are adding pagination, and applying a CSS transition at just the right time to animate smoothly when snapping to a slide. The reason you don’t want to have a transition applied all the time is this will really make your touch events feel sluggish when dragging:
Instead, we’ll stick the transition on an
.is-animating state class in our CSS rather than
between touch events). The final result, with pagination, looks like this:
If you inspect the JS for this last CodePen, it’s a little more object-oriented but is still the
same code we’ve been working with all along. The only thing remaining would be to add a “flick”
listener to the slider, in case a user swiped their finger quickly.
velocityX helps us out here.
This can be added by modifying the
.isFinal conditional since we only want to register a flick at
the end of a gesture:
This is all merely proof of concept, and needs refactoring to fit into your setup. But anyways, there you have it: a fairly decent slider that doesn’t get in the way of your application.