Intuitive jQuery scrolling with mouse or touch input

Recently a design required a cross device solution for rotating an object about a central axis. The current solution involves the user pressing and holding a left or right button, which functions, but it’s none too friendly on touch devices. It’s a little bit clunky to use on a touch device where users are accustomed to swipe at various objects to move them.

We set out to make a solution that felt intuitive to use with mouse or touch input, without any code rewrites to accommodate the nuances between the two. The solution ended up being a jQuery plugin to handle, interpret and dispatch various events on either device as well as handling the animation requirements of a frame-by-frame system.

Read on for an explanation of how I solved the problem, or skip to the live demo and source.

Preparing Assets

The current system is downloading ~120 jpeg’s at total cost of about 1.5 MB. The example in this article is using 74 frames at 550kb’s and it’s a fairly plain image on its own. We experimented with a single massive sprite which reduced the total to 1 image and about 880kb’s. The end functionality was identical on both devices, but with an unexplained severe degradation of quality on the touch device.

The degraded quality ended up being a limitation the devices have on the size of an uncompressed image in memory. Our jpeg was on average 900 kb’s but it was about 32 mb’s uncompressed in memory, shattering the 8mb limitation.  We’ve still got a few experimental ideas in the works, but save for this article each frame is a single asset being downloaded.

Implementation

The initial version was written on top of an existing 30-line jquery plugin. This pugin simply detected touch movement and in what direction. If dx > 0, then the user was swiping down. I required this base since I had never worked with a touch input system and didn’t know the events would have been different.  My pre-existing thought was the touch input devices simply emulated mouse input and a website would have worked from that.

A new phone later (my very first smartphone!  I had to mention my toy in here somewhere :) ) and I had a means of poking at the code from a touch input perspective. It didn’t work so well..

Unfortunately it was easy to overlook touch input not ever having a mouse up/down state exclusive from the movement event. It made grabbing  and dragging/rotating the image a bit of an exercise.

Rethink – Adding proper touch support

Originally the plugin worked by having a drag state where the mouse was down and once released the next 100ms was used to calculate  the velocity of the mouse movement. This actually worked well on a desktop computer, but the trouble came on the touch input.  Once the ‘mouse’ was up, it no longer moved since nothing was touching the screen.. der.

So instead the plugin was modified to track every movement. Once the user took their finger off the screen or released the mouse button the plugin then calculated the inputs escape velocity based on what it was doing when it let go, not what it was doing afterwards.

Voila.  On both inputs we now had the plugin feeling like the user was throwing a ball. If you release the ball at the height of the throw then the ball will be thrown over a certain distance given the release velocity. If you release after pausing mid throw you’ll look a little silly but the ball will also just fall to the ground.

PaintMe Example


PaintME: Shows the initial mousedown/touchstart event as a red square, with dragging events showing as green squares. The mouseup/touchend event is shown as a small blue square. The arrow indicates the start and end points used to calculate the release velocity as well as the direction of the release. The yellow spheres indicate the exponential decay of velocity over time.

So we had set inputs and events for input on two devices. Once this was done a few simple animation functions were added. The animation timers tick at set intervals based on parameters passed to the plugin, like frames per second.

The plugin is self contained so it should be easy to use in similar applications. It’s possible to assign the plugin to a canvas/div/etc. and override the animate(e, f) function which will nag you whenever the current frame should be altered. Cake.

Play around in the example above and below to see the outcome.

Live Demo and Source Files


Rendered 3D animation of an assassin by Adam Brook. Click and drag, drag and throw. Enjoy :)

Download

Example and Source – .zip

Comments

  • Jason Noone

    This is simply awesome, thanks! World of Warcraft allows you to download a single jpg image containing 24 different views of an item evenly spaced. I use ImageMagick to then break this single image down to 24 individual images and use this plugin to display them. Using the WoW API, I can now call any item’s image in and display it in 3D.

    This is an example of an image I am breaking down for reference:

    http://us.media.blizzard.com/wow/renders/items/item60288.jpg

    Then to split it into individual images, I use ImageMagick like this:

    convert item60288.jpg -crop 280×280 +repage +adjoin item60288-%02d.jpg

    Then those images are used with this plugin.

    Makes my fansite look much more professional.

  • Geoff

    Glad you found a use for it. I would be very interested to see the end result once it’s done :)

  • Olle Karneman

    Hi
    Very nice. I’m trying to do a image scrubber.
    My setup is something like this

    instead of canvas.

    Would it be had to make? I’m a little inexperienced with javascript?

    Regards
    Olof

  • Hamza

    Hi Geoff, great article. Did you find a way around the degraded image quality? I’ve been struggling with that.