angular-masonry icon indicating copy to clipboard operation
angular-masonry copied to clipboard

If imagesLoaded calls its callback in the same tick, the display is corrupted

Open passy opened this issue 12 years ago • 17 comments

This happens if the images are already cached and causes the images to overlap until either a resize or a manual $(element).masonry('layout') is triggered.

screenshot from 2013-07-29 11 47 21

passy avatar Jul 29 '13 09:07 passy

Workarounds: Set the height and width of images before they are loaded or disable the imagesLoaded cache.

passy avatar Jul 29 '13 09:07 passy

I've runned into the same problem, we use a library that resizes the images dynamically. How do you disable the imageLoaded cache?

goldnarms avatar Oct 09 '13 06:10 goldnarms

@goldnarms hey would you mind sharing the library you've used, I came accross the same issue :) thanks.

malikov avatar Nov 10 '13 17:11 malikov

I have this problem and setting the width and height of each image is not an option for me and I can't seem to find any information on that imagesloaded cache.

mafrost avatar Feb 20 '14 09:02 mafrost

@mafrost Is it possible to disable ImagesLoaded cache?

rmariuzzo avatar Feb 28 '14 22:02 rmariuzzo

@rmariuzzo Nah, not exactly, at least it didn't work for me. I found this pull request over at ImagesLoaded github though: https://github.com/nyroDev/imagesloaded/commit/51288c54bca5b36137d3065a6b45dee6f02a2a90 where nyroDev posts a fix. It works great for me since the problem has not reoccured.

mafrost avatar Mar 03 '14 06:03 mafrost

Thanks @mafrost you have solved all my problems! :thumbsup:

FoxxMD avatar May 01 '14 22:05 FoxxMD

I'm having this issue and the patch to ImagesLoaded doesn't seem to help.

cayblood avatar Aug 04 '14 12:08 cayblood

Here is an example: http://www.youngbloods.org/estate-sale Notice how items overlap each other until you resize the page enough to case the layout to change.

cayblood avatar Aug 04 '14 13:08 cayblood

@cayblood I worked around the problem by making a direct call to to the layout method on the masonry instance after angular-masonry has done doing its thing.

I found that angular-masonry wasn't handling image addition very well and wasn't giving the masonry instance enough time to relayout the page after adding all images to the container. I worked around having to make multiple calls to masonry by wrapping the call in a $timeout and basically waiting until angular-masonry fired the last layoutComplete event and then manually triggered it one more time. This works about 90% of the time and definitely isn't a great fix, but it's doable for me. Here's my code

FoxxMD avatar Aug 04 '14 13:08 FoxxMD

Thanks @FoxxMD. I'm going to see if I can accomplish something similar without having to create my own directive.

cayblood avatar Aug 04 '14 13:08 cayblood

Hi everybody. Had the same issue, here is what helped: in controller, you can call setTimeout(function(){ $rootScope.$broadcast('masonry.reload'); }, 1000); So it will make your masonry reload after all the images were loaded.

youanswer avatar Apr 23 '15 05:04 youanswer

Thanks @youanswer, your solution was great, I use it in several places in my app!

However, I think the timeout duration needs to be longer on slower devices or on narrow bandwidth. I keep getting complaints that on mobiles and even on some desktops the lists appear scrambled on first load (by entering URL directly in the address bar) but they seem to work fine after navigating away and then coming back to the list (probably because the resources are already loaded at that time).

I think that setting a higher timeout (device-dependent) would break user experience.

Anyone encountered these kind of "random" problems? How are your lists on smartphones?

rbosneag avatar Jun 18 '15 11:06 rbosneag

@rbosneag I used @youanswer's timeout workaround, but getting the timeout duration right on devices on low-speed internet is tricky. I ended up tracking the load events of all images in the masonry bricks and triggering a reload when all the images have loaded. It's a crude way of triggering a reload, but it seems to do the trick on all devices.

A directive to call a function on load event

angular.module('sbLoad', []) //Credit: http://stackoverflow.com/a/26781900/293847
  .directive('onImgLoad', ['$parse', function ($parse) {
    return {
      restrict: 'A',
      link: function (scope, elem, attrs) {
        var fn = $parse(attrs.onImgLoad);
        elem.on('load', function (event) {
          scope.$apply(function() {
            fn(scope, { $event: event });
          });
        });
      }
    };
  }]);

on-img-load in HTML

 <div masonry reload-on-show class="artists-container">
        <div class="artist-wrapper masonry-brick" ng-repeat="artist in artists">
            <img class="artist" ng-src="{{artist.image}}" on-img-load="notifyImgLoad($index)"/>
            <h2 class="name">{{artist.name}}</h2>
        </div>
    </div>

Calling masonry.reload when all images have loaded

var loadedCount = 0;
$scope.notifyImgLoad = function(index) {
     loadedCount++;
     if (loadedCount === $scope.artists.length) {
       console.log('Triggering reload');
       $rootScope.$broadcast('masonry.reload');
     }
   };

Plunkr here

vinaygopinath avatar Jul 25 '15 21:07 vinaygopinath

@vinaygopinath: I previously tried a solution similar to yours but it didn't work well, it appears that your code solves the problem (disregarding the flickering in the page).

Now the problem is with the Back button: if you click an artist (or change the route to anything) and then go back, the onLoad event is fired immediately because the images are cached so the masonry.refresh event is emitted sooner than it should. I might add a $timeout but this gets us back to where we started.

Do you have an issue on Back as well?

rbosneag avatar Aug 18 '15 12:08 rbosneag

I'm also experiencing the images being overlapped when changing the view, then clicking Back. For me, it only happens if I don't specify a transitionDuration. Even a value of 0 solves the issue. No idea why, haven't looked much through the source code yet.

cristiandreica avatar Nov 21 '15 15:11 cristiandreica

It works for me when I clicked on the tab it loads masonry with this code.

setTimeout(function () {
      $rootScope.$broadcast('masonry.reload');
      $scope.$apply();
}, 100);

umrtariq avatar Sep 25 '18 18:09 umrtariq