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.

Math time: Resting position

12 Dec 2014 · by David DeSandro

In the previous post, I presented a demo that calculates the resting position of a particle. This turned out to be an interesting exercise in problem solving.

Try flicking this dot around to see a green dot in place where the red dot will eventually rest.

See the Pen Flickity initial demo 6: get resting position by David DeSandro (@desandro) on CodePen.

Making and learning new things is an awkward process. Looking at this demo, it can appear like I know what I'm doing. But that's not how it came to be. Struggling to find a solution is part of the process.

Calculating resting position of a moving particle is a concept I first read about in Animate Your Way to Glory by Steven Wittens. The article is remarkable work about animation, physics and the math that underlies it all. The step-by-step animated graphs are great at aiding comprehension of these dense topics.

Steven mentions that calculating final position could be done with integration and calculus, concepts well outside my comfort zone. I knew the calculation could be done, but didn't know how it would work. This was the struggle.


Flicking is done by applying a velocity to the dot when you release it from a drag. Once flicked, the dot will keep moving, decelerating due to friction.

function dragEnd() {
  // set particle velocity
  particle.velocity = ( particle.x - previousX ) / ( currentTime - previousTime );
  particle.velocity *= 17;
}

Particle.prototype.update = function() {
  this.velocity += this.accel;
  this.velocity *= ( 1 - this.friction );
  this.x += this.velocity;
  // reset acceleration
  this.accel = 0;
};

I have all the variables I need to calculate its end position: acceleration, velocity, position. But I need the math to do it. This is where I stumble to remember rusty math skills from high school. I know velocity is decreasing due to friction. With each tick of the animation, the velocity is multiplied by the friction effect.

this.velocity *= ( 1 - this.friction );

I can calculate the velocity after 3 ticks of animation by multiplying it by the friction effect three times over.

veloAfter3 = this.velocity * ( 1 - this.friction ) * ( 1 - this.friction ) * ( 1 - this.friction )

This looks to be exponential.

log pow

// t is number of ticks of animation
endVelo = startVelo * ( 1 - friction ) ^ t

Eventually, velocity will reach 0, and the dot stops moving. I can't get its end position from this equation, but I can figure out how many ticks of animation happen. The number in question is t. This looks like algebra, which I can handle. It's an exponent, which leads me to remember logarithms. I forget how they work. Khan Academy refreshes my memory. eV is end velocity, sV is start velocity.

Velocity, logarithm equations

I've got the equation worked out. Now to bring it into JavaScript. Math.log is in base e, and this equation has a different base 1 - f. The MDN article even covers using Math.log with different bases.

var ticks = getBaseLog( 1 - this.friction, restingVelo / Math.abs( this.velocity ) );

function getBaseLog( a, b ) {
  return Math.log( b ) / Math.log( a );
}

I've got the number of ticks of animation for the particle to reach its resting position. With this, I can use the position equation, which is just x = x + velocity every frame. With each frame velocity decreases due to the friction factor velocity = velocity * ( 1 - friction).

// fF = 1 - friction = friction factor
// initial tick
v = v * fF;
x = x + v;
// tick 2
v = v * fF;
x = x + v;
// tick 3
v = v * fF;
x = x + v;
// for t ticks
...

// put it all in a single equation
restingX = x + (v * fF) + (v * fF^2) + (v * fF^3) + ... (v * fF^t)
// factor out v
restingX = x + v * (1 + fF^1 + fF^2 + fF^3 + ... fF^t)

I could solve this with a loop, but this feels like there's a smarty-pants math technique that can solve the ascending exponential portion. Turns out, there is!

restingX = x + v * (1 + fF^1 + fF^2 + fF^3 + ... fF^t)
restingX = x + v * ( (fF^t - 1) / (t - 1) )
var sum = ( Math.pow( fFriction, ticks + 1 ) - 1 ) / ( fFriction - 1 );
// resting position
return this.x + this.velocity  * sum;
// as implemented
Particle.prototype.getRestingPosition = function() {
  // get how many ticks until velocity is slow
  var restingVelo = 0.07; // ideally, this is 0, but that would take infinite amount of ticks
  var fFriction = 1 - this.friction;
  var ticks = getBaseLog( fFriction, restingVelo / Math.abs( this.velocity ) );
  // integrate to determine resting position
  var sum = ( Math.pow( fFriction, ticks + 1 ) - 1 ) / ( fFriction - 1 );
  // additional fFriction to account for initial tick
  return this.x + this.velocity * fFriction * sum;
}

function getBaseLog( a, b ) {
  return Math.log( b ) / Math.log( a );
}

Programming loop

I tackled this problem with algebra because it felt like the proper way to do it. But my initial inclination was to run a sort of simulation, using a programming loop — a more familiar concept. I was able to write this code in one shot.

// little simulation where thing will rest
var restingVelo = 0.07;
var velo = this.velocity;
var restX = this.x;
// keep iterating until simulation velocity is close to 0
while ( Math.abs( velo ) > restingVelo ) {
  velo *= 1 - this.friction;
  restX += velo;
}
return restX;

And wouldn't ya know it. This code works just as well.

See the Pen Flickity initial demo: get resting position, programming loop by David DeSandro (@desandro) on CodePen.

This code is much easier to read and understand. It even performs better on most browsers (Math.log and Math.pow can be expensive operations).


So after going through all the rigamarole with re-learning logarithms, and uncovering that exponential math bit, a better solution was in my grasp the entire time.

Afterward, I even tried digging deeper into Khan Academy's calculus videos in an effort to find a more elegant solution. No luck so far. If you know a better way to do this, please give me a holler. I can use your enlightenment.

Initial demos

8 Dec 2014 · by David DeSandro

I'm attempting to make a new product for Metafizzy. As part of its development, I'm going to be ambitious and try to blog the process.


Here's the idea:

A swipeable gallery widget that feels natural, like a native mobile swipeable view

There already are other great solutions out there, that are touch responsive and have nice animations. But when you swipe back and forth a bunch, they start to feel unnatural, with animations that don't quite feel right. I think there's an opportunity there. First step is to see if this is an idea worth pursuing by throwing together some initial demos.

These demos are (as Dave Wright would put it) guts on the table. Totally sloppy. No hang-ups on quality or style. It's the kind of work that happens at the beginning to get the creative process going.

First demo is a draggable particle dot with barebones physics. You can drag and release the dot, and it will keep moving with inertia, slowing down with friction.

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

Particle.prototype.update = function() {
  this.velocity += this.accel;
  this.velocity *= ( 1 - friction );
  this.x += this.velocity;
  this.accel = 0;
};

I'm trying to see how a gallery would move if it worked like a physics demo, with dragging and attraction forces. Demo #2 add two attractors that pull the dot in.

See the Pen Flickity initial demo 2: attractors by David DeSandro (@desandro) on CodePen.

// apply force of attractors to particle
if ( !isDragging ) {
  for ( var i=0, len = attractors.length; i < len; i++ ) {
    var attractor = attractors[i];
    var distance = attractor.x - particle.x;
    var force = Math.max( maxDistance - Math.abs( distance) , 0 );
    force /= maxDistance;
    force *= force;
    force *= distance > 0 ? 1 : -1;
    force *= 4;
    particle.applyForce( force );
  }
}

This was my first idea on how the force fields should work. As the dot gets closer to the attractor, the force of attraction increases. This is how gravity works. I modeled this demo after the basic gravity demo in The Nature of Code. This lets the dot ease-in to the force field. But it hits the attractor abruptly, then starts shaking. If you try to flick the dot from one attractor to another, it feels like it has to pass over a big hump.

Demo #3 reverses the force field. Instead of attraction being highest at the center of the attractor, it's higher further from the particle. This is like a rubber band. The further you pull a rubber band, the more force the band pulls back. It sounds counter-intuitive, but the result feels much nicer. The particle eases-in to the attractor, rather than accelerating towards it.

See the Pen Flickity initial demo 3: attractors by David DeSandro (@desandro) on CodePen.

var distance = attractor.x - particle.x;
var force = Math.abs( distance ) <= maxDistance ? distance : 0;
force *= 0.03;
particle.applyForce( force );

Those force multipliers like force *= 0.03 and force *= 4 are completely arbitrary. It's the result of me fiddling with the numbers to get better behavior.

This demo is an improvement over the previous, but it's wobbly and could use some fiddling. Demo #4 fiddles with friction and force.

var friction = 0.3;

var force = Math.abs( distance ) < maxDistance ?
  Math.abs( distance ) / maxDistance : 0;
force *= 8;
force = distance < 0 ? -force : force;

See the Pen Flickity initial demo 4: fiddling by David DeSandro (@desandro) on CodePen.

This is feeling good. I can flick the dot to the next attractor. I can also flick it over two attractors with a big flick. If the dot is released a little bit away, it nestles back into its origin. But it doesn't feel effortless. You need a big flick to move the dot from one attractor to another.

My next idea is to adjust friction so that the particle can escape its current attractor and easily get to its next one.

var force = Math.abs( distance ) < maxDistance ?
  Math.abs( distance ) / maxDistance : 0;
var friction = force ? 1 - force : 0;
friction *= 0.3;
force *= 8;
force = distance < 0 ? -force : force;
particle.friction = Math.max( particle.friction, friction );
particle.applyForce( force );

I don't understand my own code here, but I don't care either. I only care about the resulting behavior.

See the Pen Flickity initial demo 5: dynamic friction by David DeSandro (@desandro) on CodePen.

Neat idea but not much of an improvement. Next idea!

The problem is that the attractor where the flick starts will pull the dot back to before it gets a chance to escape. What if this origin attractor is disabled, and only the end attractor is active.

Demo #6 is just a drag/flick demo like the first, but this one predicts where the dot will end up after a flick.

See the Pen Flickity initial demo 6: get resting position by David DeSandro (@desandro) on CodePen.

(I'll follow up on the math used to calculate end position in a later post.)

Now I can use the calculated end-of-flick position to activate the closest attractor.

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

Hey, not bad! This demo feels pretty good. Not perfect, but good enough that I know the concept has merit.

The sliding behavior is the primary user experience of the gallery. I could have started this project working on the markup or API or animation engine. But by starting with these behavioral demos, I get to focus on what will make the product special.

npm & Browserify support added to Isotope, Packery, & Masonry

30 Nov 2014 · by David DeSandro

Isotope, Packery, Masonry, Draggabilly, and imagesLoaded all got big upgrades in the past weeks, adding npm and Browserify support. Now using these libraries with Browserify is as simple as a couple lines of code:

npm install isotope-layout
var Isotope = require('isotope-layout');

var iso = new Isotope( '#container', {
  // options...
});

Look over the specific docs for more details for each library.


Getting Browserify support meant adding CommonJS export to the libraries, and all to all their dependencies. I used the UMD spec, which has exports to support AMD, CommonJS, and browser globals. Here's Masonry's exports:

if ( typeof define === 'function' && define.amd ) {
  // AMD
  define( [
      'outlayer/outlayer',
      'get-size/get-size'
    ],
    masonryDefinition );
} else if ( typeof exports === 'object' ) {
  // CommonJS
  module.exports = masonryDefinition(
    require('outlayer'),
    require('get-size')
  );
} else {
  // browser global
  window.Masonry = masonryDefinition(
    window.Outlayer,
    window.getSize
  );
}

Adding CommonJS support is a simple task, but getting it done required updating all those dependency libraries. So much code wrangling.

They're all up on npm and ready for Browserifying. Have at it!

Isotope v2 released

31 Mar 2014 · by David DeSandro

  • Responsive web design
  • RequireJS
  • Bower, Component(1), Browserify
  • jsFiddle & CodePen

When Isotope v1 was released three years ago, these things either weren't on the map or didn't even exist. Front-end development has made incredible leaps since then. Isotope v1 was designed to be at the cutting edge of front-end technology, but it eventually felt like it was being left behind by its own environment. With this big version 2 upgrade, Isotope is back in front.

Isotope v2 big features include:

Read over the changes from Isotope v1 for a number of other improvements and upgrades.

Isotope documentation has been upgraded as well. All demos are displayed along side their explanations. Each demo can be edited on CodePen (that's 44 demos!), providing clear visibility to how Isotope works. Here's the filtering demo:

See the Pen Isotope - filtering by David DeSandro (@desandro) on CodePen.

Commercial licenses purchased for v1 can be used for v2. No need to purchase a new one.

My thanks go out to all you eager folks for trying out the v2 beta. Catching all those bugs makes me all the more confident that Isotope v2 will be a great experience.

Isotope v2 beta

31 Dec 2013 · by David DeSandro

Isotope v2 beta is out! Version 2 is a big upgrade:

The beta is released so you can get aquainted with the new version before I flip the switch. As a user, you probably won't have to change much to use v2. This is a beta, so feel free to submit new issues you come across.

Happy 2014!