前言
如何实现在某一区域中多个元素随机显示且不重叠?
一、实现方案
- 随机数
根据生成的随机数设置div容器的top、left值; - 碰撞检测
检测两个元素直接是否存在重叠;
二、主要代码
- 设置div的 top、left
/***
* @name computed
* @description 计算每个盒子的left top
* @params divArr 随机布局的div数组
*/
function computed(divArr) {
maxDimensions = getMaxDimension(divArr);
var widthBoundary = maxDimensions.maxWidth;
var heightBoundary = maxDimensions.maxHeight;
for (var i = 0; i < divArr.length; i++) {
rDivLeft = getRDivNumber(
widthBoundary,
window.innerWidth - widthBoundary
);
rDivTop = getRDivNumber(
heightBoundary,
window.innerHeight - heightBoundary
);
divArr[i].style.left = rDivLeft + "px";
divArr[i].style.top = rDivTop + "px";
}
examineEach();
}
computed(allDiv);
- 碰撞检测
/***
* @name isCollision
* @description a,b为两个任意盒子,检测其是否碰撞
* @params a
* @params b
* @return Boolean
*/
function isCollision(a, b) {
var a_l = a.offsetLeft; // a_l为a盒子左侧偏移量
var a_t = a.offsetTop; // a_t为a盒子顶部偏移量
var a_r = a_l + a.offsetWidth; // a_r为a盒子右侧距页面左侧的距离
var a_b = a_t + a.offsetHeight; // a_b为a盒子底部距页面最顶端的距离
var b_l = b.offsetLeft; // b_l等为b盒子 同上解释
var b_t = b.offsetTop; // b_t为b盒子顶部偏移量
var b_r = b_l + b.offsetWidth; // b_r为b盒子右侧距页面左侧的距离
var b_b = b_t + b.offsetHeight; // b_b为b盒子底部距页面最顶端的距离
if (b_r < a_l || b_b < a_t || a_r < b_l || a_b < b_t) {
// 当满足此条件时,没有发生碰撞,此时返回值为false没有检测到碰撞
return false;
} else {
// 否则为true,即发生碰撞
return true;
}
}
/***
* @name examineEach
* @description 检查每一个盒子之间是否重叠
*/
function examineEach() {
maxFrequency += 1;
for (var i = 0; i < allDiv.length; i++) {
for (var j = i + 1; j < allDiv.length; j++) {
if (isCollision(allDiv[i], allDiv[j]) && maxFrequency < 9000) { // maxFrequency 避免无限循环
// 如果重叠 再次计算位置
computed([allDiv[i], allDiv[j]]);
}
}
}
}
三、完整代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>多个元素随机定位显示且不重叠</title>
</head>
<style>
.rDiv {
width: 60px;
height: 60px;
position: absolute;
text-align: center;
color: #fff;
font-size: 20px;
}
.rDiv1 {
background-color: pink;
width: 100px;
height: 150px;
}
.rDiv2 {
background-color: skyblue;
width: 80px;
height: 140px;
}
.rDiv3 {
background-color: goldenrod;
width: 70px;
height: 120px;
}
.rDiv4 {
background-color: sandybrown;
width: 120px;
height: 180px;
}
.rDiv5 {
background-color: silver;
width: 160px;
height: 200px;
}
.rDiv6 {
background-color: slategray;
width: 200px;
height: 280px;
}
.rDiv7 {
background-color: blanchedalmond;
width: 300px;
height: 200px;
}
.rDiv8 {
background-color: cyan;
width: 260px;
height: 180px;
}
</style>
<body>
<div class="rDiv rDiv1">Div1</div>
<div class="rDiv rDiv2">Div2</div>
<div class="rDiv rDiv3">Div3</div>
<div class="rDiv rDiv4">Div4</div>
<div class="rDiv rDiv5">Div5</div>
<div class="rDiv rDiv6">Div6</div>
<div class="rDiv rDiv7">Div7</div>
<div class="rDiv rDiv8">Div8</div>
<button onclick="javascript:window.changePosition()">点击随机位置</button>
</body>
<script>
var allDiv = document.getElementsByClassName("rDiv");
var maxFrequency = 0; // 计算的最大次数
function changePosition() {
/***
* @name getMaxDimension
* @description 返回div的最大宽度和最大高度
* @params divArr div数组
* @return Object
*/
function getMaxDimension(divArr) {
var maxWidth = 0;
for (var i = 0; i < divArr.length; i++) {
if (divArr[i].offsetWidth > maxWidth) {
maxWidth = divArr[i].offsetWidth;
}
}
var maxHeight = 0;
for (var i = 0; i < divArr.length; i++) {
if (divArr[i].offsetHeight > maxHeight) {
maxHeight = divArr[i].offsetHeight;
}
}
var values = { maxWidth: maxWidth, maxHeight: maxHeight };
return values;
}
/***
* @name getRDivNumber
* @description 返回一个任意的随机数
* @params min
* @params max
* @return Number
*/
function getRDivNumber(min, max) {
return Math.random() * (max - min) + min;
}
/***
* @name isCollision
* @description a,b为两个任意盒子,检测其是否碰撞
* @params a
* @params b
* @return Boolean
*/
function isCollision(a, b) {
var a_l = a.offsetLeft; // a_l为a盒子左侧偏移量
var a_t = a.offsetTop; // a_t为a盒子顶部偏移量
var a_r = a_l + a.offsetWidth; // a_r为a盒子右侧距页面左侧的距离
var a_b = a_t + a.offsetHeight; // a_b为a盒子底部距页面最顶端的距离
var b_l = b.offsetLeft; // b_l等为b盒子 同上解释
var b_t = b.offsetTop; // b_t为b盒子顶部偏移量
var b_r = b_l + b.offsetWidth; // b_r为b盒子右侧距页面左侧的距离
var b_b = b_t + b.offsetHeight; // b_b为b盒子底部距页面最顶端的距离
if (b_r < a_l || b_b < a_t || a_r < b_l || a_b < b_t) {
// 当满足此条件时,没有发生碰撞,此时返回值为false没有检测到碰撞
return false;
} else {
// 否则为true,即发生碰撞
return true;
}
}
/***
* @name computed
* @description 计算每个盒子的left top
* @params divArr 随机布局的div数组
*/
function computed(divArr) {
var maxDimensions = getMaxDimension(divArr);
var widthBoundary = maxDimensions.maxWidth;
var heightBoundary = maxDimensions.maxHeight;
for (var i = 0; i < divArr.length; i++) {
let rDivLeft = getRDivNumber(
widthBoundary,
window.innerWidth - widthBoundary
);
let rDivTop = getRDivNumber(
heightBoundary,
window.innerHeight - heightBoundary
);
divArr[i].style.left = rDivLeft + "px";
divArr[i].style.top = rDivTop + "px";
}
examineEach();
}
computed(allDiv);
/***
* @name examineEach
* @description 检查每一个盒子之间是否重叠
*/
function examineEach() {
maxFrequency += 1;
for (var i = 0; i < allDiv.length; i++) {
for (var j = i + 1; j < allDiv.length; j++) {
if (isCollision(allDiv[i], allDiv[j]) && maxFrequency < 9000) {
// maxFrequency 避免无限循环
// 如果重叠 再次计算位置
computed([allDiv[i], allDiv[j]]);
}
}
}
}
}
changePosition();
</script>
</html>
四、最终效果
五、总结
- 父级盒子
当前是以浏览器的窗口宽高作为父级盒子的尺寸,若是想自定义父级盒子尺寸
可将 computed 函数中的 window.innerWidth 和 window.innerHeight 修改成对应的和盒子的宽高
例如:
for (var i = 0; i < divArr.length; i++) {
let rDivLeft = getRDivNumber(
widthBoundary,
document.getElementById("box").offsetWidth - widthBoundary
);
let rDivTop = getRDivNumber(
heightBoundary,
document.getElementById("box").offsetHeight - heightBoundary
);
divArr[i].style.left = rDivLeft + "px";
divArr[i].style.top = rDivTop + "px";
}
- 存在问题
父级容器尺寸较小的时候可能会出现无限循环的现象,导致内存溢出
- 解决办法
这个问题暂时还没找到好的解决办法
暂定方法:添加 maxFrequency字段,根据个人需求,判断循环次数,避免内存溢出