我正在开发一个门户/仪表板类型的界面,它具有可以在仪表板空间中自由拖动的面板/小部件,只要它们不覆盖任何其他面板。可以通过包含所有可用面板的菜单将新面板添加到仪表板,单击菜单项时,面板将放置在仪表板中。当前占据仪表板空间的面板都在这样的对象中表示:
{
'panel_1': { top: 0, left: 0, width: 300, height: 350 },
'panel_2': { top: 0, left: 370, width: 200, height: 275 },
'panel_3': { top: 370, left: 0, width: 275, height: 400 },
...
}
我的问题是,什么是有效的算法,当用户点击菜单中的一个时,正确地将一个新面板(具有给定的宽度和高度)放置在最靠近左侧和顶部的未占用空间中(x和y)值0,0,没有重叠任何现有的面板?
答案 0 :(得分:2)
我认为,简单的强力算法适合你。我记得,适合矩形解决了另一个问题
对仪表板轴进行迭代,找出是否可以放置矩形,直到X < rectangle.widh + dashboard.width
,Y是否相同。
仪表板上的Foreach X,Y迭代每个面板以查找它们是否重叠。您可以应用一些优化,以减少迭代次数。如果面板重叠矩形,则可以将X或Y(嵌套循环中)增加1,而不是增加面板的宽度或高度。
在大多数情况下,您不会进行dashboard.width*dashboard.height*panel.count
次迭代。通过一些优化,它会找到最适合的快速
答案 1 :(得分:0)
我知道这是一个老问题,但如果有人想要概念证明那么它看起来像这样:
function findSpace(width, height) {
var $ul = $('.snap-layout>ul');
var widthOfContainer = $ul.width();
var heightOfContainer = $ul.height();
var $lis = $ul.children('.setup-widget'); // The li is on the page and we dont want it to collide with itself
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) {
// Found a space
return pos;
}
var colliderPos = $collider.position();
// We have collided with something, there is no point testing the points within this widget so lets skip them
var newX = colliderPos.left + $collider.width() - 1; // -1 to account for the ++ in the for loop
x = newX > x ? newX : x; // Make sure that we are not some how going backwards and looping forever
var colliderBottom = colliderPos.top + $collider.height();
if (heightOfShortestInRow == 1 || colliderBottom - y < heightOfShortestInRow) {
heightOfShortestInRow = colliderBottom - y; // This isn't actually the height its just the distance from y to the bottom of the widget, y is normally at the top of the widget tho
}
}
y += heightOfShortestInRow - 1;
}
//TODO: Add the widget to the bottom
}
function isOverlapping($obsticles, tAxis, width, height) {
var t_x, t_y;
if (typeof (width) == 'undefined') {
// Existing element passed in
var $target = $(tAxis);
tAxis = $target.position();
t_x = [tAxis.left, tAxis.left + $target.outerWidth()];
t_y = [tAxis.top, tAxis.top + $target.outerHeight()];
} else {
// Coordinates and dimensions passed in
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;
}