Cards that can be Clicked and Tossed

Click and Toss in Plain Javascript

Conor T Murphy
4 min readNov 24, 2017

--

If any of you are familiar with Wes Bos’s Javascript 30 Challenge, then you should recognize the code from the click and drag challenge. What this article will cover is Ariya Hidayat’s explanation of the logic behind the click and toss feature, and how I incorporated into the aforementioned setup.

Here’s our starting point:

While this code allows us to scroll by clicking and dragging, it’s not the most user friendly experience. To incorporate the toss feature, we need to understand the math behind exponential decay.

Exponential decay is when a quantity decreases at a rate proportional to its size. This idea is represented by the following differential equation:

In this case, our initial quantity will be the velocity at the time the user releases up on the mouse. We calculate this velocity by determining the total distance the user has moved, while the mouse is in the down position, divided by the time. The time is equal to the timestamp generated when the user releases the mouse, subtracted by the timestamp the user creates when he or she clicks down. If the velocity is fast enough, then we use the value to calculate amplitude, which is multiplied by a constant and added to the target variable — the location the scroll will ultimately stop. We use requestAnimationFrame on a function that continues the scroll until a threshold is met.

The autoScroll function makes use of the differential equation noted above, with one difference — time’s relationship to decay. The formula will change to represent a mean lifetime, which indicates the expected time it takes our velocity to decay below a certain threshold.

As you can see, the time constant (T) has an inverse relationship to the decay rate, and represents the time it takes the velocity to reach 1/e its initial value. This value is hard-coded in the autoScroll function as 325, but can be changed to your liking. We will also need a time value equal to the difference between the timestamp generated when the function is invoked and the timestamp created when the mouse was released. The elapsed time will be divided by the time constant, and the result will be put inside a ‘Math.exp’ function. This value is multiplied by our initial velocity and set equal to the variable, delta. Delta will be added to our target variable. As the elapsed time gets smaller and smaller so will delta, and once the value is small enough the recursive process will end, and the scroll will have reached its target point. The final result should look something like this:

As you can see from my code, I have not developed this for mobile devices. To do so, I would use the same four event listeners, but for touch events. However, if you wanted to bypass that process, you can just add the following line to your CSS file:

-webkit-overflow-scrolling: touch;

Now that the toss feature is complete let’s talk about snap points.

Snap points will refocus the viewport to center a particular element. Our target variable — where the scroll ultimately stops — will be the key component to determine which element will be centered in the viewport. In order for this to work, we need to know where each element is in the DOM and how much space it takes up.

To get these dimensions, we query select all the cards and use the ‘offsetLeft’ method to determine the location of the left side of the element, then add the width of the element to the get the location of the right side. Since, I have margins around my cards, I used ‘getComputedStyle’ to get those numbers to add to my total width. Now, I know not only the the location of each card, but also how much space it takes up in the DOM.

When the target is ultimately determined, if it falls within the dimensions of a particular card, the slider will refocus. The code for this process looks like this:

if (target > wrap.left && target < wrap.right) {
slider.scrollLeft = wrap.left;
}

The click and toss with snap points feature should look like this:

That’s it for now, stay tuned for more.

--

--