根据可用空间堆叠div

时间:2018-10-09 22:42:56

标签: javascript css math

我有一个div集合,这些div可以根据起始值和结束值通过JavaScript进行绝对定位和调整。

var posts = [
	{
  	start: 50,
    end: 200,
    text: "Lorem ipsum"
  },
	{
  	start: 280,
    end: 350,
    text: "dolor"
  },
	{
  	start: 140,
    end: 300,
    text: "sit amet"
  },
	{
  	start: 440,
    end: 590,
    text: "consectetur"
  },
	{
  	start: 460,
    end: 570,
    text: "adipiscing"
  },
	{
  	start: 480,
    end: 550,
    text: "elit"
  },
];

$.each(posts, function(i, post){
	var obj = $("<div />");
  
 	obj.css({
  	left: post.start,
    width: post.end - post.start
  })
  obj.text(post.text);
  
  obj.appendTo("#timeline");
})
body {
  font-family: Montserrat, Arial, sans-serif;
}

.timeline {
  margin: 20px 0;
  border-top: 1px solid #ccc;
  border-bottom: 1px solid #ccc;
  position: relative;
  height: 60px;
}

.timeline div {
  display: flex;
  justify-content: center;
  align-items: center;
  position: absolute;
  height: 50px;
  top: 5px;
  background: darkblue;
  color: #fff;
  border-radius: 6px;
  padding: 0 10px;
  box-shadow: 0 0 0 2px #fff;
}


h1 {
  font-size: 18px;
  text-transform: uppercase;
  color: #999;
  font-weight: 400;
  letter-spacing: 0.05em;
  margin: 50px 0 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h1>Current</h1>
<div id="timeline" class="timeline"></div>




<h1>Ideal</h1>
                      <!-- Height of the timeline adjusted to make room -->
<div class="timeline" style="height: 170px">
  <div style="left: 50px; width: 150px;">Lorem ipsum</div>
                                       <!-- This one not moved down because
                                       there is still room for it in the
                                       original position -->
  <div style="left: 280px; width: 70px;">dolor</div>
                                         <!-- Element moved down to
                                         since it overlaps two previous ones -->
  <div style="left: 140px; width: 160px; top: 60px;">sit amet</div>
  <div style="left: 440px; width: 150px;">consectetur</div>
                                         <!-- Element moved down -->
  <div style="left: 460px; width: 110px; top: 60px;">adipiscing</div>
                                        <!-- Element moved down even further -->
  <div style="left: 480px; width: 70px; top: 115px;">elit</div>
</div>

但是,如果两个或更多个div的起始值和结束值重叠,则生成的div也将重叠。理想情况下,我想计算哪些div会重叠,并添加一个top值,以便每个div都完全可见。 start值较大的元素应该是向下移动的元素。

如何确定哪些元素重叠,以便将这些元素向下移一两个凹口?

2 个答案:

答案 0 :(得分:1)

这是一种比较幼稚的方法,它只是循环浏览所有帖子,并对照其他帖子检查每个帖子。如果发现它们在相同范围内,则会增加最高值。仅在帖子具有相同的最高值时,才会进行比较。

请注意,此方法的运行时间为O(n ^ 2),虽然不是很大,但对于少量发布,应该没问题。

function detectOverlaps(posts) {
  for (let i=0; i<posts.length; i++) {
    for (let j=0; j<posts.length; j++) {
      if (i == j) continue;
      var post1 = posts[i];
      var post2 = posts[j];
      if (post1.top != post2.top) continue;
      if ((post1.start < post2.end && post1.start > post2.start) ||
          (post1.end > post2.start && post1.end < post2.end)) {
        var post = (post1.start>post2.start) ? post1 : post2;
        if (!post.top) post.top = 0;
        post.top += 60; // Change this by the pixel amount
      }
    }
  }
  return posts;
}

Here is the updated fiddle!

答案 1 :(得分:0)

跟踪每行实际插入的内容,然后可以运行一个简单的check-overlap函数来确定下一个div是否适合:

var posts = [
	{
  	start: 50,
    end: 200,
    text: "Lorem ipsum"
  },
	{
  	start: 280,
    end: 350,
    text: "dolor"
  },
	{
  	start: 140,
    end: 300,
    text: "sit amet"
  },
	{
  	start: 440,
    end: 590,
    text: "consectetur"
  },
	{
  	start: 460,
    end: 570,
    text: "adipiscing"
  },
	{
  	start: 480,
    end: 550,
    text: "elit"
  },
];

var lines = [];

$.each(posts, function(i, post){
	var obj = $("<div />");

  var lineNumber = addToLines(lines, post);
  
  if (lineNumber === lines.length) {
    lines.push([post]);
  }
  
  obj.css({
  	left: post.start,
    width: post.end - post.start,
    top: 3 + lineNumber * 54
  })
  obj.text(post.text);
  
  obj.appendTo("#timeline");
})

function addToLines(lines, post)
{
  for (var i = 0; i < lines.length; i++){
    if (fitsInLine(lines[i], post)) {
      lines[i].push(post);
      return i;
    }
  };
  
  return i;
}

function fitsInLine(line, postToAdd)
{
  for (var i = 0; i < line.length; i++){
    if (overlaps(postToAdd, line[i])) return false;
  }
  
  return true;
}

function overlaps(x, y)
{
  if (x.start < y.end && x.start > y.start) return true;
  
  if (x.end < y.end && x.end > y.start) return true;
  
  return false;
}
body {
  font-family: Montserrat, Arial, sans-serif;
}

.timeline {
  margin: 20px 0;
  border-top: 1px solid #ccc;
  border-bottom: 1px solid #ccc;
  position: relative;
  height: 180px;
}

.timeline div {
  display: flex;
  justify-content: center;
  align-items: center;
  position: absolute;
  height: 50px;
  top: 5px;
  background: darkblue;
  color: #fff;
  border-radius: 6px;
  padding: 0 10px;
  box-shadow: 0 0 0 2px #fff;
}


h1 {
  font-size: 18px;
  text-transform: uppercase;
  color: #999;
  font-weight: 400;
  letter-spacing: 0.05em;
  margin: 50px 0 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h1>Current</h1>
<div id="timeline" class="timeline"></div>



<h1>Ideal</h1>
                      <!-- Height of the timeline adjusted to make room -->
<div class="timeline" style="height: 170px">
  <div style="left: 50px; width: 150px;">Lorem ipsum</div>
                                       <!-- This one not moved down because
                                       there is still room for it in the
                                       original position -->
  <div style="left: 280px; width: 70px;">dolor</div>
                                         <!-- Element moved down to
                                         since it overlaps two previous ones -->
  <div style="left: 140px; width: 160px; top: 60px;">sit amet</div>
  <div style="left: 440px; width: 150px;">consectetur</div>
                                         <!-- Element moved down -->
  <div style="left: 460px; width: 110px; top: 60px;">adipiscing</div>
                                        <!-- Element moved down even further -->
  <div style="left: 480px; width: 70px; top: 115px;">elit</div>
</div>