jQuery and Greensock animations -- animate all elements on screen with classname - javascript

I'm trying to animate all elements that match the class "customers" on my page once they are scrolled into view.
My current version:
$(window).on('scroll', function() {
if(!$('.customers').hasClass('animated')) {
if($('.customers').isOnScreen(0.45, 0.45)) {
TweenMax.staggerTo($('.customer'), 0.3, {bottom:"+=50px", opacity:1, ease: Power2.easeOut}, 0.15);
$('.customers').addClass('animated');
}
}
})
Works to animate the first element when it enters the screen, and unfortunately as a result also animates the others while they are offscreen. What I want to happen is that each element that matches "customers" animates when it is scrolled into view.
(note that isOnScreen is a custom function that does element detection within the window).
I've tried using jquery's each function as such:
$(window).on('scroll', function() {
$('.customers').each(function( i ) {
if(!this.hasClass('animated')) {
if(this.isOnScreen(0.45, 0.45)) {
TweenMax.staggerTo($('.customer'), 0.3, {bottom:"+=50px", opacity:1, ease: Power2.easeOut}, 0.15);
this.addClass('animated');
}
}
})
And Ive also tried wrapping each "this" statement as a jquery element as $(this).
I'm getting unexpected behavior in that the elements conitnue to animate as I scroll, even though they should have had their "animated" class removed (I want them to animate only the first time they enter the screen).
What I'm thinking I may need to do is create an array of customers, and then perform the TweenMax to each element in the array, but I'm not sure if this will work.

All right here is what I think you needed to do.
There had to be a listener for the scroll event on the window object to begin with in the first place of course. Stating the obvious.
Then you loop through the .customers elements using an each.
Then you check if each of those .customers elements already have a animated class on them. If, they do, then nothing happens but if they don't, the rest follows.
The current .customers element is then checked if it is in a defined viewport area using the custom function .isonScreen().
Then TweenMax animates .customer elements found within the current .customers element that we are currently looping through. Notice the difference between .customers, the parent element, and the child .cusotmer elements. Remember, we are inside a loop, so each of the .customers element is looped through and then we further try to find .customer elements within each of them. The jQuery that helps us find inner .customer elements is: $(this).find('.customer').
Next up, in your CSS, opacity: 0; line was previously commented out for .customer elements. I uncommented it back on.
We then use .staggerFromTo method of TweenMax to define a set of initial properties to begin our tween with, and end of another set of properties, all with a little bit of stagger between the animations so they don't just come up all the same time, neither do they wait for each other to finish before the next one plays. It is an overlapping animation.
Another thing to note here is that we are animating y property of the elements which is a special property provided by TweenMax which is basically a short-cut to animate translateY(...) property as if you did that using CSS.
Finally, you apply the .animated class on the currently looped .customers element.
JavaScript:
$(window).on('scroll', function() {
$('.customers').each(function() {
if (!$(this).hasClass('animated')) {
if ($(this).isOnScreen(0.45, 0.45)) {
TweenMax.staggerFromTo($(this).find('.customer'), 0.3, {
y: 200,
opacity: 0
}, {
y: 0,
opacity: 1,
ease: Power2.easeOut
}, 0.15);
$(this).addClass('animated');
}
}
});
});
Here is the fiddle. Hope this helps.

Related

fadeTo two objects

I want to make an image on my site to opacity 0.5 and then i want to "pop out" on this image a word with opacity equal to 1. The problem is that when i set image on whatever speed it starts to queue all functions as many times as i enter/leave image with my cursor.
the word is loading faster (immadietly) than image with 'slow' and
the word doesn't change opacity to 1, cus it's loosing opacity with
image (which is set to 0.5)
How to make "word" to load later than image?
How to make function not repeatable whenever I'm moving my cursor through image (like 10x/sec) so it won't queue everything and
continue to fadeTo until everything is done?
(the most important) How to make word to not inherit opacity from image?
$(document).ready(function(){
$('#classic').mouseenter(function(){
$('#classic').fadeTo('slow', 0.25)
{
$('#classic').append("Classic");
$("Classic").fadeTo('slow', 1);
};
});
$('#classic').mouseleave(function(){
$('#classic').fadeTo('slow', 1);
{
$('#classic').empty();
};
});
});
First of all, make sure that either (a) the element that has id="classic" is not an img--because imges should not have children--or (b) you are adding the text after the img and not appending it to it. I'm going to assume that there's a div with id="classic" which has an img child:
1) You have it almost right. To make the word fade in after the image has faded out you need to use a callback function. The syntax for this is:
$('#classic').fadeTo('slow', 0.25, function() {
// now the fading is done!
});
And not
$('#classic').fadeTo('slow', 0.25)
{
....
}
You'll also probably want to add opacity: 0 for an initial value on the text that you add to the DOM so you can then fade it in.
2) jQuery has a function just for this called .stop(). Call this function whenever you need to stop animations from queuing up. So change the above to:
$('#classic').stop().fadeTo('slow', 0.25, function() {
// now the fading is done!
});
3) In CSS all elements inherit opacity from their parent. The easiest fix for this is simply to make the added element a sibling (or any other DOM element) of the transparent element and not a child. Then use negative relative positioning to stick it over the transparent element. So, assuming this structure:
<div id="classic">
<img src="myPicture" />
</div>
you'd use this jQuery:
$('#classic img').stop().fadeTo('slow', 0.25, function() {
$('#classic').append("<span>Classic</span>");
$("#classic span").fadeTo('slow', 1);
});
Make only the image fade out and in, so its siblings are unaffected. And in CSS:
#classic span {
display: inline-block;
opacity: 0; /* make it 0 initially so we can fade it in */
position: relative;
top: -48px; /* or whatever value */
left: -88px; /* or whatever value */
}
So here's the combined jQuery:
$('#classic').mouseenter(function () {
$('#classic img').stop().fadeTo('slow', 0.25, function() {
$('#classic').append("<span>Classic</span>");
$("#classic span").fadeTo('slow', 1);
});
});
$('#classic').mouseleave(function () {
$('#classic img').stop().fadeTo('slow', 1, function() {
$('#classic span').remove();
});
});
Here's a JSFiddle. Hope this helps!
I've updated your code here: http://jsfiddle.net/3dytbr3m/1/
You should use classes on your buttons. Classes are identifiers that can be applied on multiple elements. That way you don't have to use double code.
I also use $(this) which means that you are targeting the element that has the mouse over.

Add item to top of list and scroll down

I feel this should be simple, but I can't get my head round how to do it.
I want to add new items to the top of a list, and have the list scroll down to reveal the new item.
The closest I've got so far is here: http://jsfiddle.net/philgyford/cmsh0zoz/4/ This creates and adds a new list item by doing:
var content = 'Hello';
if (Math.random() < 0.3) {
content += '<br/>Another line';
};
$('<li>').html(content)
.hide()
.prependTo( $('ul') )
.slideDown('slow', function() {
// Remove final element:
$('ul').find('li:gt(9)').remove();
});
That reveals the new item by having it slideDown, which isn't quite what I want - I want it to be as if the new item has appeared behind the "Test list" heading, and then the whole list slides down to reveal it.
Note that list items have varying heights, which complicates things slightly.
Instead of using slideDown, considering animating the ul's top margin, starting with the negative height of the newly-added element (including padding).
First, add this to your h1 style:
position: relative;
That effectively puts the h1' on top of the ul. (See css positioning z-index negative margins for why we can't use z-index for this.)
You can then animate the ul like this:
function scroll() {
var content = 'Hello';
if (Math.random() < 0.3) {
content += '<br/>Another line';
};
$('ul').prepend('<li>'+content)
.css('margin-top',-$('li').first().outerHeight(true))
.delay(1000)
.animate({'margin-top':0},
function() {
$(this).find('li:gt(9)').remove();
scroll();
}
);
};
Calling the scroll function after the animation is finished may be better than using setInterval, because it guarantees that scrolling won't occur until after the previous scroll. The delay keeps it from running continuously.
Fiddle at: http://jsfiddle.net/w0gnzqx6/8/

Choosing Parent Height With white-space:nowrap

So I have these DIVs which I have arranged to slide left an right inside of the parent.
See the following JSFiddle to see the design:
http://jsfiddle.net/StevP/C9WL7/
You can see that by adjusting the margin-left of the first child DIV by multiples of -100%, it's rather simple to correctly horizontally position the DIVs inside the parent. Therefore, it's very easy to animate.
Now, this brings me to my issue. I'm using jQuery to move them left and right. It works great. However, I'd like to choose which child the parent gets its height from.
I know, I can just add...
$('#parent').height($('.child:eq()').outerHeight());
...Which is what I have it currently doing. However, the contents of the children are likely to change causing them to resize (by animate) and, therefore, be cut off. So, having a set height isn't a possibility.
I need to use height:auto; on the parent and somehow cause it to ignore the heights of specific children. I can't for the life of me think of a way.
I don't want to use a timer and onresize/.resize() don't seem to work with my Chrome.
You could use jQuery to monitor the DOM subtree and adjust the height of your parent div in the callback like this:
$('.content').bind('DOMSubtreeModified', function(e) {
if (e.target.innerHTML.length > 0) {
$(".parent").height($(".content").height());
}
});
Here's a working jsfiddle: http://jsfiddle.net/9386d/
And a question explaining the dom subtree: jQuery watch for domElement changes?
jQuery docs for bind(): http://api.jquery.com/bind/
Well... To be perfectly honest I'm not really a huge fan of jQuery anymore so I feel bad offering this answer. It just feels so frik'n inefficient, but here is a solution that does three things: 1) it resizes the hight of the container on step and uses a CSS transition attribute for eye candy (works just as well without). 2) it sets the child height of all but the current child to 0 and uses overflow:hidden so they don't affect the flow of the document anymore. 3) it resets these children to automatic height on animation start so they are visible during transition. All I can say is "yuck", but it does work.
CSS
.child{
...
overflow:hidden;
}
jQuery
var animation_prefs = {
duration: 3000,
start: function() {
$('.child').height('auto');
},
step: function(now) {
var current_index = (Math.floor((now + 50) / 100) * -1);
$('#parent').height($('.child:eq(' + current_index + ')').outerHeight());
$('#parent').data('current', current_index);
},
complete: function() {
$('#parent').height('auto');
$('.child:not(:eq('+$('#parent').data('current')+'))').height(0);
}
}
$('.child:eq(0)').animate(
{
marginLeft:'-200%' //~ Move it back 2 children
},
animation_prefs
).animate(
{
marginLeft:'-100%' //~ Move it back 1 child
},
animation_prefs
).animate(
{
marginLeft:'-200%' //~ Move it back 2 children again
},
animation_prefs
);
Demo
http://jsfiddle.net/Gq4xs/show
Source
http://jsfiddle.net/Gq4xs/

Can't get hidden image to display with .show()

I'm using the vimeo api to slide a video off the screen after it finishes playing. Underneath the video player, hidden, I have an image that says 'replay'. The image though is slightly bigger than the player so I want to hide the image via .hide() or display: none in the css and then show it after the animation of the video player completes.
Here's my js:
$(document).ready(function() {
$(".vimeo-container img").hide();
$('iframe.vimeo').each(function(){
Froogaloop(this).addEvent('ready', ready);
});
function ready(playerID){
Froogaloop(playerID).addEvent('finish', onFinish);
}
function onFinish(playerID) {
var player = "#" + playerID;
$(player).animate({width: "0%"}, 750, function() {
$(player).next("img").show();
});
}
});
So the first line is hiding the image. And then when the onFinish function completes I'm trying to show() the image, but it won't work. I should note that when I reverse it and do:
$(player).next("img").hide();
it works.
Here's my HTML:
%section#container1
.row.video-left
.large-8.columns
.vimeo-container
.flex-video.widescreen.vimeo
%iframe.vimeo#player1{allowfullscreen: "", frameborder: "0", height: "225", mozallowfullscreen: "", src: "http://player.vimeo.com/video/60122989?api=1&player_id=player1", webkitallowfullscreen: "", width: "400"}
= image_tag "behind1.png", class: "behind1"
And CSS:
.vimeo-container {
position: relative;
.behind1 {
margin-top: -27em;
}
I've also tried setting display: none in the css, but that wont work either. Not sure what I'm missing.
Thanks.
EDIT
function onFinish(playerID) {
var player = "#" + playerID;
$(player).animate({width: "0%"}, 750, function() {
console.log($(player));
$(player).next().show();
});
}
When I log out ($(player) it returns:
And when I log out console.log($(player).next()); it logs out the image that I am trying to show.
According to the jQuery documentation on the animate method here:
Note: Unlike shorthand animation methods such as .slideDown() and .fadeIn(), the .animate() method does not make hidden elements visible as part of the effect. For example, given $( "someElement" ).hide().animate({height: "20px"}, 500), the animation will run, but the element will remain hidden.
I had a similar need in a project and what worked for me there was to set the z-index of the element I wanted to hide to be less than that of the background. Then, when I wanted to show (or, in your case, animate) I could apply the jQuery methods to the element as if they were hidden (by increasing the z-index so that the element becomes visible), yet not incur the undefined behaviour of attempting to manipulate a hidden element.
Another option would be to move the element off the screen by way of a negative (x, y) coordinate and work from there. I'm not sure which visually would be more appealing in your use case but mention it for completeness.

jQuery - delete element if exists body area

I am using jQuery animate to change the position of multiple elements on the page (decorative elements). I want the element to be deleted if it exits body area. (if left is larger than body width or top is larger than body height).
The following can not be used in my case:
overflow hidden for the body
manually animating the element. I want to use jQuery animate to keep it simple (I dinamically create elements, animate them then I don't care about them, don't keep track of them, they have a .remove() methode when the animation is complete)
http://jsfiddle.net/a7Nck/
So in this JSfiddle I want the red div to dissappear when it reaches right edge of the body so that no scrollbars will appear.
Isn't there any CSS3 media query for example so that if a div is not in the viewport it will be hidden?
EDIT:
I just thought of a solution: get the width of the body prior to triggering the animation and then animate the top and left by using the minimum between the body size and what the animation should do. The problem is that this will affect animation speed.
You can use animate's step function.
The second version of .animate() provides a step option — a callback function that is fired at each step of the animation. This function is useful for enabling custom animation types or altering the animation as it is occurring. It accepts two arguments (now and fx), and this is set to the DOM element being animated.
var w = $(window).width()
$('div').animate({
left: '20px'
}, 500).delay(1000).animate({
left: '2000px'
}, {
step: function(now, fx) {
if (now > w) {
$(fx.elem).remove()
}
}
}, 1000);
Fiddle
One possible workaround (assuming your first restriction is about globally adding overflow: hidden on your CSS): add overflow: hidden with js when the animation starts, and remove it when it ends:
$('body').css('overflow', 'hidden');
$('div').animate({left: '20px'},400).delay(1000).animate(
{left: '2000px'},1000, false, function(){
$(this).hide();
$('body').css('overflow', 'auto');
});​
http://jsfiddle.net/a7Nck/3/
Just hide the div when the last animation get completed i.e.
$('div').animate({left: '20px'},400).delay(1000).animate({left: '2000px'},1000, function(){ $(this).hide(); });
See DEMO
As soon as the div leaves the visible area, the scrollbar will appear. If you want to prevent the scrollbar from appearing, you can't allow the div to leave the screen. Here's my solution:
$('div')
.animate({left: '20px'},400)
.delay(1000)
.animate(
{left: ($(window).width()-$('div').width()) + "px"},
1000,
null, // default easing
function() { $('div').hide() }
);
Jsfiddle: http://jsfiddle.net/j4rdA/1/
​

Categories