Dragdealer.js

Jan 2014: Almost 4 years from the initial Dragdealer launch, a new version is released. For more details read the blog post or check out the freshly baked GitHub repo.

Drag-based JavaScript component, embracing endless UI solutions

2d dragging and tapping, mouse and touch, ~12KB. No dependency. Any browser.

<div id="demo-simple-slider" class="dragdealer">
  <div class="handle red-bar">drag me</div>
</div>
new Dragdealer('demo-simple-slider');
drag me

JS API

Here are the options, callbacks and methods Dragdealer supports, but you can read the source code for more information.

Constructor

  • Dragdealer(wrapper, options={}) Accepts an id or a DOM reference for the wrapper element. See possible options below.

Options

  • bool disabled=false Init Dragdealer in a disabled state. The handle will have a .disabled class.
  • bool horizontal=true Enable horizontal dragging.
  • bool vertical=false Enable vertical dragging.
  • number x=0 Initial horizontal (left) position. Accepts a float number value between 0 and 1.
  • number y=0 Initial vertical (top) position. Accepts a float number value between 0 and 1.
  • number steps=0 Limit the positioning of the handle within the bounds of the wrapper, by defining a virtual grid made out of a number of equally-spaced steps. This restricts placing the handle anywhere in-between these steps. E.g. setting 3 steps to a regular slider will only allow you to move it to the left, to the right or exactly in the middle.
  • bool snap=false When a number of steps is set, snap the position of the handle to its closest step instantly, even when dragging.
  • bool slide=true Slide handle after releasing it, depending on the movement speed before the mouse/touch release.
  • bool loose=false Loosen-up wrapper boundaries when dragging. This allows the handle to be *slightly* dragged outside the bounds of the wrapper, but slides it back to the margins of the wrapper upon release.
  • number top=0 Top padding between the wrapper and the handle.
  • number bottom=0 Bottom padding between the wrapper and the handle.
  • number left=0 Left padding between the wrapper and the handle.
  • number right=0 Right padding between the wrapper and the handle.
  • fn callback(x, y) Called when releasing handle, with the projected x, y position of the handle. Projected value means the value the slider will have after finishing a sliding animation, caused by either a step restriction or drag motion (see steps and slide options.)
  • fn dragStopCallback(x, y) Same as callback(x,y) but only called after a drag motion, not after setting the step manually.
  • fn dragStartCallback(x, y) Same as dragStopCallback(x,y) but called at the beginning of a drag motion and with the sliders initial x, y values.
  • fn animationCallback(x, y) Called every animation loop, as long as the handle is being dragged or in the process of a sliding animation. The x, y positional values received by this callback reflect the exact position of the handle DOM element, which includes exceeding values (even negative values) when the loose option is set true.
  • string handleClass=handle Custom class of handle element.
  • bool css3=true Use css3 transform in modern browsers instead of absolute positioning.
  • fn customRequestAnimationFrame Provide custom requestAnimationFrame function (used in tests).
  • fn customCancelAnimationFrame Provide custom cancelAnimationFrame function (used in tests).

Methods

  • disable Disable dragging of a Dragdealer instance. Just as with the disabled option, the handle will receive a .disabled class
  • enable Enable dragging of a Dragdealer instance. The .disabled class of the handle will be removed.
  • reflow Recalculate the wrapper bounds of a Dragdealer instance, used when the wrapper is responsive and its parent container changed its size, or after changing the size of the wrapper directly.
  • getValue Get the value of a Dragdealer instance programatically. The value is returned as an [x, y] tuple and is the equivalent of the projected value returned by the regular callback, not animationCallback.
  • getStep Same as getValue, but the value returned is in step increments (see steps option)
  • setValue(x, y, snap=false) Set the value of a Dragdealer instance programatically. The 3rd parameter allows to snap the handle directly to the desired value, without any sliding transition.
  • setStep(x, y, snap=false) Same as setValue, but the value is received in step increments (see steps option)

Demos

Some demos will use jQuery to manipulate surrounding elements. This is for keeping the examples as small as possible, Dragdealer doesn't have a jQuery or any other dependency.

Just a slider

A slider is just a user control, the power lies in the value it represents. For this reason the animationCallback is your biggest ally, with it you tie the user input to any visualization you can think of. This is the most boring example.

new Dragdealer('just-a-slider', {
  animationCallback: function(x, y) {
    $('#just-a-slider .value').text(Math.round(x * 100));
  }
});
%

Content scroller

Controlling a different element is a straightforward use-case for Dragdealer. It's basic math. Let's spice it up with some vertical movement.

var availHeight = $('.content-body').outerHeight() -
                  $('.content-mask').outerHeight();
new Dragdealer('content-scroller', {
  horizontal: false,
  vertical: true,
  yPrecision: availHeight,
  animationCallback: function(x, y) {
    $('.content-body').css('margin-top', -y * availHeight);
  }
});

It was all a dream
I used to read Word Up magazine
Salt 'n' Pepa and Heavy D up in the limousine
Hangin pictures on my wall
Every Saturday Rap Attack, Mr. Magic, Marley Marl
I let my tape rock 'til my tape popped
Smoking weed and Bambu, sipping on Private Stock
Way back, when I had the red and black lumberjack
With the hat to match
Remember Rappin Duke? duh-ha, duh-ha
You never thought that hip hop would take it this far
Now I'm in the limelight cause I rhyme tight
Time to get paid, blow up like the World Trade
Born sinner, the opposite of a winner
Remember when I used to eat sardines for dinner
Peace to Ron G, Brucey B, Kid Capri
Funkmaster Flex, Lovebug Starski (wassup)
I'm blowing up like you thought I would
Call the crib, same number same hood (that's right)
It's all good (it's all good)
And if you don't know, now you know, nigga

I made the change from a common thief
To up close and personal with Robin Leach
And I'm far from cheap, I smoke skunk with my peeps all day
Spread love, it's the Brooklyn way
The Moet and Alizé keep me pissy
Girls used to diss me
Now they write letters cause they miss me
I never thought it could happen, this rapping stuff
I was too used to packing gats and stuff
Now honeys play me close like butter play toast
From the Mississippi down to the east coast
Condos in Queens, indo for weeks
Sold out seats to hear Biggie Smalls speak
Living life without fear
Putting 5 karats in my baby girl's ears
Lunches, brunches, interviews by the pool
Considered a fool cause I dropped out of high school
Stereotypes of a black male misunderstood
And it's still all good
Uh...and if you don't know, now you know, nigga

Super Nintendo, Sega Genesis
When I was dead broke, man I couldn't picture this
50-inch screen, money green leather sofa
Got two rides, a limousine with a chauffeur
Phone bill about two G's flat
No need to worry, my accountant handles that
And my whole crew is lounging
Celebrating every day, no more public housing
Thinking back on my one-room shack
Now my mom pimps a Ac with minks on her back
And she loves to show me off, of course
Smiles every time my face is up in The Source
We used to fuss when the landlord dissed us
No heat, wonder why Christmas missed us
Birthdays was the worst days
Now we sip champagne when we thirst-ay
Uh, damn right I like the life I live
Cause I went from negative to positive
And it's all

...and if you don't know, now you know, niggaaa

The yPrecision option adjusts the (vertical) granularity of the callback values. It is useful when controlling an element larger than the Dragdealer wrapper, in offering a smooth transition at the end.

Please note that this is not a complete solution for a custom scrollbar, nor does it try to be. You could make the content scrollable with a few lines of code but I for one think generic custom scrollbars are sent from hell.

"slide to unlock"

This is how this project started, somebody wanted an iPhone-like slider. Classic.

new Dragdealer('slide-to-unlock-old', {
  steps: 2,
  callback: function(x, y) {
    // Only 0 and 1 are the possible values because of "steps: 2"
    if (x) {
      this.disable();
      $('#slide-to-unlock-old').fadeOut();
    }
  }
});
slide to unlock

But the iPhone changed since then, iOS has a different start screen. Let's see how hard would it be to reproduce it.

new Dragdealer('slide-to-unlock-new', {
  x: 1,
  steps: 2,
  loose: true,
  callback: function(x, y) {
    // Only 0 and 1 are the possible values because of "steps: 2"
    if (!x) {
      this.disable();
      $('#slide-to-unlock-new').fadeOut();
    }
  }
});
slide to unlock

By no means do these demos try to be exact replicas, but merely to show the flexibility of Dragdealer.

The latter example has the handle (the dragging object) bigger than the wrapper, which in turn has overflow: hidden; to mask the bigger handle surface. You should check out the HTML and CSS to understand these examples better.

Image carousel

Let's kick it up a notch. How about a touch-ready image carousel... piece of cake. The entire string of images will be the draggable handle, masked by a wrapper the size of a single image (a slide.)

new Dragdealer('image-carousel', {
  steps: 4,
  speed: 0.3,
  loose: true,
  requestAnimationFrame: true
});

The speed option makes Dragdealer snappier, by speeding up the sliding animations. It takes up values between 0 and 1 and it defaults to 0.1.

Interactive canvas mask

With Dragdealer you can go from creating a simple slider to an entire website. I'm only saying this because I've seen more than a few examples of full-window implementations.

var canvasMask = new Dragdealer('canvas-mask', {
  x: 0,
  // Start in the bottom-left corner
  y: 1,
  vertical: true,
  speed: 0.2,
  loose: true,
  requestAnimationFrame: true
});

// Bind event on the wrapper element to prevent it when a drag has been made
// between mousedown and mouseup (by stopping propagation from handle)
$('#canvas-mask').on('click', '.menu a', function(e) {
  e.preventDefault();
  var anchor = $(e.currentTarget);
  canvasMask.setValue(anchor.data('x'), anchor.data('y'));
});

Dragdealer is open source,
see you on GitHub

The dragged surface can host rich content,
including links for scrolling inside itself

The masked content can be discovered through
both horizontal and vertical dragging

The surface boundaries have an elastic ease and
the corners can be slightly pulled inwards

The previous version of Dragdealer had a major drawback for this use-case: buttons and links from inside the handle (which is the entire content) would not clickable on touch devices anymore, once Dragdealer was initialized. That is fixed now.

Dragdealer is just a drag component, the rest is up to you.

© 2010+ @skidding — Best regards