Search code examples
javascriptdraggablehorizontal-scrolling

Javascript horizontal drag transform position


I'm trying to create a horizontal slider which can be dragged left / right and it will update its transform translate position accordingly. I want to try and approach this in pure javascript, so no external libraries please.

What I have so far works on initial load and will drag to its position, but then it won't update again to continue.

var object = document.querySelector('.js-slider-container'),
initX, firstX;

object.addEventListener('mousedown', function(e) {

  e.preventDefault();
  initX = this.style.transform;
  firstX = e.pageX;

  this.addEventListener('mousemove', dragIt, false);

  console.log(initX);

  window.addEventListener('mouseup', function() {
    object.removeEventListener('mousemove', dragIt, false);
  }, false);

}, false);

object.addEventListener('touchstart', function(e) {

  e.preventDefault();
  initX = this.style.transform;

  var touch = e.touches;
  firstX = touch[0].pageX;

  this.addEventListener('touchmove', swipeIt, false);

  window.addEventListener('touchend', function(e) {
    e.preventDefault();
    object.removeEventListener('touchmove', swipeIt, false);
  }, false);

}, false);

function dragIt(e) {
  this.style.transform = `translate3d(${initX+e.pageX-firstX}px,0,0)`;
}

function swipeIt(e) {
  var contact = e.touches;

  this.style.transform = `translate3d(${initX+contact[0].pageX-firstX}px,0,0)`;
}

Here is a jsfiddle to show what I mean, it works on initial run - but then wont continue to update its position from that point. I also can't figure out how to cap it's left and right position to avoid over scroll.

Any advice is appreciated, cheers


Solution

  • Initially the value of this.style.transform will be an empty string "" and later when you drag it'll get updated to translate3d(-578px, 0px, 0px).

    You are using this string value directly in your calculations without picking only x value, thus it's failing.

    We can use a RegEx to pick x value only like below then use it in calculations.

     const txMatch = this.style.transform.match(/translate3d\((-?\d+)px,.+\)/);
     initX = txMatch ? +(txMatch[1] || 0) : 0;
    

    var object = document.querySelector('.js-slider-container'),
      initX, firstX;
    
    object.addEventListener('mousedown', function(e) {
      e.preventDefault();
      const txMatch = this.style.transform.match(/translate3d\((-?\d+)px,.+\)/);
      initX = txMatch ? +(txMatch[1] || 0) : 0;
      firstX = e.pageX;
      this.addEventListener('mousemove', dragIt, false);
      window.addEventListener('mouseup', function() {
        object.removeEventListener('mousemove', dragIt, false);
      }, false);
    }, false);
    
    object.addEventListener('touchstart', function(e) {
      e.preventDefault();
      const txMatch = this.style.transform.match(/translate3d\((-?\d+)px,.+\)/);
      initX = txMatch ? +(txMatch[1] || 0) : 0;
      var touch = e.touches;
      firstX = touch[0].pageX;
    
      this.addEventListener('touchmove', swipeIt, false);
    
      window.addEventListener('touchend', function(e) {
        object.removeEventListener('touchmove', swipeIt, false);
      }, false);
    
    }, false);
    
    function dragIt(e) {
      const maxMove = this.offsetWidth - this.parentElement.offsetWidth;
      const x = initX + e.pageX - firstX;
      if (x >= 0 || x <= -maxMove) {
       return;
      }
      this.style.transform = `translate3d(${x}px,0,0)`;
    }
    
    function swipeIt(e) {
      var contact = e.touches;
      this.style.transform = `translate3d(${initX+contact[0].pageX-firstX}px,0,0)`;
    }
    *,
    *:before,
    *:after {
      -webkit-box-sizing: inherit;
      box-sizing: inherit;
    }
    
    .container_full-vh {
      position: relative;
      height: 100vh;
    }
    
    section {
      padding: 400px 0;
    }
    
    
    /* Slider */
    
    .slider {
      position: absolute;
      top: 0;
      left: 0;
      bottom: 0;
      z-index: 9;
      will-change: transform;
      display: flex;
      align-items: center;
      width: 100%;
      height: 100vh;
      overflow-x: hidden;
    }
    
    .slider-container {
      position: relative;
      left: 10px;
      display: grid;
      align-items: center;
      grid-template-columns: repeat(7, 33.3333vw);
      grid-column-gap: 4.167vw;
      padding: 0 8.33333vw;
      height: unset;
      cursor: grab;
    }
    
    .slider-item {
      display: flex;
      align-items: center;
      justify-content: center;
      pointer-events: none;
      will-change: transform;
      position: relative;
      overflow: hidden;
      opacity: 1;
      visibility: inherit;
    }
    
    .slider-item_img-wrap {
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      object-fit: cover;
      overflow: hidden;
      opacity: 1;
      visibility: inherit;
    }
    
    .slider-item:before,
    .slider-item_img-wrap:before {
      content: "";
      display: block;
      padding-bottom: calc(100%/0.8);
    }
    
    .slider-item_img {
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      object-fit: cover;
      max-width: unset;
      pointer-events: none;
      user-select: none;
      transform-origin: left center;
      transform: scale(1.75);
      will-change: transform;
    }
    
    .slider-item_content {
      display: flex;
      flex-direction: column;
      justify-content: center;
      position: relative;
      z-index: 1;
      user-select: none;
    }
    
    .slider-item_content-heading {
      display: flex;
      overflow: hidden;
    }
    
    .slider-item_content-heading h3 {
      pointer-events: none;
      font-size: 7.569vw;
      line-height: 6.944vw;
      color: white;
      text-transform: uppercase;
      transform-origin: left bottom;
      will-change: transform;
    }
    <div class="container_full-vh">
    
      <section>
        <div class="js-slider slider">
          <div class="js-slider-container slider-container">
    
            <div class="slider-item">
              <div class="slider-item_img-wrap">
                <img src="https://images.unsplash.com/photo-1479839672679-a46483c0e7c8?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=800&q=80&ar=0.8" class="slider-item_img">
              </div>
              <div class="slider-item_content">
                <div class="slider-item_content-heading">
                  <h3>Indigo</h3>
                </div>
              </div>
            </div>
    
            <div class="slider-item">
              <div class="slider-item_img-wrap">
                <img src="https://images.unsplash.com/photo-1566688342604-dbe3e7357104?ixlib=rb-1.2.1&amp;auto=format&amp;fit=crop&amp;w=800&amp;q=80&amp;ar=0.8" class="slider-item_img">
              </div>
              <div class="slider-item_content">
                <div class="slider-item_content-heading">
                  <h3>Rouge</h3>
                </div>
              </div>
            </div>
    
            <div class="slider-item">
              <div class="slider-item_img-wrap">
                <img src="https://images.unsplash.com/photo-1472835560847-37d024ebacdc?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=800&q=80&ar=0.8" class="slider-item_img">
              </div>
              <div class="slider-item_content">
                <div class="slider-item_content-heading">
                  <h3>Juane</h3>
                </div>
              </div>
            </div>
    
            <div class="slider-item">
              <div class="slider-item_img-wrap">
                <img src="https://images.unsplash.com/photo-1541320823636-40247af897bf?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=800&q=80&ar=0.8" class="slider-item_img">
              </div>
              <div class="slider-item_content">
                <div class="slider-item_content-heading">
                  <h3>Orange</h3>
                </div>
              </div>
            </div>
    
            <div class="slider-item">
              <div class="slider-item_img-wrap">
                <img src="https://images.unsplash.com/photo-1566787020216-3e4f973ec5ec?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=800&q=80&ar=0.8" class="slider-item_img">
              </div>
              <div class="slider-item_content">
                <div class="slider-item_content-heading">
                  <h3>Vert</h3>
                </div>
              </div>
            </div>
    
          </div>
        </div>
      </section>
    
    </div>