I have a few elements on my page which I'm loading 5 at a time.
What I need to do is to animate the loaded elements using animate.css.
To explain this, I've created this fiddle:
https://jsfiddle.net/89v781rL/6/
Scroll down to see the Show more button and click on it to see it in action.
When i load more items, the elements are animated but because they are layed out in a Masonry style
, the position of them keep changing which is not what i want. Basically I need to keep the elements in their first position and keep adding/loading more elements.
If you comment out the following CSS code and run the JSFIDDLE, you will see how nicely the elements are layed out and that is how i want them to be when they are loading 5 at a time too:
#grid li:nth-child(n+6) {
/*display: none;*/
}
what I need to do is to bounce the elements UP using animate.css.
This is my full code:
$('#sales').click(function() {
$('#grid li').not(':visible').slice(0, 5).slideDown();
$('#grid li').not(':visible').slice(0, 5).addClass('animated bounceInUp');
});
Note: I don't want to use any plugins like this
Mainly because it is very big and bloated for my project and it doesn't function properly in my project either.
Could someone please advise on this issue?
EDIT:
Here is another failed attempt to do this:
https://jsfiddle.net/89v781rL/8/
And this one too:
https://jsfiddle.net/89v781rL/9/
TL;DR: Proposed solution
.
Simply put, what you want is not possible. At least not how you started it.
But, here's the thing: the reason I got into coding (coming from design background - and also the main reason I answered your question) is because a bunch of web developers told me about a particular design I made, that it's not possible. So I (re)searched and experimented until I found a way.
Ever since, whenever I come to this conclusion, I always translate it to:
It's possible, I'm just doing it wrong™.
In your particular case, you're using a "masonry" technique that's not exactly masonry. It's a column layout technique, called CSS columns
. Here's what it does, from a technical point of view:
break-inside
declarations. Note the suggestive name of the property value: avoid
, it's not forbid
or disallow
. Based on contents, this rule doesn't always apply.But, in short, here's how your 2 columns layout renders initially:
1 4
2 5
3 6
When you decide to add more content, it will go through the steps above again, considering the updated content height resulting in:
1 5
2 6
3 7
4
It will calculate possible breaking options and will go for the one that results in the smallest height for the parent element. If two or more result in the same height, most browsers will choose the one that makes the later/last column shorter.
If your content needs to stay into place once rendered, the CSS Columns
technique is clearly not an option. So you're looking at techniques using absolute positioning. You have many options, but the notable ones (I'm probably subjective - but is anyone really objective?) are:
Masonry
. It comes with the very handy stamp
method, specifically designed for your requirement (stick existing elements into place, so adding more content will not re-position existing elements - which could happen occasionally, depending on contents and parent - for example, if you have a list of existing short items and you add a very tall one, it would result in a column much taller than the rest; if stamp wasn't called on existing items, they will be re-positioned so that resulting columns have minimal differences in height)Isotope
- I've never had the chance to use it, but I heard good things about it.javascript
and absolute positioning (just like Masonry) but is incredibly efficient. He explains how he did it in this SO answer
and here's a good tutorial
on the technique. If you don't care much about the technical part and are only interested in the juice, I found a light plugin, called Bootstrap Waterfall
which, apart from everything linked above, has a production ready usable version.Everything I wrote so far is what a bit of time and decent search-fu would have gotten you.
The only step left (and the real answer) would be animating each item into view using the required animate.css
effect. I chose Bootstrap-waterfall
for the layout, but you could as well go with any of the other options. Here it is:
// Included waterfall script as it doesn't load from github for everyone
+function(t){"use strict";function i(i){this.$pins=i,this.tasks=[],this.timerId=null,this.deferred=new t.Deferred}function e(t){this.img=t,this.initialWidth=t.width,this.initialHeight=t.height}function n(i){return this.each(function(){var e=t(this),n=e.data("mystist.waterfall"),s="object"==typeof i&&i;n&&"string"!=typeof i&&n.destroy()&&(n=null),n||e.data("mystist.waterfall",n=new o(this,s)),"string"==typeof i&&n[i]()})}var s=s||{now:Date.now||function(){return(new Date).getTime()},throttle:function(t,i,e){var n,o,r,a=null,h=0;e||(e={});var l=function(){h=e.leading===!1?0:s.now(),a=null,r=t.apply(n,o),a||(n=o=null)};return function(){var u=s.now();h||e.leading!==!1||(h=u);var c=i-(u-h);return n=this,o=arguments,0>=c||c>i?(a&&(clearTimeout(a),a=null),h=u,r=t.apply(n,o),a||(n=o=null)):a||e.trailing===!1||(a=setTimeout(l,c)),r}},debounce:function(t,i,e){var n,o,r,a,h,l=function(){var u=s.now()-a;i>u&&u>=0?n=setTimeout(l,i-u):(n=null,e||(h=t.apply(r,o),n||(r=o=null)))};return function(){r=this,o=arguments,a=s.now();var u=e&&!n;return n||(n=setTimeout(l,i)),u&&(h=t.apply(r,o),r=o=null),h}}},o=function(i,e){this.$element=t(i),this.options=t.extend({},o.DEFAULTS,e),this.id=Math.random().toString().slice(2),this.$fakePin=null,this.$container=null,this.$pins=null,this.pinWidth=null,this.imgWidth=null,this.lefts=[],this.tops=[],this.init().calculateWidth().calculatePosition().sail(),t(window).on("resize.mystist.waterfall"+this.id,s.debounce(t.proxy(function(){t(window).off("scroll.mystist.waterfall"+this.id),this.calculateWidth().calculatePosition().ship(r.getLoadedPins.call(this))},this),777))};o.VERSION="0.2.4",o.DEFAULTS={},o.prototype.init=function(){return this.initPins().initAttributes(),this},o.prototype.initPins=function(){var i=this.$element.children().length>0?this.$element.children().remove():t(this.$element.data("bootstrap-waterfall-template"));return i.each(function(){var i=t(this).find("img:eq(0)");i.length>0&&(t(this).data("bootstrap-waterfall-src",i.attr("src")),i.attr("src","data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="))}),this.$pins=i,this},o.prototype.initAttributes=function(){return this.$fakePin=this.$pins.first().clone(),this.$container=t("<div />").css("position","relative"),this.$element.html(this.$container),this},o.prototype.calculateWidth=function(){var t=this.$fakePin.clone();return this.$container.append(t.css("opacity",0)),this.pinWidth=t.outerWidth(!0),this.imgWidth=t.find("img:eq(0)").css("width","100%").width(),t.remove(),this},o.prototype.calculatePosition=function(){for(var t=parseInt(this.$container.width()/this.pinWidth,10),i=[],e=[],n=0;t>n;n++)i.push(n*this.pinWidth),e.push(0);return this.lefts=i,this.tops=e,this},o.prototype.sail=function(){var e=r.getToLoadPins.call(this),n=new i(e);return n.load().run().deferred.done(t.proxy(function(){this.ship(e)},this)),this},o.prototype.ship=function(i){return this.render(i).updateHeight(),t(window).on("scroll.mystist.waterfall"+this.id,s.throttle(t.proxy(function(){r.isWantMore.call(this)&&(t(window).off("scroll.mystist.waterfall"+this.id),this.sail())},this),500)),this},o.prototype.render=function(i){var e=this;return i.each(function(){e.placePin(t(this))}),this},o.prototype.placePin=function(t){var i=a.indexOf(this.tops,Math.min.apply(null,this.tops)),e=r.getPosition.call(this,i);return t.css({position:"absolute",left:e.left,top:e.top}),t.data("bootstrap-waterfall-pin")&&r.setImageHeight.call(this,t),t.data("bootstrap-waterfall-src")&&(r.makeImageAvailable.call(this,t),t.removeData("bootstrap-waterfall-src")),this.$container.append(t),r.updatePosition.call(this,i,t),this},o.prototype.updateHeight=function(){var t=a.indexOf(this.tops,Math.max.apply(null,this.tops));return this.$container.height(this.tops[t]),this},o.prototype.destroy=function(){return t(window).off("scroll.mystist.waterfall"+this.id),t(window).off("resize.mystist.waterfall"+this.id),this.$element.empty().removeData("mystist.waterfall"),this};var r={getToLoadPins:function(){var i=parseInt(this.$container.width()/this.pinWidth,10),e=3*i,n=this.$pins.map(function(){return t(this).find("img").length>0&&t(this).data("bootstrap-waterfall-src")?t(this):void 0});return n.slice(0,e)},getLoadedPins:function(){var i=this.$pins.map(function(){return t(this).find("img").length>0&&!t(this).data("bootstrap-waterfall-src")?t(this):void 0});return i},isWantMore:function(){return t(window).scrollTop()+t(window).height()>a.getDocHeight()-377?!0:!1},getPosition:function(t){var i={left:this.lefts[t],top:this.tops[t]};return i},setImageHeight:function(t){var i=t.data("bootstrap-waterfall-pin"),e=this.imgWidth*i.img.height/i.img.width;t.find("img:eq(0)").css({height:e,width:"auto"})},makeImageAvailable:function(t){t.find("img:eq(0)").attr("src",t.data("bootstrap-waterfall-src"))},updatePosition:function(t,i){this.tops[t]+=i.outerHeight(!0)}};i.prototype.load=function(){var i=this;return this.$pins.each(function(){var n=new Image;n.src=t(this).data("bootstrap-waterfall-src");var s=new e(n);i.tasks.push(s),t(this).data("bootstrap-waterfall-pin",s)}),this},i.prototype.run=function(){return this.timerId=setInterval(t.proxy(function(){this.isDone()?this.stop():this.check()},this),40),this},i.prototype.isDone=function(){return 0===this.tasks.length?!0:!1},i.prototype.stop=function(){clearInterval(this.timerId),this.timerId=null,this.deferred.resolve()},i.prototype.check=function(){for(var t=0;t<this.tasks.length;t++){var i=this.tasks[t];i.isLoaded()&&this.tasks.splice(t--,1)}},e.prototype.isLoaded=function(){return this.img.width!==this.initialWidth||this.img.height!==this.initialHeight||this.img.width*this.img.height>1024?!0:!1};var a={getDocHeight:function(){var t=document;return Math.max(t.body.scrollHeight,t.documentElement.scrollHeight,t.body.offsetHeight,t.documentElement.offsetHeight,t.body.clientHeight,t.documentElement.clientHeight)},indexOf:function(t,i){if(null==t)return-1;for(var e=0,n=t.length;n>e;e++)if(t[e]===i)return e;return-1}},h=t.fn.waterfall;t.fn.waterfall=n,t.fn.waterfall.Constructor=o,t.fn.waterfall.noConflict=function(){return t.fn.waterfall=h,this}}(jQuery);
// note waterfall should normally be linked as resource in your project
$('#waterfall').data('bootstrap-waterfall-template', $('#waterfall-template').html());
let wf = $('#waterfall').waterfall();
let fader = {
wh: $(window).height(),
full: function() {
$('#waterfall .pin').each(function(i, e) {
(function(i, e) {
setTimeout(function() {
fader.check(e)
}, i * 150);
})(i, e)
})
},
check: function(e) {
if (fader.wh > e.getBoundingClientRect().top + 60) {
$(e).addClass('inView');
setTimeout(function() { $(e).addClass('fix') }, 750)
}
},
resize:function(){
fader.wh = $(window).height();
fader.full();
},
light: function() {
let fst = $('#waterfall .pin:not(".inView")').eq(0);
if (fst.is('.pin')) {
fader.check(fst[0])
}
}
};
setTimeout(function() { fader.full() }, 210);
$(window)
.on('scroll', fader.light)
.on('resize', _.throttle(fader.resize,
500,{leading:false,trailing:true})
);
#waterfall .pin {
width: calc(50% - 4px);
opacity: 0;
animation-duration: 0.75s;
animation-fill-mode: both; }
#waterfall .pin.inView {
opacity: 1;
animation-name: bounceInUp; }
#waterfall .pin.inView.fix {
animation: none; }
#waterfall .pin a {
display: block;
padding: 4px 4px 8px; }
* {
box-sizing: border-box; }
.container {
text-align: center; }
.container #waterfall {
max-width: 800px;
margin: 0 auto; }
@keyframes bounceInUp {
from, 60%, 75%, 90%, to {
animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); }
from {
opacity: 0;
transform: translate3d(0, 3000px, 0); }
60% {
opacity: 1;
transform: translate3d(0, -20px, 0); }
75% {
transform: translate3d(0, 10px, 0); }
90% {
transform: translate3d(0, -5px, 0); }
to {
transform: translate3d(0, 0, 0); }
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/lodash/4.17.4/lodash.min.js"></script>
<script id="waterfall-template" type="text/template">
<div class="pin">
<a href="javascript:;">
<img src="https://tympanus.net/Development/GridLoadingEffects/images/1.jpg" />
</a>
</div>
<div class="pin">
<a href="javascript:;">
<img src="https://tympanus.net/Development/GridLoadingEffects/images/2.jpg" />
</a>
</div>
<div class="pin">
<a href="javascript:;">
<img src="https://tympanus.net/Development/GridLoadingEffects/images/3.jpg" />
</a>
</div>
<div class="pin">
<a href="javascript:;">
<img src="https://tympanus.net/Development/GridLoadingEffects/images/4.jpg" />
</a>
</div>
<div class="pin">
<a href="javascript:;">
<img src="https://tympanus.net/Development/GridLoadingEffects/images/5.jpg" />
</a>
</div>
<div class="pin">
<a href="javascript:;">
<img src="https://tympanus.net/Development/GridLoadingEffects/images/6.jpg" />
</a>
</div>
<div class="pin">
<a href="javascript:;">
<img src="https://tympanus.net/Development/GridLoadingEffects/images/7.jpg" />
</a>
</div>
<div class="pin">
<a href="javascript:;">
<img src="https://tympanus.net/Development/GridLoadingEffects/images/8.jpg" />
</a>
</div>
<div class="pin">
<a href="javascript:;">
<img src="https://tympanus.net/Development/GridLoadingEffects/images/9.jpg" />
</a>
</div>
<div class="pin">
<a href="javascript:;">
<img src="https://tympanus.net/Development/GridLoadingEffects/images/10.png" />
</a>
</div>
<div class="pin">
<a href="javascript:;">
<img src="https://tympanus.net/Development/GridLoadingEffects/images/11.png" />
</a>
</div>
<div class="pin">
<a href="javascript:;">
<img src="https://tympanus.net/Development/GridLoadingEffects/images/12.png" />
</a>
</div>
<div class="pin">
<a href="javascript:;">
<img src="https://tympanus.net/Development/GridLoadingEffects/images/2.jpg" />
</a>
</div>
<div class="pin">
<a href="javascript:;">
<img src="https://tympanus.net/Development/GridLoadingEffects/images/6.jpg">
</a>
</div>
<div class="pin">
<a href="javascript:;">
<img src="https://tympanus.net/Development/GridLoadingEffects/images/1.jpg" />
</a>
</div>
<div class="pin">
<a href="javascript:;">
<img src="https://tympanus.net/Development/GridLoadingEffects/images/3.jpg" />
</a>
</div>
<div class="pin">
<a href="javascript:;">
<img src="https://tympanus.net/Development/GridLoadingEffects/images/2.jpg" />
</a>
</div>
<div class="pin">
<a href="javascript:;">
<img src="https://tympanus.net/Development/GridLoadingEffects/images/4.jpg" />
</a>
</div>
<div class="pin">
<a href="javascript:;">
<img src="https://tympanus.net/Development/GridLoadingEffects/images/5.jpg" />
</a>
</div>
<div class="pin">
<a href="javascript:;">
<img src="https://tympanus.net/Development/GridLoadingEffects/images/6.jpg" />
</a>
</div>
<div class="pin">
<a href="javascript:;">
<img src="https://tympanus.net/Development/GridLoadingEffects/images/7.jpg" />
</a>
</div>
<div class="pin">
<a href="javascript:;">
<img src="https://tympanus.net/Development/GridLoadingEffects/images/8.jpg" />
</a>
</div>
<div class="pin">
<a href="javascript:;">
<img src="https://tympanus.net/Development/GridLoadingEffects/images/9.jpg" />
</a>
</div>
<div class="pin">
<a href="javascript:;">
<img src="https://tympanus.net/Development/GridLoadingEffects/images/10.png" />
</a>
</div>
<div class="pin">
<a href="javascript:;">
<img src="https://tympanus.net/Development/GridLoadingEffects/images/11.png" />
</a>
</div>
<div class="pin">
<a href="javascript:;">
<img src="https://tympanus.net/Development/GridLoadingEffects/images/12.png" />
</a>
</div>
<div class="pin">
<a href="javascript:;">
<img src="https://tympanus.net/Development/GridLoadingEffects/images/2.jpg" />
</a>
</div>
<div class="pin">
<a href="javascript:;">
<img src="https://tympanus.net/Development/GridLoadingEffects/images/6.jpg">
</a>
</div>
<div class="pin">
<a href="javascript:;">
<img src="https://tympanus.net/Development/GridLoadingEffects/images/1.jpg" />
</a>
</div>
<div class="pin">
<a href="javascript:;">
<img src="https://tympanus.net/Development/GridLoadingEffects/images/2.jpg" />
</a>
</div>
<div class="pin">
<a href="javascript:;">
<img src="https://tympanus.net/Development/GridLoadingEffects/images/3.jpg" />
</a>
</div>
<div class="pin">
<a href="javascript:;">
<img src="https://tympanus.net/Development/GridLoadingEffects/images/4.jpg" />
</a>
</div>
<div class="pin">
<a href="javascript:;">
<img src="https://tympanus.net/Development/GridLoadingEffects/images/5.jpg" />
</a>
</div>
<div class="pin">
<a href="javascript:;">
<img src="https://tympanus.net/Development/GridLoadingEffects/images/6.jpg" />
</a>
</div>
<div class="pin">
<a href="javascript:;">
<img src="https://tympanus.net/Development/GridLoadingEffects/images/7.jpg" />
</a>
</div>
<div class="pin">
<a href="javascript:;">
<img src="https://tympanus.net/Development/GridLoadingEffects/images/8.jpg" />
</a>
</div>
<div class="pin">
<a href="javascript:;">
<img src="https://tympanus.net/Development/GridLoadingEffects/images/9.jpg" />
</a>
</div>
<div class="pin">
<a href="javascript:;">
<img src="https://tympanus.net/Development/GridLoadingEffects/images/10.png" />
</a>
</div>
<div class="pin">
<a href="javascript:;">
<img src="https://tympanus.net/Development/GridLoadingEffects/images/11.png" />
</a>
</div>
<div class="pin">
<a href="javascript:;">
<img src="https://tympanus.net/Development/GridLoadingEffects/images/12.png" />
</a>
</div>
<div class="pin">
<a href="javascript:;">
<img src="https://tympanus.net/Development/GridLoadingEffects/images/2.jpg" />
</a>
</div>
<div class="pin">
<a href="javascript:;">
<img src="https://tympanus.net/Development/GridLoadingEffects/images/6.jpg">
</a>
</div>
<div class="pin">
<a href="javascript:;">
<img src="https://tympanus.net/Development/GridLoadingEffects/images/1.jpg" />
</a>
</div>
<div class="pin">
<a href="javascript:;">
<img src="https://tympanus.net/Development/GridLoadingEffects/images/3.jpg" />
</a>
</div>
<div class="pin">
<a href="javascript:;">
<img src="https://tympanus.net/Development/GridLoadingEffects/images/2.jpg" />
</a>
</div>
<div class="pin">
<a href="javascript:;">
<img src="https://tympanus.net/Development/GridLoadingEffects/images/4.jpg" />
</a>
</div>
<div class="pin">
<a href="javascript:;">
<img src="https://tympanus.net/Development/GridLoadingEffects/images/5.jpg" />
</a>
</div>
<div class="pin">
<a href="javascript:;">
<img src="https://tympanus.net/Development/GridLoadingEffects/images/6.jpg" />
</a>
</div>
<div class="pin">
<a href="javascript:;">
<img src="https://tympanus.net/Development/GridLoadingEffects/images/7.jpg" />
</a>
</div>
<div class="pin">
<a href="javascript:;">
<img src="https://tympanus.net/Development/GridLoadingEffects/images/8.jpg" />
</a>
</div>
<div class="pin">
<a href="javascript:;">
<img src="https://tympanus.net/Development/GridLoadingEffects/images/9.jpg" />
</a>
</div>
<div class="pin">
<a href="javascript:;">
<img src="https://tympanus.net/Development/GridLoadingEffects/images/10.png" />
</a>
</div>
<div class="pin">
<a href="javascript:;">
<img src="https://tympanus.net/Development/GridLoadingEffects/images/11.png" />
</a>
</div>
<div class="pin">
<a href="javascript:;">
<img src="https://tympanus.net/Development/GridLoadingEffects/images/12.png" />
</a>
</div>
<div class="pin">
<a href="javascript:;">
<img src="https://tympanus.net/Development/GridLoadingEffects/images/2.jpg" />
</a>
</div>
<div class="pin">
<a href="javascript:;">
<img src="https://tympanus.net/Development/GridLoadingEffects/images/6.jpg">
</a>
</div>
</script>
<div class="container">
<div id="waterfall"></div>
</div>
Its main advantage (and why I chose this route) is it lazy-loads the images just before they scroll into view, which means you no longer need a button to load more. You can just put all the images in the template <script>
tag (read the plugin docs - scroll to Q&A - to see why it recommends adding markup like that).
I've also singled out the BounceInUp
animation from animate.css
so you don't need to load it.
Note the CSS
needs prefixing and, if you want it, the fiddle
has the SCSS
.
As a general rule I tried to keep it as light as possible, especially in terms of javascript
listeners (on scroll
I'm only listening to the position of the next item in the list, not to all).
Intended as proof of concept, for pointing in the right direction.
I know what I did is not exactly what you asked (in the sense it doesn't allow you to add more items after first calculation - unfortunately, Bootstrap Waterfall doesn't currently have an -update()
or addItems()
method and I really think they should add one, without recalculating existing pins
This is no longer true: Mystist, author of bootstrap-waterfall.js
has responded to my github request
and added the method. I now updated the jsFiddle
by adding an add more button to it and it works as expected. Huge thumb up for Mystist from me). Of course, you will probably need to adapt the script to your project's particular case, to how you're bringing the new items in. In the jsFiddle I chose to make a function that generates a new <script>
template on the fly and adds pins randomly to it.
Regardless, here are the principles to follow with this layout:
fix
class).scroll
and check if the next not-visible item has entered view. Animate it when it does and .fix
it after end of animation.I don't think you will be able to achieve this effect using the column-count css because of the way it calculates the columns. When you set 'display:none;' on an item the positioning of the items in columns behaves as if it didn't exist. It seems the columns are also taking into account the height of the element.
My suggestion is could you change your approach and rather use your own wrappers for columns and your own javascript/jquery?
Anyway looking at this problem and searching around I found that your question appears to be a duplicate on here and here is the answer (similar conclusion to mine but there's a clever workaround too): CSS column-count elements jumping across columns
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With