Odo Draggable

A class for versatile, performant draggability.

Support

IE9+.

Dependencies

Odo Pointer, Odo Device, Odo Helpers, Tiny Emitter.

Odo Pointer also expects Object.assign from ES6 to be available.

X Axis

Setup

var x = new OdoDraggable(document.getElementById('draggable-x'), {
  axis: 'x'
});

Position

Set the position of the draggable element. The parameters are percentages, so 50 equates to 50%.

// Set to 50%
x.setPosition(50, 50);

// Set to 0
x.setPosition(0, 0);

The position of the draggable element can be retrieved with getPosition. Optionally specify the first parameter as true to get the percentage coordinates instead of pixels.

x.setPosition(50, 50);
// Get pixels
x.getPosition(); // Coordinate {x: 480, y: 0}
// Get percentages
x.getPosition(true);// Coordinate {x: 50, y: 0}

Limits

You can also sets limits on the draggable instance, which restricts it to given boundaries. The only parameter is a Rect. Rects have a left, top, width, and height and can be accessed from the math property of OdoHelpers.

In this example, the draggable stops at the edges of the .container element. Since the draggable is centered within the container and the draggable starts at 0, 0, the left limit needs to be negative half the leftover space. The minimum x position is the left value. The maximum x is the left + width. The width in this case, is the leftover space (20% because draggable is 80%). The y axis works the same way, but in this example, the top and height parameters don't matter because this draggable is restricted to the x axis.

var math = OdoHelpers.math;
var draggableWidth = x.element.offsetWidth;
var containerWidth = container.offsetWidth;

var left = (containerWidth - draggableWidth) / -2;
var top = 0;
var width = containerWidth - draggableWidth;
var height = 0;
var rect = new OdoHelpers.Rect(left, top, width, height);
x.setLimits(rect);

Y Axis

Heads up! Any draggable element on the Y axis requires its parent to have a height to work with the fallback left and top positioning.

Setup

var y = new OdoDraggable(document.getElementById('draggable-y'), {
  axis: 'y'
});

Disabling

The draggable instance can be disabled (and re-enabled). The carousel, for example, disables dragging while its animating.

// Disable draggable
y.isEnabled = false;

// Enable draggable.
y.isEnabled = true;

Both Axis

Just like the Y axis note above, the element which contains the draggable element must have a height, otherwise the top percentage value is not relative to anything and is calculated as zero.

Setup

var xy = new OdoDraggable(document.getElementById('draggable-xy'), {
  axis: 'xy'
});

Friction

You can make a draggable instance act like it is resisting the user's touch by setting a friction value. A value of 1 represents no friction; 100% of the user's movements are translated into movement of the draggable instance. Use a value less than 1, like 0.4, and the draggable element does not move completely in sync with your pointing device.

// Give it friction, like the edge slide of a carousel.
xy.friction = 0.4;

// Remove friction.
xy.friction = 1;

Throwable

Give it a toss.

By default, draggable instances are not throwable. Use the isThrowable option to enable it. You must also set a boundary for the throwable element, otherwise you can throw it off the screen!

// Create new draggable
var throwable = new OdoDraggable(document.getElementById('draggable-throw-me'), {
  axis: 'xy',
  isThrowable: true
});

var element = throwable.element;
var parent = element.parentNode;
var draggableWidth = element.offsetWidth;
var draggableHeight = element.offsetHeight;
var containerWidth = parent.offsetWidth;
var containerHeight = parent.offsetHeight;

var left = 0;
var top = 0;
var width = containerWidth - draggableWidth;
var height = containerHeight - draggableHeight;
var rect = new OdoHelpers.Rect(left, top, width, height);

// Set a boundary for the draggable so that it won't be thrown outside of its parent.
throwable.setLimits(rect);

More like a carousel

This example uses the same markup and css as the Odo Carousel, except the .carousel-element's width is set to fit the exact amount of slides (width is 500% instead of 2000%).




Here we follow the same steps. Set the isThrowable option and then set limits.

var freescroll = new OdoDraggable(document.getElementById('draggable-carousel'), {
  axis: 'x',
  isThrowable: true
});

var element = freescroll.element;
var parent = element.parentNode;
var draggableWidth = element.offsetWidth;
var containerWidth = parent.offsetWidth;

var left = -draggableWidth + containerWidth;
var top = 0;
var width = -left;
var height = 0;
var rect = new OdoHelpers.Rect(left, top, width, height);

freescroll.setLimits(rect);

Events

Draggable emits three events: START, MOVE, and END.

instance.on(OdoDraggable.EventType.START, function(pointerEvent) {
  console.log('drag started', pointerEvent);
});

instance.on(OdoDraggable.EventType.MOVE, function (pointerEvent) {
  console.log('drag move:', pointerEvent);
});

instance.on(OdoDraggable.EventType.END, function (pointerEvent) {
  console.log('drag ended:', pointerEvent);
});

PointerEvent

Here is the data contained within a drag end event from the first draggable on this page. There are a few simple properties here that have already determined the drag movement compared to its axis.

{
  axisDirection: "left",
  currentVelocity: {
    x: 1.88,
    y: 0.14
  }
  delta: {
    x: -32,
    y: 0
  },
  deltaTime: 203,
  didMoveOnAxis: true,
  direction: "left", // OdoPointer.Direction.LEFT
  distance: 32,
  end: {
    x: -32,
    y: 0
  },
  isDirectionOnAxis: true,
  position: {
    percent: {
      x: -3.2160804020100504,
      y: 0
    },
    pixel: {
      x: -32,
      y: 0
    },
  },
  start: {
    x: 0,
    y: 0
  },
  target: div,
  type: "ododraggable:end", // OdoDraggable.EventType.END
  velocity: {
    x: 0.15763546798029557,
    y: 0
  }
}

Disposal

To properly destroy a draggable instance, you must call the dispose method. This removes event listeners, nullifies DOM references, and performs other clean up.

xy.dispose();

Options

OdoDraggable.Defaults = {
  // Draggable axis.
  axis: OdoPointer.Axis.X,

  // Amplifies throw velocity by this value. Higher values make the throwable
  // travel farther and faster.
  amplifier: 24,

  // Once the velocity has gone below this threshold, throwing stops.
  velocityStop: 0.08,

  // On each throw frame, the velocity is multiplied by this friction value.
  // It must be less than 1. Higher values let the throwable slide farther and longer.
  throwFriction: 0.94,

  // Whether the draggable will keep its movement momentum after the user releases.
  isThrowable: false
}