CSS interfering with element position while dragging

2

I have a script that allows you to click and drag an element across the screen and take you to the bottom 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 running of the script by moving the dragged element away from the mouse and disrupting touchend detection at the bottom of the page.

The div can not be removed. How do I compensate for the position of the element I'm 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

    
asked by anonymous 30.01.2015 / 21:22

1 answer

2

The moveToPos function uses the translate function of CSS to change the object of place. The translate functions as an array operation, and therefore relative to the initial position of the object.

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 10px right.

The parameters calculated for the moveToPos function did not take the initial position of the object under consideration, and calculated the new final coordinates of the object instead of how much it should move relative to its initial position.

In your case, the initial position of the element was y = 55px because of div . When he was dragged 1px down, he calculated that the final position was y = 56px . But calling translate(0, 56) will move element 56px 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 translate operation, so you can use them to compensate 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 this also there is no more need of touchPos . It can be overridden 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: '
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script><scriptsrc="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>
d7'; 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; }
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
    
31.01.2015 / 05:26