isotope icon indicating copy to clipboard operation
isotope copied to clipboard

centered fitRows

Open mariodermez opened this issue 9 years ago • 11 comments

http://jsfiddle.net/desandro/P6JGY/24/

I will use this jsfiddle as it is supposed to be an answer on centering items within isotope. Well it's not. Centering items means equal space from the sides per row of elements like in the image below. Why is this so hard to be achieved? I've paid almost 2000 euros in senior developers and no one could give me a fixed version of isotope with items centered like this. They were always floating to the left which is visually bad. fbxcu

mariodermez avatar Jul 30 '16 08:07 mariodermez

@desandro in your jsfiffle if you replace html with this below you will see what I mean.

<div id="container">
  <div class="item w1 h1"></div>
  <div class="item w1 h1"></div>
  <div class="item w1 h1"></div>
  <div class="item w1 h1"></div>
  <div class="item w1 h1"></div>
  <div class="item w1 h1"></div>
  <div class="item w1 h1"></div>
  <div class="item w1 h1"></div>
  <div class="item w1 h1"></div>
  <div class="item w1 h1"></div>
</div>

mariodermez avatar Jul 30 '16 08:07 mariodermez

I am THIS close to achieving this with a fitRows based layout and the code fragments supplied by genielabs in this thread: https://github.com/metafizzy/isotope/issues/428

I have created a plugin as such:

/*jshint browser: true, strict: true, undef: true, unused: true */

(function (window, factory) {
    'use strict';
    // universal module definition
    if (typeof define === 'function' && define.amd) {
        // AMD
        define([
            'get-size/get-size',
            'isotope/js/layout-mode'
        ],
          factory);
    } else if (typeof module == 'object' && module.exports) {
        // CommonJS
        module.exports = factory(
          require('get-size'),
          require('isotope-layout/js/layout-mode')
        );
    } else {
        // browser global
        factory(
          window.getSize,
          window.Isotope.LayoutMode
        );
    }

}(window, function factory(getSize, LayoutMode) {
    'use strict';

    // -------------------------- definition -------------------------- //

    // create an Outlayer layout class
    var FitRowsCentered = LayoutMode.create('fitRowsCentered');
    var proto = FitRowsCentered.prototype;

    proto._resetLayout = function () {
        // pre-calculate offsets for centering each row
        this.x = 0;
        this.y = 0;
        this.maxY = 0;
        this._getMeasurement('gutter', 'outerWidth');
        this.centerX = [];
        this.currentRow = 0;
        this.initializing = true;

        for (var i = 0, len = this.items.length; i < len; i++) {
            var item = this.items[i];
            this._getItemLayoutPosition(item);
        }

        this.centerX[this.currentRow].offset = (this.isotope.size.innerWidth + this.gutter - this.x) / 2;

        this.initializing = false;
        this.currentRow = 0;

        // centered offsets were calculated, reset layout
        this.x = 0;
        this.y = 0;
        this.maxY = 0;

        this._getMeasurement('gutter', 'outerWidth');
    };

    proto._getItemLayoutPosition = function (item) {
        item.getSize();
        var itemWidth = item.size.outerWidth + this.gutter;
        // if this element cannot fit in the current row
        var containerWidth = this.isotope.size.innerWidth + this.gutter;
        if (this.x !== 0 && itemWidth + this.x > containerWidth) {

            if (this.initializing)
                this.centerX[this.currentRow].offset = (containerWidth - this.x) / 2;
            this.currentRow++;

            this.x = 0;
            this.y = this.maxY;
        }

        if (this.initializing && this.x == 0) {
            this.centerX.push({ offset: 0 });
        }

        var position = {
            x: this.x + (this.initializing ? 0 : this.centerX[this.currentRow].offset),
            y: this.y
        };

        this.maxY = Math.max(this.maxY, this.y + item.size.outerHeight);
        this.x += itemWidth;

        return position;
    };

    proto._getContainerSize = function () {
        return { height: this.maxY };
    };

    return FitRowsCentered;

}));

It works perfectly on initial load. However, I am having issues when I use it with filtering. Once I call

$('#modulesGrid').isotope({ filter: selector, animationOptions: { duration: 300, easing: 'easeOutQuart' } });

the items are initially left-aligned. However, after clicking the same filtering link a second time, the items center perfectly.

Would it be possible to somehow collaborate to make this work? All credits to genielabs for the initial code, by the way, all I did was turn it into a plugin.

ingedev avatar Oct 05 '16 09:10 ingedev

I figured out what the issue was with the above solution when used in combination with filters. Instead of looping through the items, I needed to loop through the filtered items. I am attaching a file that works for me.

Please note it has not been tested very thoroughly yet and I am using it in combination with Bootstrap columns. No fixed column widths in my scenario. Other scenarios have not been tested yet. I just wanted to leave it here in case it helps someone else who is looking for a similar solution. And maybe David can have a look at it and see if he can possibly polish it up a bit and see if he can make it available for others.

Again, please note that 95% of this solution was written by genielabs in the other thread I linked to above.

isotope.fitRows.centered.zip

ingedev avatar Oct 07 '16 08:10 ingedev

Add a 👍 reaction to this issue if you would like to see this feature added. Do not add +1 comments — They will be deleted.


@ingedev Thank you so much for your contribution here.

I needed to loop through the filtered items

Unfortunately, Isotope v2 (and now v3) doesn't provide an easy way to do this. That's why I've held off on implementing this kind of layout mode. If others are interested in the layout mode, I can look back into it.

desandro avatar Oct 24 '16 21:10 desandro

Absolutely interested in. Something I often needed in the past and sadly couldn't achieve 100% by using isotope

mightym avatar Nov 15 '16 15:11 mightym

Hey @ingedev, may I ask how you load up Isotope/Masonry and your plugin? I am loading via Webpack, and have everything working via:

   "plugins":  [
     "./../node_modules/isotope-layout/dist/isotope.pkgd.js",
     "./../node_modules/imagesloaded/imagesloaded.pkgd.js",
    ],

But when I add your plugin like so:

   "plugins":  [
     "./../node_modules/isotope-layout/dist/isotope.pkgd.js",
     "./../node_modules/imagesloaded/imagesloaded.pkgd.js",
     "./scripts/util/isotope.FitRows.centered.js"
    ],

I get the error thrown from the base isotope package:

Uncaught Error: No layout mode: fitRowsCentered

(isotope.pkgd.js:3392):

throw new Error( 'No layout mode: ' + layoutMode );

A little new to webpack and js modules; any and all help greatly appreciated!

lukedanielson avatar Jul 08 '17 01:07 lukedanielson

@ingedev Thank you so much - your solution worked for me. 🥇

FrancesCoronel avatar Jan 08 '18 03:01 FrancesCoronel

I have some code that initiates isotope a few times on a page and is triggered by one filter. Similar to this example: https://github.com/metafizzy/isotope/issues/1026

This works for this example on page load, but when the filter is triggered, my solution for hiding empty sections stops working and the filter doesn't work properly either.

Here are two codepens: WORKING - with fitRows https://codepen.io/anon/pen/pLzOWO

ERROR - with fitRowsCentered https://codepen.io/anon/pen/OvLodQ

Any ideas on how to get the centered layout to work for my case? Thank you!

-edit- it seems to only have the issue when the filter is applied to more than one iteration of a container on a page. Still stumped on where the error is though. Any ideas?

HandHugs avatar Mar 08 '18 07:03 HandHugs

@ingedev's solution does not work with ajax loaded elements.

lofcz avatar Jul 17 '19 05:07 lofcz

Fixed @ingedev's solution to work with ajax loaded content


(function (window, factory) {
    'use strict';
    // universal module definition
    if (typeof define === 'function' && define.amd) {
        // AMD
        define([
            'get-size/get-size',
            'isotope/js/layout-mode'
        ],
          factory);
    } else if (typeof module == 'object' && module.exports) {
        // CommonJS
        module.exports = factory(
          require('get-size'),
          require('isotope-layout/js/layout-mode')
        );
    } else {
        // browser global
        factory(
          window.getSize,
          window.Isotope.LayoutMode
        );
    }

}(window, function factory(getSize, LayoutMode) {
    'use strict';

    // -------------------------- definition -------------------------- //

    // create an Outlayer layout class
    var FitRowsCentered = LayoutMode.create('fitRowsCentered');
    var proto = FitRowsCentered.prototype;

    proto._resetLayout = function () {
        // pre-calculate offsets for centering each row
        this.x = 0;
        this.y = 0;
        this.maxY = 0;
        this._getMeasurement('gutter', 'outerWidth');
        this.centerX = [];
        this.currentRow = 0;
        this.initializing = true;

        console.log(this.isotope);

        for (var i = 0, len = this.isotope.items.length; i < len; i++) {
            var item = this.isotope.items[i];
            this._getItemLayoutPosition(item);
        }

        //alert(this.isotope.filteredItems.length);

        this.centerX[this.currentRow].offset = (this.isotope.size.innerWidth + this.gutter - this.x) / 2;

        this.initializing = false;
        this.currentRow = 0;

        // centered offsets were calculated, reset layout
        this.x = 0;
        this.y = 0;
        this.maxY = 0;

        this._getMeasurement('gutter', 'outerWidth');
    };

    proto._getItemLayoutPosition = function (item) {
        item.getSize();
        var itemWidth = item.size.outerWidth + this.gutter;
        // if this element cannot fit in the current row
        var containerWidth = this.isotope.size.innerWidth + this.gutter;
        if (this.x !== 0 && itemWidth + this.x > containerWidth) {

            if (this.initializing)
                this.centerX[this.currentRow].offset = (containerWidth - this.x) / 2;
            this.currentRow++;

            this.x = 0;
            this.y = this.maxY;
        }

        if (this.initializing && this.x == 0) {
            this.centerX.push({ offset: 0 });
          //  alert("kokot");
        }

        var position;

        if (typeof this.centerX[this.currentRow] !== 'undefined') {
             position = {
                x: this.x + (this.initializing ? 0 : this.centerX[this.currentRow].offset),
                y: this.y
            };
        } else {
             position = {
                x: this.x,
                y: this.y
            };
        }

       

        this.maxY = Math.max(this.maxY, this.y + item.size.outerHeight);
        this.x += itemWidth;

        return position;
    };

    proto._getContainerSize = function () {
        return { height: this.maxY };
    };

    return FitRowsCentered;

}));

Save yourself a headache and use this.

lofcz avatar Jul 17 '19 05:07 lofcz

Any idea on doing the same centering but with the masonry layout (not fixed width items) @lofcz ?

PlopTheReal avatar Jan 31 '20 13:01 PlopTheReal