Nowadays there are many Javascript libraries to make it easy the task of adding sliders to a website. Most have a host of features, to adapt the slider to the particular needs of each case. However, despite having so many functionalities, sometimes the sliders lack features that would result in a much better user experience.

In the case of sliders that implemented the drag and drop functionality, it is hard to see how almost none (not to be absolute) implements the ability to continue the movement that the user has started, adding momentum and deceleration as necessary.

In this tutorial we will introduce a new library, specifically to develop sliders with momentum in a simple way. Also we will create a fancy countdown timer, using this excellent design by Oleg Frolov as inspiration:

Original design by Oleg Frolov

The final result will be the following:

Fancy Countdown Timer

If you want to know how to implement it, just keep reading :)

Introducing MomentumSlider

Before starting to implement our countdown timer component, let's see what the new library MomentumSlider can do for us.

As its name suggests, the main functionality of this new library is to create sliders with momentum, although it also contains the basic functionalities of a common slider.

It has no dependencies, so to start using it we just have to include it in the HTML and create a new instance of MomentumSlider. Something like:

var mySlider = new MomentumSlider({
    el: '.container', // HTML element to append the slider
    range: [1, 5]     // Autogenerated number range
});

As you can figure out, the previous code is intended to generate a slider of numbers using the defined range, and include it within the .container element.

Of course, not all sliders are number ranges, so we can also provide custom elements for our slider, for which we must provide an HTML structure like the following:

<div class="container">
    <!-- Slider container -->
    <div class="ms-container ms-container--horizontal">
        <!-- Slider track -->
        <ul class="ms-track">
            <!-- Slider slides -->
            <li class="ms-slide">1</li>
            <li class="ms-slide">2</li>
            <li class="ms-slide">3</li>
            <li class="ms-slide">4</li>
            <li class="ms-slide">5</li>
        </ul>
    </div>
</div>

And then create a new instance of MomentumSlider in this way:

var mySlider = new MomentumSlider({
    el: '.ms-container' // HTML element to init a slider
});

The final result of this second example will be exactly the same as in the first, which we have done on purpose to illustrate how HTML is generated in the first case. The advantage of this second example is that within each .ms-slide element we can put anything :)

On the other hand, the general styles and dimensions of the slider must be specified in the CSS code. The library will work using those dimensions, which gives a lot of possibilities for customization.

So, after this brief introduction to our new library to develop sliders with momentum, we can begin to develop our countdown component.

HTML Structure

Let's start with the necessary HTML code:

<!-- Mobile container -->
<main class="container">
    <!-- Progress bar -->
    <div class="progress"></div>
    <!-- Countdown counter -->
    <div class="counter">1</div>
    <!-- Button to toggle countdown -->
    <button class="button">Toggle</button>
</main>

As we can see it is a piece of code quite simple. We have not even had to add the numbers for the slider manually, since they will be generated in the Javascript.

Adding Styles

Now we will add the general styles to match the original design:

body {
  overflow: hidden;

  // Decorative background
  &:before {
    content: '';
    position: absolute;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
    border-left: 50vw solid #FF6E6E;
    border-right: 50vw solid #FF6E6E;
    border-top: 50vh solid #354051;
    border-bottom: 50vh solid #354051;
    z-index: -1;
  }
}

// Mobile container
.container {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 340px;
  height: 540px;
  background-color: #354051;
  box-shadow: 0 0 100px rgba(0, 0, 0, 0.5);
  transform: translate(-50%, -50%);
  overflow: hidden;

  // Header bar, just an aesthetic detail
  &:before {
    content: '';
    position: absolute;
    left: 0;
    top: 0;
    width: 100%;
    height: 25px;
    background-color: rgba(0, 0, 0, 0.2);
    z-index: 3;
  }
}

Then we can add the styles needed for the slider. The most important styles here are those designed to position the slider elements, as well as some other styles to achieve the look and feel we want:

// Slider dimmensions
$ms-container-width: 340px;
$ms-slide-width: 125px;
$ms-slide-height: 540px;

// Slider container
.ms-container {
  position: relative;
  top: 50%;
  width: $ms-container-width;
  max-width: 100%;
  margin: 0 auto;
  overflow: hidden;
  transform: translateY(-50%);
}

// Slider track
.ms-track {
  position: relative;
  left: calc(50% - #{$ms-slide-width / 2}); // Centering
  white-space: nowrap;
  font-size: 0;
  list-style: none;
  padding: 0;
  margin: 0;
  will-change: transform;
}

// Slides
.ms-slide {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: $ms-slide-width;
  height: $ms-slide-height;
  font-size: 100px;
  font-family: 'Roboto Mono', monospace;
  color: #FFFFFF;
  user-select: none;
  will-change: transform;
}

To finish with our slider we need to add the gradient effect to smoothly hide the slides adjacent to the current slide. We get that with a pair of pseudo-elements from the .ms-container element. Let's see the code:

// Slider container
.ms-container {

  // Gradients to hide the adjacent numbers smoothly
  &:before, &:after {
    content: '';
    position: absolute;
    top: 0;
    width: 80px;
    height: 100%;
    z-index: 1;
    pointer-events: none;
  }
  &:before {
    left: 0;
    background-image: linear-gradient(to right, rgba(53, 64, 81, 1) 25%, rgba(53, 64, 81, 0));
  }
  &:after {
    right: 0;
    background-image: linear-gradient(to left, rgba(53, 64, 81, 1) 25%, rgba(53, 64, 81, 0));
  }
}

And we're done with the styles for the slider!

Adding Interactivity with Javascript

We already have our slider looking great, we just need to use our library MomentumSlider to add the necessary functionality. Let's see how we can do it in the following piece of code:

// Initializing the slider
var ms = new MomentumSlider({
    el: '.container', // HTML element to append the slider
    range: [1, 60],   // Generate the elements of the slider using the range of numbers defined
    loop: 2,          // Make the slider infinite, adding 2 extra elements at each end
    style: {
        // Styles to interpolate as we move the slider
        // The first value corresponds to the adjacent elements
        // The second value corresponds to the current element
        transform: [{scale: [0.4, 1]}],
        opacity: [0.3, 1]
    }
});

Surely you have noticed that the previous code is simple, except perhaps for the style option. That is why we are going to explain a bit more about what this option is for.

The style option allows you to interpolate CSS styles as the slider is moved. Each property to be interpolated receives an Array value, in which the first value corresponds to the previous adjacent slide, the second value corresponds to the current slide, and the third value corresponds to the subsequent adjacent slide. In the absence of a third value (as in our case), the first value will be used instead.

In this way, you can achieve very nice effects, like the one we have achieved in our countdown timer :)

Implementing the Countdown

So far we have seen how to create our slider from scratch. Now let's see how to add a simple countdown functionality.

In order to avoid making the tutorial too long, we are going to skip the necessary CSS code to achieve the final result we want to show. However, we think it is important to highlight how we have implemented the countdown timer functionality.

First we will listen the click event on the Toggle button, and we will start or stop the countdown accordingly:

// Simple toggle functionality
button.addEventListener('click', function () {
    if (running) {
        stop();
    } else {
        start();
    }
    running = !running;
});

Of course, for everything to work properly we must implement the start and stop functions respectively. Let's see the code, commented in detail for a better understanding:

// Start the countdown
function start() {
    // Disable the slider during countdown
    ms.disable();
    // Get current slide index, and set initial values
    seconds = ms.getCurrentIndex() + 1;
    counter.innerText = secondsInitial = seconds;
    root.style.setProperty('--progress', 0);
    // Add class to trigger CSS transitions for `running` state
    container.classList.add('container--running');
    // Set interval to update the component every second
    timer = setInterval(function () {
        // Update values
        counter.innerText = --seconds;
        root.style.setProperty('--progress', (secondsInitial - seconds) / secondsInitial * 100);
        // Stop countdown if it's finished
        if (!seconds) {
            stop();
            running = false;
        }
    }, 1000);
}

// Stop the countdown
function stop() {
    // Enable slider
    ms.enable();
    // Clear interval
    clearInterval(timer);
    // Reset progress
    root.style.setProperty('--progress', 100);
    // Remove `running` state
    container.classList.remove('container--running');
}

Finishing

And we have finished the essential parts of our fancy component! Check it out :)

Fancy Countdown Timer

In this tutorial we saw how to use the new MomentumSlider library to create amazing sliders. Please note that this library is still in development, so if you find an error or want to contribute, you can do so through the Github repository. Stay tunned!

And remember that in this tutorial we only explain the essential parts of our countdown timer. We invite you to check the full code on Github, or modify it and try new things in the Codepen demo.

We sincerely hope that you have enjoyed the tutorial and that it has been useful!