Wednesday, July 23, 2014

Masonry and Pagination

Recently I've decided to add Pinterest-style layout and infinite scrolling of images to the main page of one of my apps. (I may decide not to keep it later on, but hey, this is a good learning experience, right?)

I've done my homework and settled with a gem called kaminari for pagination, and Masonry, a JS grid layout library. Due to the popularity of dynamic layouts, a lot of code examples are available. Based on those, my snippets look like so (in index.html.erb):

<div class="masonry-container">
  <% @contents.each do |content| %>
   <div class="masonry-item">
     <%= image_tag content.image.url(:original) %>
    </div>
  <% end %>
</div>
<%= paginate @contents %>

and in application.js:

$(function()
{
    var $container = $('.masonry-container');
    $container.masonry({
        isFitWidth: true,
        //columnWidth: '.masonry-item',
        itemSelector: '.masonry-item'
        }).imagesLoaded(function(){
            $container.masonry('reload');
    });

    $container.infinitescroll({
            navSelector  : "nav.pagination",
            nextSelector : "nav.pagination a[rel=next]",
            itemSelector : ".masonry-container div.masonry-item",
        },
        function(newElements) {
            var $newElems = $(newElements).css({opacity: 0});
            $newElems.imagesLoaded(function(){
                $newElems.animate({opacity: 1});
                $container.masonry('appended', $newElems, true);
            });
        }
    );
});

I was generally happy with the behavior of my scrolling except for several things.
  1. I wanted to center and distribute all images in the main container evenly, for which Masonry provides an option isFitWidth: true. As I discovered, you can't use the option columnWidth at the same time (that's why it is commented), as this combination appears to stack all images on top of each other. (I guess, I didn't read the documentation well enough but it took me some time to discover the cause).
  2. The images I was loading differed in height quite a bit and perhaps for this reason they were not distributed as nicely as I wanted them to; there was too much white space vertically for my taste. Until I can find a better solution, I am settling with the following:
    • Paperclip gem for image uploading allows to serialize the image dimensions and to store them in the database;
    • One can create a scope, where the results will be sorted by height in a descending order. The scope will be used in controller to filter the results like so:

    • scope :by_height, ->   { order('height DESC') }
      @contents = Content.all.by_height.page(params[:page])
      

    Sorting the images in descending order beforehand gave me the desired look: very nicely and tightly stacked images with much less white space in between.
  3. As you scroll down and images are being loaded, you are supposed to be getting a message at the end of the current window saying something like "More images are being loaded....". My message somehow got attached to the top of the page, not the bottom. I wonder what combination of the CSS rules and/or other factors could be causing this behavior. In the meantime, I'm fixing the position of that element forcefully:

  4. #infscr-loading{
      position:fixed;
      bottom: 0;
    }
    

In conclusion: that was fun.

No comments :

Post a Comment