好的,我已经想出了一个解决方案。我不喜欢基于暴力方法的想法,因为我认为这种方法效率不高。但我意识到,如果你可以查看哪些现有的小部件挡住了放置小部件的位置,那么你可以跳过网格的大部分。
以下是一个示例:(在此示例中,要放置的小部件为20x20,页面宽度为100px。)
This diagram is 0.1 scale and got messed up so I've had to add an extra column
*123456789A*
1+---+ +--+1
2| | | |2
3| | +--+3
4| | 4
5+---+ 5
*123456789A*
- 我们试图将一个小部件放置在0x0处,但由于该坐标处有一个50x50的小部件,所以无法放置。
- 因此,我们将当前正在扫描的x坐标推进到51并再次检查。
- 然后,我们发现在0x61处有一个40x30的小部件。
- 因此,我们将x坐标推进到90,但这不足以留出放置小部件的空间,因此我们将y坐标增加并将x重置为0。
- 我们知道前一行上的小部件至少有30px高,因此我们将y坐标增加到31。
- 我们在0x31遇到相同的50x50小部件。
- 因此,我们将x增加到51,并发现可以在51x31处放置小部件。
以下是JavaScript代码:
function findSpace(width, height) {
var $ul = $('.snap-layout>ul');
var widthOfContainer = $ul.width();
var heightOfContainer = $ul.height();
var $lis = $ul.children('.setup-widget');
for (var y = 0; y < heightOfContainer - height + 1; y++) {
var heightOfShortestInRow = 1;
for (var x = 0; x < widthOfContainer - width + 1; x++) {
console.log(x + '/' + y);
var pos = { 'left': x, 'top': y };
var $collider = $(isOverlapping($lis, pos, width, height));
if ($collider.length == 0) {
return pos;
}
var colliderPos = $collider.position();
var newX = colliderPos.left + $collider.width() - 1;
x = newX > x ? newX : x;
var colliderBottom = colliderPos.top + $collider.height();
if (heightOfShortestInRow == 1 || colliderBottom - y < heightOfShortestInRow) {
heightOfShortestInRow = colliderBottom - y;
}
}
y += heightOfShortestInRow - 1;
}
}
这里是更长、不太优雅的版本,它还调整了容器的高度(我现在只是粗略地拼凑起来,稍后会进行清理和编辑)
function findSpace(width, height,
yStart, avoidIds
) {
var $ul = $('.snap-layout>ul');
var widthOfContainer = $ul.width();
var heightOfContainer = $ul.height();
var $lis = $ul.children('.setup-widget');
var bottomOfShortestInRow;
var idOfShortestInRow;
for (var y = yStart ? yStart : 0; y <= heightOfContainer - height + 1; y++) {
var heightOfShortestInRow = 1;
for (var x = 0; x <= widthOfContainer - width + 1; x++) {
console.log(x + '/' + y);
var pos = { 'left': x, 'top': y };
var $collider = $(isOverlapping($lis, pos, width, height));
if ($collider.length == 0) {
return pos;
}
var colliderPos = $collider.position();
var newX = colliderPos.left + $collider.width() - 1;
x = newX > x ? newX : x;
colliderBottom = colliderPos.top + $collider.height();
if (heightOfShortestInRow == 1 || colliderBottom - y < heightOfShortestInRow) {
heightOfShortestInRow = colliderBottom - y;
var widgetId = $collider.attr('data-widget-id');
if (!avoidIds || !$.inArray(widgetId, avoidIds)) {
bottomOfShortestInRow = colliderBottom;
idOfShortestInRow = widgetId;
}
}
}
y += heightOfShortestInRow - 1;
}
if (!yStart) {
var idsToAvoid = [];
for (var attempts = 0; attempts < widthOfContainer; attempts++) {
idsToAvoid.push(idOfShortestInRow);
heightOfContainer = $ul.height();
var maxAvailableRoom = heightOfContainer - bottomOfShortestInRow;
var extraHeightRequired = height - maxAvailableRoom;
if (extraHeightRequired < 0) { extraHeightRequired = 0;}
$ul.height(heightOfContainer + extraHeightRequired);
var result = findSpace(width, height, bottomOfShortestInRow, idsToAvoid);
if (result.top) {
return result;
}
bottomOfShortestInRow = result.bottom;
idOfShortestInRow = result.id;
if (!bottomOfShortestInRow) {
break;
}
}
debugger;
$ul.height($ul.height() + height);
return { 'left': 0, 'top': $ul.height() - height };
} else {
return { 'bottom': bottomOfShortestInRow, 'id': idOfShortestInRow };
}
}
function isOverlapping($obsticles, tAxis, width, height) {
var t_x, t_y;
if (typeof (width) == 'undefined') {
var $target = $(tAxis);
tAxis = $target.position();
t_x = [tAxis.left, tAxis.left + $target.outerWidth()];
t_y = [tAxis.top, tAxis.top + $target.outerHeight()];
} else {
t_x = [tAxis.left, tAxis.left + width];
t_y = [tAxis.top, tAxis.top + height];
}
var overlap = false;
$obsticles.each(function () {
var $this = $(this);
var thisPos = $this.position();
var i_x = [thisPos.left, thisPos.left + $this.outerWidth()]
var i_y = [thisPos.top, thisPos.top + $this.outerHeight()];
if (t_x[0] < i_x[1] && t_x[1] > i_x[0] &&
t_y[0] < i_y[1] && t_y[1] > i_y[0]) {
overlap = this;
return false;
}
});
return overlap;
}