Flickable.js: A Zepto Plugin to Enable Touch Gestures on Any HTML Element

Lately I’ve been working on a mobile web application using HTML + PhoneGap to create an iOS app. For Javascript I’ve been using Zepto to handle basic gesture events, but for more complex touch interactions I found Zepto lacking.

So I created my first Zepto plugin called Flickable (jump to download). The plugin allows you to make any element touchable; useful for flicking between sections, or sliding elements around the page.

Demos

Before I get to how Flickable works, here are some demos to show you what it does. These demos are currently optimised for iOS and Android devices only. iOS 5+ is recommended because of its GPU acceleration for CSS transitions.

Page Flipper

Flickable Demo 1

This demo allows the user to drag or flip between pages, similar to the way you would flip through photos on a phone.

Thumbnail Slider

Flickable Demo 2

This is similar to the way the App Store allows you to flip through app screenshots, but it can also be used to allow users to slide through navigation items that don’t fit within a single page.

Vertical Jigsaw

Flickable Demo 3

Slide up or down on the three segments to match up the images.

How it works

Flickable is designed to be flexible and work around your markup. It doesn’t generate any wrapper elements and doesn’t need a special CSS file to be included.

Say you had this HTML setup:

My intension is to show just one of those green boxes (<section>) on the screen at a time, and allow the user to swipe between them. In this case I have placed the #flickable_wrapper element that contains the boxes inside a #wrapper element which has an overflow: hidden applied to it so that there is only room for 1 box at a time.

Now I can apply Flickable to it like so:

$('#flickable_wrapper').flickable({segments:3});

Flickable will automatically calculate the width of each segment by dividing the #flickable_wrapper width by the number of segments specified (in this case 3). That’s it! Now I can move #flickable_wrapper around with my finger and Flickable will snap to the nearest segment once I release.

See the Page Flipper demo above for an example of how this would work.

Installation

To install Flickable, first download the plugin file and include it in your HTML after Zepto.

<script type="text/javascript" src="libs/zepto.min.js"></script><script type="text/javascript" src="plugins/zepto.flickable.js"></script>

Usage

To use it, simply target the element you want to be Flickable, and apply the plugin to it. At minimum you should provide the number of segments Flickable will slice the element.

HTML:

<div id="wrapper">
<ul id="flickable-element">
	<li>Content</li>
	<li>Content</li>
	<li>Content</li>
</ul>
</div>

JavaScript:

$('#flickable-element').flickable({segments:3});

Fallback for desktops

If you need to allow for non-touchscreens you can bind the available Flickable methods to click events, like so:

$('.next').click(function() {
    $('#flickable-element').flickable('scrollNext');
});

Documentation

Event Callbacks

Event Type Description
onCreate function(flickableObjects) Triggered when Flickable object is created.
onStart function(eventData) Triggered when a touch event begins.
onMove function(eventData) Triggered when the element is moved via a gesture in any direction.
onScroll function(eventData) Triggered when element snaps to the nearest segment in any direction.
onScrollPrev function(eventData) Triggered when element snaps to the previous segment.
onScrollNext function(eventData) Triggered when element snaps to the next segment.
onFlick function(eventData) Triggered when element user flicks in a valid direction.
onFlickLeft function(eventData) Triggered when element user flicks from right to left.
onFlickRight function(eventData) Triggered when element user flicks from left to right.
onFlickUp function(eventData) Triggered when element user flicks from bottom to top.
onFlickDown function(eventData) Triggered when element user flicks from up to down.
onEnd function(eventData) Triggered when the user lifts their finger off the screen, ending the touch event.

Most events also include the event data object, which is structured like so:

eventData = {

   // Starting touchpoint [x pos, y pos, timestamp]
   start: {x:0, y:0, time: 0},

   delta: {

      // Previous touchpoint
      prevPos: {x:0, y:0},

      // Distance relative to original touchpoint
      dist: {x:0, y:0},

      // Direction of touch
      // [-1 left/up, +1 right/down, 0 no movement]
      dir: {x:0, y:0}

   },

   end: {

      // Duration of touch
      duration: 0,

      // Speed of movement along x and y axis
      speed: {x:0, y:0},

      // +1/-1 if the touch was deemed to
      // be a flick left/right up/down
      flick: {x:0, y:0}
   }

}

Options

Option Type Default Description
segments Number 5 Number of segments in which to divide the target element.
flickThreshold Float 0.7 Threshold in which a simple “touch and move” gesture becomes a “flick”.If you’re targeting Android, you may need to lower this to make it more sensitive because of Android’s lower frame rates for translate3d animations.
flickDirection ‘x’ or ‘y’ ‘auto’ Direction in which to divide the element into sections, and in which the user can flick.If not specified, the direction will be automatically calculated based on which side of the target element is the longest.
preventDefault Boolean true Whether or not to cancel default actions on the target element. Note that if this is set to false, the page will scroll with the gesture.

Methods

Method Description Example
segment Gets or sets the current segment. Note segments start at 0. $(‘#thing’).flickable(‘segment’); //gets current segment
$(‘#thing’).flickable(‘segment’, 5); //sets segment to 5
scrollNext Scroll to next segment. $(‘#thing’).flickable(‘scrollNext’);
scrollPrev Scroll to previous segment. $(‘#thing’).flickable(‘scrollPrev’);

Download Flickable

Download and read full documentation on GitHub.

UPDATE: Added GitHub link.

Comments

  • http://twitter.com/Leimina Emmanuel Pelletier

    This seems really nice. I’ll test it for sure! Great work :)

  • Anonymous

    Thanks Emmanuel :) Hope it works out for you!

  • Joshua

    Very nicely done. I’d like to use it on a pretty high profile redesign I’m doing now but I’m running into a problem with the onScroll callback.

    I find that it’s being called on every step of animation instead of when the scroll is complete, and unfortunately this is causing some pretty bad hitching.

    For example, on a 320px wide object I get 45 messages to the console:

    $(‘.gallery-items ul’).flickable({segments:10, onScroll: function(){ console.log($(this).flickable(‘segment’)); } });

  • Ben

    This looks awesome, I’ll be rebuilding my site and this looks like a nice enhancement to responsive layouts.

  • http://twitter.com/ppcbz barman

    this is most excellent.

  • Anonymous

    Thanks Joshua! I’ll look into that bug today and let you know. There’s another thing I’m wanting to patch too so I’ll likely push out a new version within the next day.

  • Anonymous

    Hey again. I’ve just committed some changes to GitHub that should hopefully fix your issue.

    I’ve also added the segment number in the event callback so you don’t need to use flickable(‘segment’) to retrieve the segment. For example:

    $(‘.gallery-items ul’).flickable({
    segments:10,
    onScroll: function(eventData, segment){
    console.log(segment);
    }
    });

    See readme for info on the other options i’ve added.

    Let me know if that did the trick!

  • Joshua

    Fantastic, thanks so much!

  • http://twitter.com/rubylillie ruby lily

    can’t wait to try this out, I’ve had a few projects come up asking for this kind of functionality. thanks for sharing it!

  • http://twitter.com/rubylillie ruby lily

    I’m getting a “Uncaught TypeError: Object [object Object] has no method ‘flickable’” when I try to run the demo as downloaded from github. Could it be missing some part?

  • Anonymous

    hmm that’s weird. I’m not getting it when I try, what browser are you running it in?

  • http://twitter.com/rubylillie ruby lily

    I was trying IE9 to check out the prev/next text links.

  • Anonymous

    yeah, the target platform for flickable was mobiles so I didn’t worry too much about IE.

    however if you watch the repo I’m actually working on releasing a version that works with jQuery, which means I can have an IE-friendly fallback.

  • deimos

    @TomLongo
    Is there a way to keep track of which item/element is currently active/shown after a flick to the left or right?

  • Joa

    Wow, this is amazing! Is there a best practice to handle the orientation change without scaling the content? I’ve tried to recreate the flickable but it didn’t work:

    window.addEventListener(‘orientationchange’, function() {
    $(‘#demo2′).css(‘width’, (3*$(‘body’).width()) + ‘px’);
    $(‘#demo2 li’).css(‘width’, $(‘body’).width() + ‘px’);
    $(‘#demo2′).flickable({segments:3});
    }, false);

    Is it further possible to nest flickables? Thanks again!

  • Joshua

    FYI: http://mobile.infiniti.com/
    Just launched this morning, Flickable is used for the galleries and works great.

  • Film42

    I love this. One thing to make it more like the app store would be to allow scrolling up and down. That is, when scrolling on a page, it snaps to the scroll view and when you flick up or down it pulls you out, again, just like the app store. Maybe a weekend project for me. Anyways, you’re amazing, keep up the good work man!

  • Paul Wong

    Thanks for this! Looks amazing.
    I was wondering if it’s possible to have a vertical scrollable “card” and flick “left/right” at the same time.
    Similar to prev generation Google+ app, when viewing streams.