Left alignment with element collision detection

1

Alignment criteria:
 - Align left from element with lower value of 'left'.
 - Elements with collision must remain beside the collided element.

In the example the algorithm searches the leftmost element in reference to the other elements and stores its position, then searches to align all other elements from the stored data.

 arrayTopValue = [];
 arrayLeftValue = [];
 arrayLeftOriginal = [];
 arrayWidth = [];
 arrayHeight = [];
 arrayIdDivs = [];
 arrayAboutDivCollision = [];

$("#boxExample").children().each(function() {
//top
var position = $(this).css("top").indexOf("px");
var res = $(this).css("top").substr(0, position);
arrayTopValue.push(parseInt(res));


//left
var position = $(this).css("left").indexOf("px");
var res = $(this).css("left").substr(0, position);
arrayLeftValue.push(parseInt(res));
arrayLeftOriginal.push(parseInt(res));
arrayLeftValue.sort(function(a, b) {
  return a - b
});

//width
var position = $(this).css("width").indexOf("px");
var res = $(this).css("width").substr(0, position);
arrayWidth.push(parseInt(res));


//height
var position = $(this).css("height").indexOf("px");
var res = $(this).css("height").substr(0, position);
arrayHeight.push(parseInt(res));


//idDivs
var tempIds = $(this).attr("id");
arrayIdDivs.push(tempIds);

});

How does the algorithm know it will collide or not? To know this the algorithm checks if the element that will align left, is above the stored element:

          if (distTopPlusHeightRefNext < distTopRef) 
          {
            $("#" + arrayIdDivsFolllow[i]).css({'left': arrayLeftValue[0] + "px"
          });

If the element is above, it applies the stored left position to the element that will be left aligned.

The same thing happens if the element to be aligned is below the stored element.

if (distTopRefNext > distTopPlusHeightRef) 
{
    $("#" + arrayIdDivsFolllow[i]).css({'left': arrayLeftValue[0] + "px"
});

So when these two previous options are not possible to align, it means that the element will collide.

if (distTopPlusHeightRefNext > distTopRef && distTopRefNext < distTopPlusHeightRef) {
    var tempValue = marginLeftPlusWidthRef;
    tempValue = tempValue + 3; //Espaço entre os elementos em pixel
    $("#" + arrayIdDivsFolllow[i]).css({'left': tempValue + "px"
    });

Once the element collides, the reference values need to be updated so that the element can align from the nearest element. Ex: getValDivs ().

But there is an error because div1 is not colliding with div3, as can be seen in link as when two elements have the same position generates an alignment bug, could someone tell me what is wrong? Thankful

    
asked by anonymous 30.07.2016 / 01:31

1 answer

1

As I really had a hard time manipulating your code, I made another option, which, although I did not have time to test many possible positions, I think it's working right and what you asked for:

$(document).ready(function() {
  var leftList = []; // armazenará o left de todos os quadrados
  var minLeft; // receberá o menor left de todos os quadrados
  var topList = []; // armazenará o top de todos os quadrados
  var minTop; // armazenará o top de todos os quadrados
  var widthList = []; // armazenará o width de todos os quadrados
  var heightList = []; // armazenará o height de todos os quadrados
  var margin = 5; // espaçamentos entre os quadrados

  $('div').draggable(); // aplica o draggable a todos os quadrados, inclusive o grande

  function loadValues() { // carregará as variáveis supracitadas
    // loop que percorrerá todos o quadros
    $('#boxExample').children().each(function(i) { 
      var left = $(this).position().left; // captura o left do elemento em questão dentro do loop
      leftList[i] = left; // adiciona o left ao array
      var top = $(this).position().top; // captura o top 
      topList[i] = top; // adiciona o top ao array
      var height = $(this).height(); // captura o height 
      heightList[i] = height; // adiciona o height ao array
      var width = $(this).width(); // captura o width 
      widthList[i] = width; // adiciona o width ao array
    });
    minLeft = Math.min.apply(Math, leftList); // usa o Math.min em um array para retornar o menor left
    minTop = Math.min.apply(Math, topList); // retorna o menor top
  }

  function align() {
    loadValues(); // carrega a função passada
    // percorre novamente todos o quadros
    $('#boxExample').children().each(function(i) {
      loadValues(); // carrega os valores a cada mudança
      var thisLeft = leftList[i]; 
      // captura o left do elemento em questão usando o index do loop e aplicando ao array
      var thisTop = topList[i]; // faz o mesmo com o top
      var newLeft = minLeft; // deixa por padrão o left de cada elemento como sendo o menor
      var before = []; // array com as medidas dos quadros a esquerda do elemento em questão
      leftList.forEach(function(el, index) { // percorre o array de lefts
      // este primeiro if verifica a colisão somente em quadrados mais a esquerda que o em questão no primeiro loop
        if (el <= thisLeft && i != index) {
          // este verifica a colisão baseado no top
          if ((topList[index] >= thisTop && topList[index] <= thisTop + heightList[i]) || (topList[index] <= thisTop && topList[index] >= thisTop - heightList[index])) {
            // adiciona do before[] o left, o width e a margin, e subtrai o minLeft 
            before.push(el + widthList[index] - minLeft + margin);
          }
        }
      });
      // soma ao newLeft (que era por padrão o menor left) o maior valor do array before ou zero se ele for o elemento mais a esquerda
      newLeft += before.length == 0 ? 0 : Math.max.apply(Math, before);
      $(this).css({
        left: newLeft  // aplica o estilo
      })
    });
  }
  $('#btn2').click(function() {
    // este loop serve apenas para repetir a função várias vezes e o alinhamento funcione com eficácea
    $('#boxExample').children().each(function(i) {
      align();
    });
  });
})
#div1 {
  position: absolute;
  width: 30px;
  height: 30px;
  border: 1px solid black;
  left: 334px;
  top: 240px;
}
#div2 {
  position: absolute;
  width: 60px;
  height: 60px;
  border: 1px solid black;
  left: 198px;
  top: 41px;
}
#div3 {
  position: absolute;
  width: 80px;
  height: 80px;
  border: 1px solid black;
  left: 222px;
  top: 170px;
}
#div4 {
  position: absolute;
  width: 100px;
  height: 100px;
  border: 1px solid black;
  left: 86px;
  top: 106px;
}
#boxExample {
  position: relative;
  width: 460px;
  height: 360px;
  border: 1px solid black;
}
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script><scripttype="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/jquery-ui.min.js"></script><divid="boxExample">
  <div id="div1">1</div>
  <div id="div2">2</div>
  <div id="div3">3</div>
  <div id="div4">4</div>
</div>
<button id="btn2">goLeft</button>

Functions used:

31.07.2016 / 21:33