javascript - 随机展开图像,在容器内不重叠

标签 javascript

我正在想办法解决这个问题。

假设我有 10 张图片,每张图片都是 95x95 - 然后我创建一个高度为 300px、宽度为 100% 的容器(相对位置)。

现在我想拍摄这 10 张图片(绝对位置)并使用 topleft 将它们随机放置在容器内,但我不想让它们相互重叠其他。

我创建图片标签

 for (let person of people) {
  let personBubble = document.createElement('a')

  personBubble.style.backgroundImage = "url(" + person.picture_url + ")";
  personBubble.style.display = 'block';
  personBubble.style.width = '100%';
  personBubble.style.height = '100%';
  personBubble.style.backgroundSize = '100% 100%';
  personBubble.style.position = 'absolute';
  personBubble.style.top = '0';
  personBubble.style.left = '0';
  personBubble.style.borderRadius = '50%';
  personBubble.style.zIndex = '10';
 }

现在基本上我想以这样的方式更改顶部和左侧属性,以便可以解决这个问题(而不是让所有 10 个属性都位于顶部 0 和左侧 0)

this is kinda the feel I'm going for

最佳答案

好吧,我要尝试在这里做一些奇特的事情。

想法:从靠近中心的单个图像开始,我们将其称为“准备就绪”。现在,对于剩余的每个图像,选择一个“准备好”的图像,并与其随机选择的 4 个边缘之一对齐。如果在此对齐之后,新对齐的图像与另一个先前定位的图像重叠,请重试此对齐机制。

工作示例:

let global = window;

global.params = { sep: 1, randOff: 0 };

let randInt = (min, max) => Math.floor(min + Math.random() * (max - min));

let getBound = (elem, isRoot) => {
  let { width, height } = elem.getBoundingClientRect();
  return {
    x: isRoot ? width >> 1 : (parseInt(elem.style.left) || 0),
    y: isRoot ? height >> 1 : (parseInt(elem.style.top) || 0),
    w: width,
    h: height,
    hw: width >> 1, // half-width
    hh: height >> 1 // half-height
  };
};
let boxCollides = (box, boxes, parBound, sep) => {
  
  let b1 = getBound(box);
  
  // Make sure `box` doesn't stick outside its `parBound`
  if (parBound) {
    if ((b1.x - b1.hw) < (parBound.x - parBound.hw)
      || (b1.x + b1.hw) > (parBound.x + parBound.hw)
      || (b1.y - b1.hh) < (parBound.y - parBound.hh)
      || (b1.y + b1.hh) > (parBound.y + parBound.hh))
      return true;
  }
  
  // Make sure `box` doesn't overlap any other box
  for (let box2 of boxes) {
    
    if (box === box2) continue;
    
    let b2 = getBound(box2);
    
    let sepX = Math.max(b1.x, b2.x) - Math.min(b1.x, b2.x);
    let sepY = Math.max(b1.y, b2.y) - Math.min(b1.y, b2.y);
    
    // If there isn't sufficient separation on either axis there's a collision
    if (sepX < (b1.hw + b2.hw + sep) && sepY < (b1.hh + b2.hh + sep)) return true;
    
  }
  
  return false;
  
};
let reposition = (boxes, { sep=1, randOff=0 }=global.params) => {
  
  // Awesome (and terrible) `shuffle` implementation
  boxes = [ ...boxes ].sort(function() { return Math.random() - 0.5; });
  
  let parBound = getBound(boxes[0].parentNode, true);
  
  // Consider the 1st box "ready"; position it in the center
  Object.assign(boxes[0].style, { left: `${parBound.x}px`, top: `${parBound.y}px` });
  
  // Start counting at 1 (since 1 box is initially "ready")
  for (let numReady = 1; numReady < boxes.length; numReady++) {
    
    let box = boxes[numReady];
    let b = getBound(box);
    
    // Use a counting loop to prevent too many attempts
    for (let attempts = 0; attempts < 500; attempts++) {
      
      // The bound of a random ready box
      let b2 = getBound( boxes[ randInt(0, numReady) ] );
      
      let side = randInt(0, 4);             // Randomly pick side to align to
      let off = randInt(-randOff, randOff); // Calculate random offset for `box`...
      let [ x, y ] = [ 0, 0 ];              // We'll calculate `x` and `y` next...
            
      // Align left
      if (side === 0) [ x, y ] = [ b2.x - (b2.hw + b.hw + sep), b2.y + off ];
      
      // Align right
      if (side === 1) [ x, y ] = [ b2.x + (b2.hw + b.hw + sep), b2.y + off ];
      
      // Align top
      if (side === 2) [ x, y ] = [ b2.x + off, b2.y - (b2.hh + b.hh + sep) ];
      
      // Align bottom
      if (side === 3) [ x, y ] = [ b2.x + off, b2.y + (b2.hh + b.hh + sep) ];
      
      Object.assign(box.style, { left: `${x}px`, top: `${y}px` });
      
      // Check if `box` now collides any of the ready boxes. If it doesn't,
      // we've successfully positioned it and `box` is ready!
      if (!boxCollides(box, boxes.slice(0, numReady), parBound, sep)) break;
      
    }
    
  }
  
};

window.addEventListener('load', () => {
  
  let boxesElem = document.querySelector('.boxes');
  for (let i = 0; i < 45; i++) {
    let boxElem = document.createElement('box');
    boxElem.classList.add('box', `dim${(i % 3)}`);
    boxesElem.appendChild(boxElem);
  }
  
  let boxes = [ ...boxesElem.childNodes ];
  
  reposition(boxes);
  setInterval(() => reposition(boxes), 2500);
  
});

for (let controlElem of document.querySelectorAll('.controls > .control')) {
  controlElem.addEventListener('click', evt => {
    
    evt.stopPropagation();
    evt.preventDefault();
        
    Object.assign(global.params, eval(`(${controlElem.textContent})`));
    
    document.getElementById('activeControl').setAttribute('id', '');
    controlElem.setAttribute('id', 'activeControl');
    
  });
}
.boxes {
  position: absolute;
  left: 0; top: 0;
  width: 100%; height: 100%;
  box-shadow: inset 0 0 0 2px #404040;
}
.boxes > .box {
  position: absolute;
  left: 50%; top: 50%;
  background-color: #ffffff;
  transition: left 400ms ease-in-out, top 400ms ease-in-out;
}
.boxes > .box.dim0 {
  width: 40px; height: 30px;
  margin-left: -20px; margin-top: -15px;
  background-color: #804000;
  box-shadow: inset 0 0 0 2px #c08000;
}
.boxes > .box.dim1 {
  width: 14px; height: 48px;
  margin-left: -7px; margin-top: -24px;
  background-color: #400080;
  box-shadow: inset 0 0 0 2px #a000f0;
}
.boxes > .box.dim2 {
  width: 22px; height: 22px;
  margin-left: -11px; margin-top: -11px;
  background-color: #600060;
  box-shadow: inset 0 0 0 2px #c000d0;
}
.controls {
  position: fixed;
  left: 0; top: 0;
  width: 130px; height: 100%;
  background-color: rgba(0, 0, 0, 0.3);
  overflow-y: auto;
}
.controls > .control {
  white-space: pre-wrap;
  font-family: monospace;
  font-size: 10px;
  cursor: pointer;
}
.controls > .control:hover,
.controls > .control#activeControl {
  background-color: rgba(0, 0, 0, 0.5);
  color: #ffffff;
}
<div class="boxes"></div>
<div class="controls">
  <div class="control" id="activeControl">
  {
    sep: 1,
    randOff: 0
  }
  </div>
  <div class="control">
  {
    sep: 10,
    randOff: 0
  }
  </div>
  <div class="control">
  {
    sep: 5,
    randOff: 10
  }
  </div>
  <div class="control">
  {
    sep: 0,
    randOff: 20
  }
  </div>
</div>

关于javascript - 随机展开图像,在容器内不重叠,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46676336/

相关文章:

javascript - 发出 HTTPs 请求时出现 "Error: getaddrinfo ENOTFOUND"错误

php - 在不使用 value 属性的情况下在输入字段上显示 var

javascript - 在其他范围内访问它

javascript - Firefox 和 Opera 中的父 div 宽度为 0,即使我有 child

javascript - window.URL.revokeObjectURL() 不会立即释放内存(或根本不释放)?

javascript - 是否有相当于 $scope.$apply 的东西?

javascript - 插入事务后如何保存一对多关系

javascript - 在 <% 标签内交织 EJS 和 Javascript 变量

javascript - 一次处理 1 个包含异步函数的循环

php 文档中的 Javascript/jquery ul/li 动画?