CSS interfering with element position while dragging

Asked

Viewed 742 times

2

I have a script that allows you to click and drag an element through the screen and take it to the footer of the page, which has an area that attracts the dragged element.

At the top of the page is a div with CSS:

width: 100%;
height: 55px;`.

And it completely interferes with the operation of the script, moving the dragged element away from the mouse and disturbing the detection of touchend at the bottom of the page.

The div cannot be removed. How do I compensate for the position of the element I am dragging?

HTML:

<div style="width: 100%; height: 55px;"></div>
<div data-drag="0" class="thing">
    <div class="circle"></div>
</div>
<div class="magnet-zone">
    <div class="magnet"></div>
</div>

JS:

var magnet = document.querySelector('.magnet-zone');

function isOverlapping(el1, el2) {
    var rect1 = el1.getBoundingClientRect(),
        rect2 = el2.getBoundingClientRect();
    return !(rect1.top > rect2.bottom || rect1.right < rect2.left || rect1.bottom < rect2.top || rect1.left > rect2.right);
}

function moveToPos(x, y, here) {
    here.style.transform = 'translate(' + Math.round(x, 10) + 'px, ' + Math.round(y, 10) + 'px) translateZ(0)';
    here.style.webkitTransform = 'translate(' + Math.round(x, 10) + 'px, ' + Math.round(y, 10) + 'px) translateZ(0)';
}

function moveMagnet(x, y) {
    var dist = 12,
        width = $('body').width() / 2,
        height = $('body').height(),
        direction = x > width ? 1 : -1,
        percX = x > width ? (x - width) / width : -(width - x) / width,
        percY = Math.min(1, (height - y) / (height / 2));
    magnet.style.marginLeft = Math.round(dist / 1.5 * percX) + 'px';
    magnet.style.marginBottom = Math.round(dist * percY) + 'px';
}

function move(event) {
    var el = this,
        magnetRect = magnet.getBoundingClientRect(),
        elRect = el.getBoundingClientRect();
    x = this._posOrigin.x + event.pageX - this._touchOrigin.x;
    y = this._posOrigin.y + event.pageY - this._touchOrigin.y;
    moveMagnet(x + elRect.width / 2, y + elRect.height / 2);
    $('body').addClass('moving');
    var touchPos = {
        top: y,
        right: x + elRect.width,
        bottom: y + elRect.height,
        left: x
    };
    overlapping = !(touchPos.top > magnetRect.bottom || touchPos.right < magnetRect.left || touchPos.bottom < magnetRect.top || touchPos.left > magnetRect.right);
    if (overlapping) {
        var mx = magnetRect.width / 2 + magnetRect.left;
        var my = magnetRect.height / 2 + magnetRect.top;
        x = mx - elRect.width / 2;
        y = my - elRect.height / 2;
        if (!$(el).hasClass('overlap')) {
            $(el).addClass('transition');
            setTimeout(function () {
                $(el).removeClass('transition');
            }, 150);

            setTimeout(function () {
                el.remove();
                setTimeout(function () {
                    $('body').removeClass('moving touching');
                }, 900);
            }, 1000);
        }
        magnet.className = magnet.className.replace(' overlap', '') + ' overlap';
        el.className = el.className.replace(' overlap', '') + ' overlap';
    } else {
        if ($(el).hasClass('transition')) {
            $(el).removeClass('transition');
        }
        if ($(el).hasClass('overlap')) {
            $(el).addClass('transition');
            setTimeout(function () {
                $(el).removeClass('transition');
            }, 100);
        }
        magnet.className = magnet.className.replace(' overlap', '');
        el.className = el.className.replace(' overlap', '');
    }
    if (Math.round(x, 10) > 0 && Math.round(x, 10) < ($(window).width() - 40)) {
        moveToPos(x, y, this);
    } else {
        if (x < ($('body').width() / 2)) {
            var width = 0;
        } else {
            var width = ($(window).width() - 60);
        }

        moveToPos(width, y, this);
    }


};
$('[data-drag]')
    .on('touchstart mousedown', onTouchStart)
    .on('touchmove drag', move);

function onTouchStart(event) {
    var rect = this.getBoundingClientRect();
    $('body').addClass('touching');
    $(this).removeClass('edge transition');
    this._touchOrigin = {
        x: event.pageX,
        y: event.pageY
    };
    this._posOrigin = {
        x: rect.left,
        y: rect.top
    };
}

Fiddle

1 answer

2

The function moveToPos uses the function translate CSS to change the place object. O translate functions as a matrix operation, and therefore relative to the object’s initial position.

That is, when you use transform: translate(10, 10), the object will not move to the position (10, 10) of the page. It will move 10px down and 10 px to the right.

The parameters calculated for the function moveToPos did not take the object’s initial position into consideration, and calculated the object’s new final coordinates rather than how much it should move relative to its initial position.

In his case, the initial position of the element was y = 55px because of the div. When he was dragged 1px down, he calculated that the final position was y = 56px. Only that call translate(0, 56) will move the 56px element down and not 1. That was the problem.

The properties offsetLeft and offsetTop give the position of the element and do not change with the operation of translate, then you can use them to compensate for the calculation of the coordinates:

x = this._posOrigin.x + event.pageX - this._touchOrigin.x - this.offsetLeft; // Aqui
y = this._posOrigin.y + event.pageY - this._touchOrigin.y - this.offsetTop;  // E aqui
...
overlapping = !(elRect.top > magnetRect.bottom ||  elRect.right < magnetRect.left ||
                elRect.bottom < magnetRect.top ||  elRect.left > magnetRect.right);
if (overlapping) {
    var mx = magnetRect.width / 2 + magnetRect.left;
    var my = magnetRect.height / 2 + magnetRect.top;
    x = (mx - elRect.width / 2) - this.offsetLeft; // Aqui também pra dar snap no lugar certo
    y = (my - elRect.height / 2) - this.offsetTop; // Idem

With that also there is no more need for touchPos. It can be replaced by the properties of the element itself.

var magnet = document.querySelector('.magnet-zone');

function isOverlapping(el1, el2) {
  var rect1 = el1.getBoundingClientRect(),
    rect2 = el2.getBoundingClientRect();
  return !(rect1.top > rect2.bottom || rect1.right < rect2.left || rect1.bottom < rect2.top || rect1.left > rect2.right);
}

function moveToPos(x, y, here) {
  here.style.transform = 'translate(' + Math.round(x, 10) + 'px, ' + Math.round(y, 10) + 'px) translateZ(0)';
  here.style.webkitTransform = 'translate(' + Math.round(x, 10) + 'px, ' + Math.round(y, 10) + 'px) translateZ(0)';
}

function moveMagnet(x, y) {
  var dist = 12,
    width = $('body').width() / 2,
    height = $('body').height(),
    direction = x > width ? 1 : -1,
    percX = x > width ? (x - width) / width : -(width - x) / width,
    percY = Math.min(1, (height - y) / (height / 2));
  magnet.style.marginLeft = Math.round(dist / 1.5 * percX) + 'px';
  magnet.style.marginBottom = Math.round(dist * percY) + 'px';
}

function move(event) {
  var el = this,
    magnetRect = magnet.getBoundingClientRect(),
    elRect = el.getBoundingClientRect();
  x = this._posOrigin.x + event.pageX - this._touchOrigin.x - this.offsetLeft;
  y = this._posOrigin.y + event.pageY - this._touchOrigin.y - this.offsetTop;
  moveMagnet(x + elRect.width / 2, y + elRect.height / 2);
  $('body').addClass('moving');
  overlapping = !(elRect.top > magnetRect.bottom || elRect.right < magnetRect.left || elRect.bottom < magnetRect.top || elRect.left > magnetRect.right);
  if (overlapping) {
    var mx = magnetRect.width / 2 + magnetRect.left;
    var my = magnetRect.height / 2 + magnetRect.top;
    x = mx - (elRect.width / 2) - this.offsetLeft;
    y = my - (elRect.height / 2) - this.offsetTop;
    if (!$(el).hasClass('overlap')) {
      $(el).addClass('transition');
      setTimeout(function() {
        $(el).removeClass('transition');
      }, 150);

      setTimeout(function() {
        el.remove();
        setTimeout(function() {
          $('body').removeClass('moving touching');
        }, 900);
      }, 1000);
    }
    magnet.className = magnet.className.replace(' overlap', '') + ' overlap';
    el.className = el.className.replace(' overlap', '') + ' overlap';
  } else {
    if ($(el).hasClass('transition')) {
      $(el).removeClass('transition');
    }
    if ($(el).hasClass('overlap')) {
      $(el).addClass('transition');
      setTimeout(function() {
        $(el).removeClass('transition');
      }, 100);
    }
    magnet.className = magnet.className.replace(' overlap', '');
    el.className = el.className.replace(' overlap', '');
  }
  if (Math.round(x, 10) > 0 && Math.round(x, 10) < ($(window).width() - 40)) {
    moveToPos(x, y, this);
  } else {
    if (x < ($('body').width() / 2)) {
      var width = 0;
    } else {
      var width = ($(window).width() - 60);
    }

    moveToPos(width, y, this);
  }


};
$('[data-drag]')
  .on('touchstart mousedown', onTouchStart)
  .on('touchmove drag', move);

function onTouchStart(event) {
  var rect = this.getBoundingClientRect();
  $('body').addClass('touching');
  $(this).removeClass('edge transition');
  this._touchOrigin = {
    x: event.pageX,
    y: event.pageY
  };
  this._posOrigin = {
    x: rect.left,
    y: rect.top
  };
}
.thing,
.thing .circle,
.magnet {
  border-radius: 50%;
  width: 60px;
  height: 60px;
}
.thing .circle,
.magnet-zone {
  -webkit-backface-visibility: hidden;
  backface-visibility: hidden;
  -webkit-perspective: 1000;
  perspective: 1000;
  -webkit-transform: translate3d(0, 0, 0);
  transform: translate3d(0, 0, 0);
}
.explain {
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  -webkit-transform: translateY(-50%);
  -ms-transform: translateY(-50%);
  transform: translateY(-50%);
}
html {
  height: 100%;
}
body {
  width: 100%;
  height: 100%;
  overflow: hidden;
  text-align: center;
  font-family: 'Open Sans', 'Lato', 'Helvetica Neue', Arial, sans-serif;
}
* {
  -webkit-tap-highlight-color: transparent;
  -webkit-tap-highlight-color: transparent;
  /* For some Androids */
}
.thing {
  position: absolute;
  -webkit-transform: translate(0, 0);
  -ms-transform: translate(0, 0);
  transform: translate(0, 0);
  margin: 0px;
  cursor: pointer;
}
.thing .circle {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: #888;
  background-image: url(http://gravatar.com/avatar/84eac3a27d1acf0ef0d835d92c999b0d?s=80);
  background-size: contain;
  background-repeat: no-repeat;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
  -webkit-transform: scale(1);
  -ms-transform: scale(1);
  transform: scale(1);
  -webkit-transition: -webkit-transform 50ms linear;
  transition: transform 50ms linear;
}
.thing.transition {
  -webkit-transition: -webkit-transform 150ms cubic-bezier(0.175, 0.885, 0.145, 1.25);
  transition: transform 150ms cubic-bezier(0.175, 0.885, 0.145, 1.25);
}
.thing.edge {
  -webkit-transition: all 400ms cubic-bezier(0.175, 0.885, 0.345, 1.1);
  transition: all 400ms cubic-bezier(0.175, 0.885, 0.345, 1.1);
}
.magnet-zone {
  pointer-events: none;
  -webkit-transition: -webkit-transform 300ms cubic-bezier(0.175, 0.885, 0.145, 1.32);
  transition: transform 300ms cubic-bezier(0.175, 0.885, 0.145, 1.32);
}
.magnet-zone {
  position: absolute;
  text-align: center;
  bottom: 10px;
  padding: 10px 20px;
  left: 50%;
  -webkit-transform: translate(-50%, 100%) translateZ(0);
  transform: translate(-50%, 100%) translateZ(0);
}
.magnet-zone.overlap .magnet {
  -webkit-transform: scale(1.1) translateZ(0);
  transform: scale(1.1) translateZ(0);
}
.touching .circle {
  -webkit-transform: scale(0.9) translateZ(0);
  transform: scale(0.9) translateZ(0);
}
.moving .magnet-zone {
  -webkit-transform: translate(-50%, 0) translateZ(0);
  transform: translate(-50%, 0) translateZ(0);
}
.magnet-zone:after {
  content: '\00d7';
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  -webkit-transform: translateY(-50%) translateZ(0);
  transform: translateY(-50%) translateZ(0);
  text-align: center;
  font-size: 2em;
  font-weight: 100;
  color: #fff;
}
.magnet {
  background: rgba(0, 0, 0, 0.3);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
  position: relative;
  -webkit-transform-origin: center;
  -ms-transform-origin: center;
  transform-origin: center;
  -webkit-transition: -webkit-transform 200ms cubic-bezier(0.175, 0.885, 0.145, 1.15);
  transition: transform 200ms cubic-bezier(0.175, 0.885, 0.145, 1.15);
  -webkit-transform: scale(0.8) translateZ(0);
  transform: scale(0.8) translateZ(0);
  border: 2px solid #fff;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<script src="http://threedubmedia.com/inc/js/jquery.event.drag-2.2.js"></script>
<div style="width: 100%;
height: 55px;"></div>


<div data-drag="0" class="thing">
  <div class="circle"></div>
</div>
<div class="magnet-zone">
  <div class="magnet"></div>
</div>

Browser other questions tagged

You are not signed in. Login or sign up in order to post.