在仅包含图像的列数CSS列中平衡高度

时间:2016-01-09 17:26:42

标签: javascript html css

我正在尝试使用以下代码平衡图片库中列之间的高度:

section {
    background: magenta;
    /* So you can see the area I don't want to appear */
}

.gallery {
    width: 100%;
    line-height: 0;
    -webkit-column-count: 2;
    -webkit-column-gap: 0;
    -moz-column-count: 2;
    -moz-column-gap: 0;
    column-count: 2;
    column-gap: 0;
}
<section class="gallery">
    <div>
        <img src="http://lorempixel.com/800/1000/">
    </div>
    <div>
        <img src="http://lorempixel.com/800/600/">
    </div>
    <div>
        <img src="http://lorempixel.com/800/200/">
    </div>
    <div>
        <img src="http://lorempixel.com/800/700/">
    </div>
    <div>
        <img src="http://lorempixel.com/800/900/">
    </div>
    <div>
        <img src="http://lorempixel.com/800/400/">
    </div>
    <div>
        <img src="http://lorempixel.com/800/200/">
    </div>
    <div>
        <img src="http://lorempixel.com/800/600/">
    </div>
    <div>
        <img src="http://lorempixel.com/800/700/">
    </div>
    <div>
        <img src="http://lorempixel.com/800/600/">
    </div>
    <div>
        <img src="http://lorempixel.com/800/550/">
    </div>
    <div>
        <img src="http://lorempixel.com/800/700/">
    </div>
    <div>
        <img src="http://lorempixel.com/800/600/">
    </div>
    <div>
        <img src="http://lorempixel.com/800/1000/">
    </div>
    <div>
        <img src="http://lorempixel.com/800/700/">
    </div>
</section>

这是一个JSFiddle:https://jsfiddle.net/auan2xnj/1/

图片都具有不同的高度,但都具有相同的宽度。我的column-fill设置为balance(默认情况下)。

问题:

在JSFiddle中,它看起来相当不错,但在我正在开发的网站上,比较列的高度出现了一些巨大的差距。我猜这是因为图像在HTML中的顺序,考虑到它们将被CSS放入列中的完全相同的顺序。这是我的项目的截图,使用与JSFiddle完全相同的代码:

enter image description here

行为:

当我为.gallery元素提供硬编码的height值时,列始终平衡得更好。这是一个问题,因为在我的网站中,图像是动态添加的,我永远不会知道所有画廊的确切高度。

请求:

我想找到一段代码(无论是什么,我认为我可以实现一些JS)来修复这个问题,或者通过重新排序HTML中的图像,以便结果是最好的,或者无论如何为了动态设置高度,以解决问题。

5 个答案:

答案 0 :(得分:9)

编辑: 如果您不需要保留line-height: 0,则只需使用.gallery img {display:block}并删除行高,这就是您所需要的一切。那将是最好的。 table-cell等可能会产生一些副作用。例如vertical-align: middle在图像下留下一个空间,而且只是一个黑客。

https://jsfiddle.net/bruLwktv/

Challange接受了,这是解决方案:;)

该算法确保每个图像都被加载,然后将它们分成两个颜色,使得最接近的总高度可以创建最小的间隙。

使用The greedy algorithm分区问题创建平衡分区。

var gallery = document.getElementsByClassName("gallery")[0]
var images = gallery.getElementsByTagName("img")
var notLoaded = 0

window.onload = function() {
  for (var i = images.length; i--;) {
    if (images[i].width == 0) {
      // let the image tell us when its loaded
      notLoaded++
      images[i].onload = function() {
        if (--notLoaded == 0) {
          allImgLoaded()
        }
      }
    }
  }
  // check if all images are already loaded
  if (notLoaded == 0) allImgLoaded()
}

function allImgLoaded() {
  // Partition images
  var imgs = partitionImages(images)
  // reorder DOM
  for (var i = images.length; i--;) {
    gallery.appendChild(imgs[i])
  }
}

function partitionImages(images) {
  var groupA = [], totalA = 0
  var groupB = [], totalB = 0

  // new array width img and height
  var imgs = []
  for (var i = images.length; i--;) {
    imgs.push([images[i], images[i].height])
  }
  // sort asc
  imgs.sort(function(a, b) {
    return b[1] - a[1]
  });
  // reverse loop
  for (var i = imgs.length; i--;) {
    if (totalA < totalB) {
      groupA.push(imgs[i][0])
      totalA += imgs[i][1]
    } else {
      groupB.push(imgs[i][0])
      totalB += imgs[i][1]
    }
  }

  return groupA.concat(groupB)
}
section {
  background: magenta;
  /* So you can see the area I don't want to appear */
}
.gallery {
  width: 100%;
  line-height: 0;
  -webkit-column-count: 2;
  -webkit-column-gap: 0;
  -moz-column-count: 2;
  -moz-column-gap: 0;
  column-count: 2;
  column-gap: 0;
}
<section class="gallery">
  <div><img src="http://lorempixel.com/800/1000/"></div>
  <div><img src="http://lorempixel.com/800/600/"></div>
  <div><img src="http://lorempixel.com/800/200/"></div>
  <div><img src="http://lorempixel.com/800/700/"></div>
  <div><img src="http://lorempixel.com/800/900/"></div>
  <div><img src="http://lorempixel.com/800/400/"></div>
  <div><img src="http://lorempixel.com/800/200/"></div>
  <div><img src="http://lorempixel.com/800/600/"></div>
  <div><img src="http://lorempixel.com/800/700/"></div>
  <div><img src="http://lorempixel.com/800/600/"></div>
  <div><img src="http://lorempixel.com/800/550/"></div>
  <div><img src="http://lorempixel.com/800/700/"></div>
  <div><img src="http://lorempixel.com/800/600/"></div>
  <div><img src="http://lorempixel.com/800/1000/"></div>
  <div><img src="http://lorempixel.com/800/700/"></div>
</section>

答案 1 :(得分:3)

是的,列实现现在很奇怪。

如果你摆脱“line-height:0;”这个问题变得不那么严重了。我不知道为什么。除了说行高的东西是一个黑客去除图像底部无处不在的小空间/边距(如果你不知道我在说什么,删除jfiddle中的行间距规则)

据说空间/边距是存在的,因为HTML不知道img元素不是文本,因此为“y”和“g”等字符的“尾部”留下了空间。这是一个荒谬的错误,我认为应该在十年前修复过。对我来说,这是IE&lt; 9级别的愚蠢实现。

无论如何,咆哮,一种方法来修复空间/边距而不使用行高hack:

img {
    vertical-align: middle;
}

此外,如果你肯定所有的图像都是相同的宽度,这不是一个问题,但在小提琴中,通过重叠列的宽度的图像使间隙问题变得更糟。你应该设置

img {
    width: 100%
    // and, for insurance, I also add:
    height: auto;
}

确保您将图像拟合到列中。

答案 2 :(得分:1)

我已经为div { -webkit-margin-before: 0; -webkit-margin-after: 0; } 元素添加了一些CSS,如果有效,请告诉我:

https://jsfiddle.net/rplittle/auan2xnj/3/

我也见过这个:

{{1}}

(必要时调整/添加前缀)

或者您可以尝试Masonry,它构建良好且易于设置。

答案 3 :(得分:1)

  
    

请求:

         

我想找到一段代码(无论它是什么,我认为我可以实现&gt;&gt;某些JS)来解决这个问题,或者通过重新排序HTML中的图像来实现&gt;&gt;结果是最好的,或者以任何方式动态设置高度以便解决问题。

  

贪婪算法是一种很好的方法,对于必须动态扩展切片的流非常有用。 (像分页一样)

我已经添加了一个功能,通过对项目进行分组并在组之间切换项目来实现更好的平衡。

这里的列是通过创建2个内联块和50%宽度的div来实现的;简单,适用于所有现代浏览器。

function balance(numGroups, items, fetchValue){
    function delta(a, b){
        var da = a-avg, db = b-avg;
        return da*da + db*db;
    }

    function testAndSwitchItems(a,b){
        var valueA, valueB,
            lengthA = a.length, lengthB = b.length,
            indexA = lengthA, indexB = lengthB,
            bestDelta = delta(a.sum, b.sum);

        //test every item in this first column against every value in the second column
        for(var i = 0; i <= lengthA; ++i){
            //accessing a not available index of an Array may deoptimize this function
            //that's why i check the index before accessing the element.
            valueA = (i < lengthA && a[i] || nullElement).value;

            for(var j = 0; j <= lengthB; ++j){
                valueB = (j < lengthB && b[j] || nullElement).value;

                //test wether it would be an improvement to switch these items
                var d = delta(
                    a.sum + valueB - valueA, 
                    b.sum + valueA - valueB
                );

                //find the best combination
                if(d < bestDelta){
                    indexA = i;
                    indexB = j;
                    bestDelta = d;
                }
            }
        }


        var elmA = indexA < lengthA && a[indexA], 
            elmB = indexB < lengthB && b[indexB],
            tmp;

        if(elmA && elmB){
            //switch items
            a[indexA] = elmB;
            b[indexB] = elmA;

        }else if(elmA){
            //move an item from a to b
            b.push(elmA);
            tmp = a.pop()
            if(elmA !== tmp)
                a[indexA] = tmp;

        }else if(elmB){
            //move an item from b to a
            a.push(elmB);
            tmp = b.pop()
            if(elmB !== tmp)
                b[indexB] = tmp;

        }else{
            //no switch would improve the result
            return false;
        }

        //compute the new sums per group
        valueA = (elmA || nullElement).value;
        valueB = (elmB || nullElement).value;
        a.sum += valueB - valueA;
        b.sum += valueA - valueB;
        return true;
    }

    function initGroupsAndReturnSum(sum, item, i){
        var value = fetchValue? fetchValue(item): item;

        //distribute the items
        if(i<numGroups){
            var group = groups[i] = [];
            group.sum = value;
        }else{
            group = groups[ i%numGroups ];
            group.sum += value;
        }

        group.push({
            index: i,
            value: value
        });

        return sum + value;
    }

    var groups = [],
        nullElement = { value: 0 },
        //don't care wether items is an Array, a nodeList or a jQuery-object/list, ...
        avg = groups.reduce.call(items, initGroupsAndReturnSum, 0) / groups.length;

    //start switching items between the groups
    do {
        for(var i=1, done = true; i<groups.length; ++i)
            for(var j=0; j<i; ++j)
                //test each combination of groups
                if(testAndSwitchItems(groups[j], groups[i])){
                    //this switch may have affected the computation against other groups
                    //do another loop
                    done = false;
                }
    }while(!done);

    function sorter(a,b){
        return a.index - b.index
    }

    function getItem(item){
        return items[item.index]
    }

    return groups.map(function(a){ 
        return a.sort(sorter).map(getItem) 
    });
}

以及使用它的可能方式

var groups = balance(2, //num columns
        //something Arraylike that contains some objects to be grouped
        document.querySelectorAll('img'),
        //a function to compute a value out of an Array-item
        img => img.naturalHeight / img.naturalWidth
    );

https://jsfiddle.net/77o3ptk2/2/

仅仅为了它,该示例还包括一些样式,如图像周围的填充和枚举,以显示数组已被洗牌的程度。

答案 4 :(得分:0)

您可以使用此http://brm.io/jquery-match-height/来均衡每列的高度。而且它在移动响应方面也很出色。不需要css,只需添加库并放置此代码段

$(function() {
    $('.gallery').find('div').matchHeight(options);
});