Build a Simple Mobile Swipe Slider with Javascript. Version 2.

Sun Dec 30 2018

In this tutorial we will make another version of slider, we’ll improve the trigger mechanism to slider the slider. You can check the previous version. This demo is available on codepen.

The HTML

The html structure is quite similar from the previous version and you can put whatever content inside each slider, in here we put some picture for the home slide and the gallery slide, here is the code :

<div>
<!--    slider container in #slider -->
  <div id="slider">
<!--      slider header -->
    <div class="header">
      <div class="header__item">Home</div>
      <div class="header__item">Gallery</div>
    </div>
<!--      slilder content -->
    <div id="slider__wrapper">
      <div id="slider-0" class="slider__item home">
        <div class="list__card">
          <div class="list__card_image">
          </div>
          <div class="list__card_desc">
             <!--  -->
          </div>
        </div>
      </div>
      <div id="slider-1" class="slider__item gallery">
        <div class="gallery__card">
          <div class="gallery__card_image"></div>
        </div>
   				<!--  -->
        </div>
      </div>
    </div>
  </div>
</div>

The CSS

Now let’s add some color to the slider:

* {
    box-sizing: border-box;
  }
  body {
     height: 100vh;
    display: flex;
    align-items: center;
    justify-content: center;
    color:black;
    font-family: 'Poppins', sans-serif;

  }

/* slider style */

  #slider {
    width: 375px;
    height: 667px;
    background-color: white;
    position: relative;
    overflow:hidden;
    border-radius: 5px;
   box-shadow: 0px 16px 32px rgba(0,0,0,.5);
  }

  .transition {
    transition : transform .3s ease;
  }

  .header {
    display : flex;
    width: 100%;
    height: 48px;
    align-items:center;
    justify-content:space-around;
    font-weight: 700;
    color: white;
    background-color: #9675CE;
    font-size: 16px;
    height: 52px;
    position: relative;
  }

/* note that the "touch-action" property is really crucial for the gesture events. */

  #slider__wrapper {
    height: 100%;
    width: 100%;
    position: relative;
    display: flex;
    touch-action: none;
  }

  .slider__item {
    min-width: 100%;
    height: 100%;
    float: left;
    padding: 24px 12px;
    
  }

/* home slider styles */

.home {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  max-height: 170px;
}

.list__card {
  display: flex;
  flex-direction: column;
  width: 48%;
  margin-bottom: 16px;
}

.list__card_image{
  background-color: grey;
  width: 100%;
  height: 124px;
  border-radius: 5px;
   background-size:cover;
  position: center center;
}


.list__card:nth-child(1) .list__card_image {
  background-image: url('https://image.ibb.co/dc24FA/image.png');
}

.list__card:nth-child(2) .list__card_image {
  background-image: url('https://image.ibb.co/e9MT8V/image.png');
}

.list__card:nth-child(3) .list__card_image {
  background-image: url('https://image.ibb.co/ncoEhq/image.png');
}

.list__card:nth-child(4) .list__card_image {
  background-image: url('https://image.ibb.co/gCey8V/image.png');
}

.list__card:nth-child(5) .list__card_image {
  background-image: url('https://image.ibb.co/kOHh2q/image.png');
}

.list__card:nth-child(6) .list__card_image {
  background-image: url('https://image.ibb.co/iwD5Nq/image.png');
}

.list__card_desc {
  display:flex;
  flex-direction: column;
  margin-top: 8px;
  margin-left: 4px;
}

.list__card_desc_title {
  font-weight: 700;
  font-size: 14px;
}

.list__card_desc_date {
  font-weight: 600;
  color: #808093;
  font-size: 13px;
}

.list__card_desc_more {
  font-weight: 400;
}

/* home gallery styles */

.gallery {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  max-height: 170px;
}

.gallery__card {
  display: flex;
  flex-direction: column;
  width: 48%;
  margin-bottom: 16px;
}

.gallery__card_image{
  width: 100%;
  height: 144px;
  border-radius: 5px;
  background-size:cover;
  position: center center;
}

.gallery__card:nth-child(1) .gallery__card_image {
  background-image: url('https://image.ibb.co/d2enJV/image.png');
}
.gallery__card:nth-child(2) .gallery__card_image {
  background-image: url('https://image.ibb.co/hO4yQA/image.png');
}
.gallery__card:nth-child(3) .gallery__card_image {
  background-image: url('https://image.ibb.co/mmXJQA/image.png');
}

.gallery__card:nth-child(4) .gallery__card_image {
  background-image: url('https://image.ibb.co/dc24FA/image.png');
}

.gallery__card:nth-child(5) .gallery__card_image {
  background-image: url('https://image.ibb.co/k3sPyV/image.png');
}

.gallery__card:nth-child(6) .gallery__card_image {
  background-image: url('https://image.ibb.co/mfc2kA/image.png');
}

.gallery__card:nth-child(7) .gallery__card_image {
  background-image: url('https://image.ibb.co/fHPCJV/image.png');
}

.gallery__card:nth-child(8) .gallery__card_image {
  background-image: url('https://image.ibb.co/iwD5Nq/image.png');
}

The Javascript

First we need to define some variables for the slider:

const slider = document.querySelector('#slider')
const sliderWrapper = document.querySelector('#slider__wrapper');
const sliderLength = document.querySelectorAll('.slider__item').length
const sliderWidth = 375;
let index = 0;

Then we define the coordinate variable, these variable will contain the x and y axis when the event triggered:

let touchstartX = 0;
let touchstartY = 0;
let touchendX = 0;
let touchendY = 0;

We also add some variable to control, when the slider can be used.

let sliderProcess = 0
let touched = false;
let notAnimated = true;

Next we will add the event listener to capture the slide or mouse drag event

slider.addEventListener('touchstart', function(event) {
    event.preventDefault()
    touchstartX = event.changedTouches[0].screenX;
    touchstartY = event.changedTouches[0].screenY;
    touched = true;
},  false );

slider.addEventListener('touchmove', function(event) {
    if(touched) {
      touchendX = event.changedTouches[0].screenX;
      touchendY = event.changedTouches[0].screenY;
      swipe();
    }
},  false );

slider.addEventListener('touchout', function(event) {
  if(touched && notAnimated)  {
     snap((touchendX - touchstartX));
  }
},  false );

slider.addEventListener('touchend', function(event) {
    if(touched && notAnimated) {
     snap((touchendX - touchstartX));
  }
},  false ); 

// The listener to handle when a left click
// on mouse button occur, and get it's coordinate

slider.addEventListener('mousedown', function(event) {
  // event.button === 0 to check if the left button is registered
  if (event.button === 0) {
    event.preventDefault()
    // set the coordinate start point
    touchstartX = event.clientX;
    touchstartY = event.clientY;
    // set the touched to true
    touched = true
  }
},  false );

// The listener to handle when the event is hover above the slider element
slider.addEventListener('mousemove', function(event) {
  // only triggered if the slider is touched, 
  //not in animated and the event comming from left click
    if(touched && notAnimated && event.button === 0) {
      // set the coordinate end point, 
      touchendX = event.clientX;
      touchendY = event.clientY;
      // tigger the swipe function
      swipe();
    }
},  false );

// The listener to handle when the event is exit the slider element

slider.addEventListener('mouseout', function(event) {
  // only triggered if the slider is touched, 
  // not in animated and the event comming from left click
  if(touched && notAnimated && event.button === 0) {
    // tigger the snap function
    snap((touchendX - touchstartX));
  }
},  false );

// The listener to handle when the event is not touching with the slider element

document.addEventListener('mouseup', function(event) {
  if(touched && notAnimated && event.button === 0) {
     // tigger the snap function
    snap((touchendX - touchstartX));
  }
},  false );

Then we add snap function to handle whether to change the current index or not

const snap = (value) => {
  // check the value from the slide, 
  // to decide the slider to slide in which direction
  if (value < -60 && index < sliderLength - 1) {
      index += 1;
    } else if (value > 60 && index > 0) {
      index -= 1;
    }  
    // slideAndSnap function called and update the current pixels
    slideAndSnap()
    sliderProcess = sliderWidth * index * - 1 ;
}

Next we add the slide And Snap function to apply the animation and handle the slide when it can be used.

const slideAndSnap = () => {
  // slide the slider
  window.requestAnimationFrame(function () {
    sliderWrapper.style.transform = 
      'translate3d(-' + sliderWidth * index  + 'px, 0,0)';
  })
  // add the transition class and set the notAnimated to false
  sliderWrapper.classList.add('transition')
  notAnimated = false;
  
  // remove the transition class and set the touched and notAnimated to true
  setTimeout(() => {
    sliderWrapper.classList.remove('transition')
    notAnimated = true;
    touched = false
   }, 300)
}

Next we add the swipe function to handle when there is a drag motion.

const swipe = () => {
  let value = touchendX - touchstartX
  let holdValue = 0
  // check if it's the last of the first element to apply 'heavy slide' effect
  if ((
      index  === sliderLength - 1 && value < 0) || 
      (index === 0 && value > 0)) 
  {
    holdValue += (value - holdValue) / 6;
    sliderSlide(sliderProcess + holdValue)
    return ;
  } 
  sliderSlide(sliderProcess + value)
}

const sliderSlide = (value) => {
  window.requestAnimationFrame(function () {
      sliderWrapper.style.transform= 'translate3d(' + value + 'px, 0,0)';
    })
}