allow native scroll in container during touchmove event like with drag and drop

Viewed 88

Is there a way to allow native scrolling easily without heavy JS modifications when you reach the border of a div via custom drag and drop via touchmove listener? When you drag the text in the div here you'll see the div inside is scrolling automatically
enter image description here

I provided an example with touchmove listeners but this one does not scroll, when you reach a border with your mouse
enter image description here
Is there an easy way to include a scrolling behavior to the 2nd example?

const element = document.body.querySelector('#draggable');

const isInContainer = (x,y) => {
    const elements = document.elementsFromPoint(x, y)
    return elements.find(el => el && el.classList && el.classList.contains('container')) || false;
}

const onMouseMove = (e) => {
    if(isInContainer(e.pageX, e.pageY)){
        element.style.top = e.pageY + 'px';
        element.style.left = e.pageX + 'px';
    }
}

const onMouseUp = () => {
    document.removeEventListener('mousemove', onMouseMove);
    document.removeEventListener('mouseup', onMouseUp)
}

element.addEventListener('mousedown', (e) => {
    e.preventDefault();
    document.addEventListener('mousemove', onMouseMove);
    document.addEventListener('mouseup', onMouseUp)
});
1 Answers

In case someone has a native better solution I'm willing to accept that one... for the time being this would be my current way to solve the issue.

Note: I made a custom interval for scrolling and don't use the mousemove event so users don't have to move the mouse to trigger it. moving outside will start the interval moving inside will clear it.

// the container that should scroll
const scrollBody = document.getElementById('scrollContainer');

// parameter to check which directions should scroll
const scrollPositions = {
  left: false,
  right: false,
  up: false,
  down: false,
}

// how far should be scrolled
const nextScrollDistance = {
  x: 0,
  y: 0
}

// scroll interval
let scrollInterval= null;
const startScrolling = (scrollBody) => {
  if (scrollInterval !== null) {
    return true;
  }

  const intervalCallback = () => {
    if (scrollInterval !== null && nextScrollDistance.x === 0 && nextScrollDistance.y === 0) {
      window.clearInterval(scrollInterval);
      scrollInterval = null;
    } else {
      scrollBody.scrollLeft += nextScrollDistance.x;
      scrollBody.scrollTop += nextScrollDistance.y;
    }
  }

  scrollInterval = window.setInterval(intervalCallback, 50);
}

const onMouseMove = (e) => {
  if(isInContainer(e.pageX, e.pageY)){
    element.style.top = e.pageY + 'px';
    element.style.left = e.pageX + 'px';
  }
  
  const rects = scrollBody.getBoundingClientRect();
  // check directions

  // max x that can be scrolled
  const maxX = scrollBody.scrollWidth - scrollBody.clientWidth;
  // max y that can be scrolled
  const maxY = scrollBody.scrollHeight - scrollBody.clientHeight;

  // check all directions if it's even possible to scroll
  const canScrollTop = Math.round(scrollBody.scrollTop) > 0;
  const canScrollBottom = Math.round(scrollBody.scrollTop) < maxY;
  const canScrollLeft = Math.round(scrollBody.scrollLeft) > 0;
  const canScrollRight = Math.round(scrollBody.scrollLeft) < maxX;

  // current x and y coordinates of the mouse
  const x = e.pageX;
  const y = e.pageY;

  // dynamic value to decrease the speed.. otherwise it might scroll too fast
  const minifier = 2;

  // the modifiers for scrollTop and scrollLeft
  nextScrollDistance.y = 0;
  nextScrollDistance.x = 0;

  if (canScrollBottom && y > rects.bottom) {
    // distance between the right border and the mouse
    const distance = Math.abs(y - rects.bottom);

    // the next time it scrolls -> scroll distance / minifier
    nextScrollDistance.y = Math.round(distance / minifier)
    scrollPositions.down = true;
  } else {
    scrollPositions.down = false;
  }

  // all other directions...
  if (canScrollTop && y < rects.top) {
    const distance = Math.abs(y - rects.top);
    nextScrollDistance.y = Math.round(distance / minifier) * -1;
    scrollPositions.up = true;
  } else {
    scrollPositions.up = false;
  }

  if (canScrollRight && x > rects.right) {
    const distance = Math.abs(x - rects.right);
    nextScrollDistance.x = Math.round(distance / minifier)
    scrollPositions.right = true;
  } else {
    scrollPositions.right = false;
  }

  if (canScrollLeft && x < rects.left) {
    const distance = Math.abs(x - rects.left);
    nextScrollDistance.x = Math.round(distance / minifier) * -1;
    scrollPositions.left = true;
  } else {
    scrollPositions.left = false;
  }

  // in case one of those are set.. trigger scrolling
  if (nextScrollDistance.x || nextScrollDistance.y) {
    startScrolling();
  }
  
}
Related