javascript 算法采取明确的高度图像和布局,他们在最佳紧凑的“砖石”式布局?

siotufzp  于 2023-02-21  发布在  Java
关注(0)|答案(1)|浏览(109)

我使用react-masonry-css将一些图片布局成类似于masonry的布局,但它只是在每列中放置了基本相同数量的图片,最终在每列底部留下的空间量上有很大的差异,如下图所示(这是所有列的底部):

(Note,本演示中有几张图片缺失,但即使它们都存在,底部剩余空间也有很大差异)。
看起来库所做的一切就是在每列中放置相同数量的项目,而不考虑图像的高度。
我希望它能更优化地分布图像,因为我还在JSON数据中包含了每个图像的宽度和高度。如何使用高度来计算如何最优化地将图像放置在已知数量的列中?乍一看,这似乎非常复杂,好像需要一个复杂的研究论文般的算法。这是真的吗?如果是,解决这个问题的一般算法是什么,所以我可以用JavaScript写一个快速的实现,如果它很简单,怎么做呢?
到目前为止,在我的脑海中,我可能会将图片的数量除以列数,以给予每列有多少的第一个粗略估计。然后我会在每列中放置一张图片,这样7列中的每一列都有1张图片。然后我会像砖块一样在每列上放置一张图片。我会尽最大努力保持列的高度相同。搜索并选择适合适当高度的图像。
这只是头脑 Storm ,但我已经看到了一些漏洞和陷阱,在这种不可行的方法。这似乎很难,不知道这是否是一个解决的问题,虽然考虑到图像可以在高度上有很大的变化。
最终的目标是让所有的柱子高度大致相同,就是这样,尽可能的接近。

3pvhb19x

3pvhb19x1#

如果我们将“最紧凑”操作为表示最短的边界框,那么这是一个identical-machines scheduling问题。每个图像对应一个作业,每列对应一台机器,作业的处理时间是对应图像的高宽比(加上填充)。
虽然调度是NP困难的,但是有一个简单且被证明有效的近似值,称为Longest Processing-Time First。对于您的问题,对于按高度/宽度降序排列的每个图像,将其分配给当前最短的列。(您可以在末尾打乱每列中图像的顺序,以避免偏向较高的图像。)边界框永远不会超过34%比它需要的更长(嗯,也许因为填隙的原因多一点)。

// Set up some test data.
const imageCount = 50;
let images = [];
for (let i = 0; i < imageCount; ++i) {
  // Allow the images to vary in aspect ratio between 5:1 and 1:5.
  images.push({
    id: i,
    width: Math.random() + 0.25,
    height: Math.random() + 0.25,
  });
}

// Parameters.
const columnCount = 10;
const thumbnailWidth = 100;
const interstitialHeight = 10;

// Algorithm begins. Initialize empty columns.
let columns = [];
let columnHeights = [];
for (let j = 0; j < columnCount; ++j) {
  // This height will be correct once the column is nonempty.
  columnHeights.push(-interstitialHeight);
  columns.push([]);
}

// Sort the images by aspect ratio descending.
function aspectRatioDescending(a, b) {
  return b.height / b.width - a.height / a.width;
}
images.sort(aspectRatioDescending);

// Assign each image to a column.
for (const image of images) {
  // Find the shortest column.
  let shortest = 0;
  for (let j = 1; j < columnCount; ++j) {
    if (columnHeights[j] < columnHeights[shortest]) {
      shortest = j;
    }
  }
  // Put the image there.
  columnHeights[shortest] +=
    interstitialHeight + thumbnailWidth * (image.height / image.width);
  columns[shortest].push(image);
}

// Shuffle the columns for aesthetic reasons.
for (const column of columns) {
  for (let k = 1; k < column.length; ++k) {
    const i = Math.floor((k + 1) * Math.random());
    let temp = column[i];
    column[i] = column[k];
    column[k] = temp;
  }
}

const maxHeight = Math.max.apply(null, columnHeights);
const minHeight = Math.min.apply(null, columnHeights);
// Analyze the layout.
console.log(
  "// The tallest column is %f%% taller than the shortest.",
  (100 * (maxHeight - minHeight)) / minHeight
);
// The tallest column is 3.030982959129835% taller than the shortest.

相关问题