vue-binpacker-plugin
Version:
a vue.js binpacker plugin
201 lines (172 loc) • 4.31 kB
JavaScript
function contains (container, rect)
{
const otherWidth = rect.width || 0
const otherHeight = rect.height || 0
return container.x <= rect.x && container.y <= rect.y && container.x + container.width >= rect.x + otherWidth && container.y + container.height >= rect.y + otherHeight
}
function merge (rects)
{
for (let i = 0; i < rects.length; i++)
{
let rect = rects[i]
let j = 0
let compareRect = rects[i + j]
while (compareRect)
{
if (compareRect === rect)
{
j++
}
else if (contains(compareRect, rect))
{
rects.splice(i, 1)
rect = rects[i]
compareRect = null
continue
}
else if (contains(rect, compareRect))
{
rects.splice(i + j, 1)
}
else
{
j++
}
compareRect = rects[i + j]
}
}
}
function overlaps (a, b, gap)
{
const aRight = a.x + a.width + gap.x
const aBottom = a.y + a.height + gap.y
const bRight = b.x + b.width + gap.x
const bBottom = b.y + b.height + gap.y
return a.x < bRight && aRight > b.x && a.y < bBottom && aBottom > b.y
}
function subtract (a, b, gap)
{
const free = []
const aRight = a.x + a.width
const aBottom = a.y + a.height
const bRight = b.x + b.width
const bBottom = b.y + b.height
// left
if (a.y < b.y)
{
free.push({
x: a.x,
y: a.y,
width: a.width,
height: b.y - a.y - gap.y
})
}
// right
if (aRight > bRight)
{
free.push({
x: bRight + gap.x,
y: a.y,
width: aRight - bRight,
height: a.height
})
}
// bottom
if (aBottom > bBottom)
{
free.push({
x: a.x,
y: bBottom + gap.y,
width: a.width,
height: aBottom - bBottom
})
}
// left
if (a.x < b.x)
{
free.push({
x: a.x,
y: a.y,
width: b.x - a.x - gap.x,
height: a.height
})
}
return free
}
function fits (a, b)
{
return a.width >= b.width && a.height >= b.height
}
function getStrategy (rtl)
{
return (rtl)
? {
sorter: function sorter (a, b)
{
return a.y - b.y || a.x - b.x
},
place: function place (positioned, space)
{
positioned.x = space.x + space.width - positioned.width
positioned.y = space.y
}
}
: {
sorter: function sorter (a, b)
{
return a.y - b.y || a.x - b.x
},
place: function place (positioned, space)
{
positioned.x = space.x
positioned.y = space.y
}
}
}
function packer (size, items, options)
{
const optionsMerged = {
gap:
{
x: 0,
y: 0
},
rtl: false
}
Object.assign(optionsMerged, options)
const spaces = [{
x: 0,
y: 0,
width: size.width || Infinity,
height: size.height || Infinity
}]
const strategy = getStrategy(optionsMerged.rtl)
return items.map(function (item)
{
const positioned = {
width: item.width || 0,
height: item.height || 0
}
const space = spaces.find(function (space)
{
return fits(space, positioned)
})
if (space)
{
strategy.place(positioned, space)
const overlapping = spaces.filter(function (space)
{
return overlaps(positioned, space, optionsMerged.gap)
})
overlapping.forEach(function (space)
{
spaces.splice(spaces.indexOf(space), 1)
spaces.push.apply(spaces, subtract(space, positioned, optionsMerged.gap))
})
merge(spaces)
spaces.sort(strategy.sorter)
}
return positioned
})
}
export default packer