Particle to slider

13 Dec 2014 · by David DeSandro

The initial demos dealt with a particle — an single point in space.

See the Pen Flickity initial demo 1: draggable dot by David DeSandro (@desandro) on CodePen.

But the aim of this product is gallery — something that has size.

See the Pen slider demo 1 by David DeSandro (@desandro) on CodePen.

This demo works similarly to the initial particle demo. Instead of positioning the particle, the cells are positioned by the slider.

// render cells
ctx.save();
// position cells by slider
ctx.translate( slider.x, 0 );
for ( var i=0, len = cells.length; i < len; i++ ) {
  var cell = cells[i];
  ctx.fillStyle = 'hsla(0, 100%, 50%, 0.5)';
  ctx.fillRect( cell.x, 20, cell.width, cell.height );
}
ctx.restore();

The last particle demo used attraction on the particle so it would land nicely at a target.

See the Pen Flickity initial demo 7: activate closest attractor by David DeSandro (@desandro) on CodePen.

Instead of a particle being attracted to targets, the position of the slider should be attracted to a particular cell. This demo aligns the center of the gallery to the center of a cell.

See the Pen slider demo 2: cell attraction by David DeSandro (@desandro) on CodePen.

function dragEnd() {
  // get closest cell to end position
  var minDistance = Infinity;
  var targetX, distance;
  for ( var i=0, len = cells.length; i < len; i++ ) {
    var cell = cells[i];
    targetX = cell.x + cell.width / 2;
    distance = Math.abs( -estimateX - targetX );
    if ( distance < minDistance ) {
      selectedIndex = i;
      minDistance = distance;
    }
  }
}

// attract the slider to that cell
function getAttraction() {
  var cell = cells[ selectedIndex ];
  var attractorX = cell.x + cell.width / 2;
  var distance = -attractorX - slider.x;
  var force = distance * 0.03;
  return force;
}

This works well enough when you flick by 2 cells. By you really got to tug it to get it to move to the next cell. Open up your phone's home screen. You can flick to the next screen by just moving it a couple pixels.

This demo fixes that. It adds additional logic so that if the selected cell hasn't changed after a flick, it checks if the user is moving to the next cell. Even if its just a little little bit, it will select the next cell.

See the Pen slider demo 3: cell attraction by David DeSandro (@desandro) on CodePen.

// if not enough velocity to escape current attractor
// boost it
if ( selectedIndex === prevIndex  ) {
  var selectedCell = cells[ selectedIndex ];
  targetX = selectedCell.x + selectedCell.width / 2;
  distance = -slider.x - targetX;
  if ( distance > 0 && slider.velocity < -1 && cells[ selectedIndex + 1 ] ) {
    // if moving towards the right, and negative velocity, and next attractor
    selectedIndex++;
  } else if ( distance < 0 && slider.velocity > 1 && cells[ selectedIndex - 1 ] ) {
    // if moving towards the left, and positive velocity, and previous attractor
    selectedIndex--;
  }
}

Niiiiiiiice.

This code works just as well for cells with varying widths.

See the Pen slider demo 4: varying widths by David DeSandro (@desandro) on CodePen.

... or with different alignment. This demo has the cursor and cell targets aligned on the left.

See the Pen slider demo 5: left aligned by David DeSandro (@desandro) on CodePen.


At this point, I feel I have a proper prototype for the gallery. It has dragging, alignment, sizing. It also works as a sandbox if I ever need to focus just on the behavior.

What's interesting is the discovering some of the counter-intuitive code that needed to be added in order for the interaction to feel 'natural.'

  1. Rubber-band physics to attract the cursor towards a target
  2. Selecting a target rather than letting all the forces take their effect
  3. Boosting subtle movement so flicking to the next cell feels effortless

Next up: it's time to make a library.