11

Using Masonry on a catalog site. By default, the items displayed sort by date, then by part number. My images are varying height. The problem is, and correct me if I'm wrong, Masonry sets row 2, item 1 below the shortest item in row 1 rather than on the left of the page. So in other words, when my database returns a certain order, Masonry places those items ordered top to bottom (when varying heights), rather than left to right.

I don't find anything in documentation to control this. Is there a way to force Masonry to keep items in order left to right and just float vertically based on height?

I attempted to post an illustration, but apparently I'm not qualified to do so. Here's a link to the illustration:

enter image description here

Yotam Omer
  • 14,510
  • 11
  • 55
  • 61
Justin Graber
  • 111
  • 1
  • 4
  • The problem you'll have is that 1 big cell can be as tall as 2 smaller cells (or more). In that case, one cell will span on 2 or more rows, I can be very confusing. For exemple, if you take your second illustration and reduce it to 3 columns, you'll stack boxes 3, 6, and 9, which are all tall. You'll end up with box 7 before 6, and you'll keep shifting if you have more boxes. Unless you vary the vertical margins to realign the boxes, I don't see how it can be done. – Tchoupi Jul 09 '13 at 01:42

5 Answers5

4

inspired by Billy's answer (and therefor by skobaljic's answer :) ) I just changed the initialisation of shortColIndex and minimumY to achieve this behaviour. Might be a little easier plus - using the colGroup's size - responsive layouts with different column counts still work.

Here is the complete code for v3.3.2:

Masonry.prototype._getItemLayoutPosition = function( item ) {
  item.getSize();
  // how many columns does this brick span
  var remainder = item.size.outerWidth % this.columnWidth;
  var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
  // round if off by 1 pixel, otherwise use ceil
  var colSpan = Math[ mathMethod ]( item.size.outerWidth / this.columnWidth );
  colSpan = Math.min( colSpan, this.cols );

  var colGroup = this._getColGroup( colSpan );
  // ### HACK: sort by natural order, not by min col height
  // get the minimum Y value from the columns
  // var minimumY = Math.min.apply( Math, colGroup );
  // var shortColIndex = utils.indexOf( colGroup, minimumY );
  var shortColIndex = jQuery(item.element).index() % colGroup.length;
  var minimumY = colGroup[shortColIndex];

  // position the brick
  var position = {
    x: this.columnWidth * shortColIndex,
    y: minimumY
  };

  // apply setHeight to necessary columns
  var setHeight = minimumY + item.size.outerHeight;
  var setSpan = this.cols + 1 - colGroup.length;
  for ( var i = 0; i < setSpan; i++ ) {
    this.colYs[ shortColIndex + i ] = setHeight;
  }

  return position;
};
Christoph
  • 41
  • 1
3

There is no option in Masonry to sort items, because it is a simple plugin that places bricks one by one. Choosing new brick position is simple: place it higher as you can.

But what one can do in this case is to change the script by adding one two lines that define sorting, assuming all bricks are same width (for example, always 5 in row).

To demonstrate this, I used jQuery Masonry (was compressed) and you can check it out in this Fiddle.

JS

$.Mason.prototype._placeBrick = function(e) {
    var n = $(e),
        r, i, s, o, u;
    r = Math.ceil(n.outerWidth(!0) / this.columnWidth), r = Math.min(r, this.cols);
    if (r === 1) s = this.colYs;
    else {
        i = this.cols + 1 - r, s = [];
        for (u = 0; u < i; u++) o = this.colYs.slice(u, u + r), s[u] = Math.max.apply(Math, o)
    }
    var a = Math.min.apply(Math, s),
        f = 0;
    for (var l = 0, c = s.length; l < c; l++)
        if (s[l] === a) {
            f = l;
            break
        }
    /* Add new calculation, what column next brick is in: */
    f = $(e).index() % this.cols; /* Get col index f: Just divide with element's index */
    a = s[f]; /* This is current height for f-th column */
    /* END of customizing */
    var h = {
        top: a + this.offset.y
    };
    h[this.horizontalDirection] = this.columnWidth * f + this.offset.x, this.styleQueue.push({
        $el: n,
        style: h
    });
    var p = a + n.outerHeight(!0),
        d = this.cols + 1 - c;
    for (l = 0; l < d; l++) this.colYs[f + l] = p;
};
skobaljic
  • 8,855
  • 1
  • 23
  • 47
0

The simple answer is "no"

Masonry almost by definition rearranges things to fit and assumes the order isn't important. I say "almost" because I think you're right that this isn't spelled out anywhere in the docs -- but that's how it works.

Sean Redmond
  • 3,584
  • 19
  • 27
0

Have a look at Masonry Ordered. I haven't tested this, but it looks like it might be the solution you are looking for: http://masonry-ordered.tasuki.org/options/

Graeme Blackwood
  • 2,096
  • 1
  • 10
  • 3
0

Inspired by skobaljic's answer (which is only compatible with Masonry v2), I have hacked v3 to do the same thing. Here it is:

Masonry.prototype._getItemLayoutPosition = function( item ) { // Hack Masonry to order items by their order in the DOM
    item.getSize();
    // how many columns does this brick span
    var remainder = item.size.outerWidth % this.columnWidth;
    var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
    // round if off by 1 pixel, otherwise use ceil
    var colSpan = Math[ mathMethod ]( item.size.outerWidth / this.columnWidth );
    colSpan = Math.min( colSpan, this.cols );

    var col = $(item.element).index() % 3; // HACK : determine which column we want based on the element's index in the DOM

    var colGroup = this._getColGroup( colSpan );
    colGroup = [this.colYs[ col ]]; // HACK : return only the column we want
    // get the minimum Y value from the columns
    var minimumY = Math.min.apply( Math, colGroup );
    var shortColIndex = col; // HACK

    // position the brick
    var position = {
      x: this.columnWidth * shortColIndex,
      y: minimumY
    };

    // apply setHeight to necessary columns
    var setHeight = minimumY + item.size.outerHeight;
    this.colYs[ shortColIndex ] = setHeight; // HACK : set height only on the column we used

    return position;
};

Hope this helps someone

Community
  • 1
  • 1
Bill
  • 3,382
  • 20
  • 41