I have got a menu on my homepage and on hover I would like them to enlarge. This is exactly what I have achieved, except there is one flaw:
When I move off before the animation ends, the option stops the animation and subtracts 30 from the width that left off from the previous animation. So it always intersects with the other animation and causes false results.
Example:
I move quickly to menu option 1, it only expands little - let's say by 10px - while I am on it, and as I move off the width decreases by 30px, which is more than the previously moved 10px, which results in a smaller button overall.
I would like to somehow capture how much it has moved during the mouseover animation and only decrease the width in the leaving function by that amount. Or, of course some other easy solution, if there is one...
Here's the code:
$('.menu_option').hover(
function() {
var w = $(this).width()+30+"";
$(this).stop().animate({ width:w}, 150, 'easeOutQuad');
}, function() {
var w = $(this).width()-30+"";
$(this).stop().animate({ width:w}, 150, 'easeOutQuad');
});
What you can do is make another variable which is the origin width then when you put it back go back to the origin:
js:
var o = $('.menu_option').width();
$('.menu_option').hover(function () {
var w = $(this).width() + 30 + "";
$(this).stop().animate({
width: w
}, 150, 'easeOutQuad');
}, function () {
$(this).stop().animate({
width: o
}, 150, 'easeOutQuad');
});
http://jsfiddle.net/Hive7/qBLPa/6/
You need to complete the previous animation before the width is calculated
$('.menu_option').hover(function () {
var $this = $(this).stop(true, true);
var w = $this.width() + 30;
$this.animate({
width: w
}, 150, 'easeOutQuad');
}, function () {
var $this = $(this).stop(true, true);
var w = $this.width() - 30 + "";
$this.animate({
width: w
}, 150, 'easeOutQuad');
});
Demo: Fiddle
Related
I am looking to animate this progress bars, but I'm having a different behavior using jQuery animate() method. I want to animate the progress bars one by one with a 0.1s delay. I will need help with choosing the right animation, because now my animation is behaving downwards. I'd like to do it in the simplest way possible.
Here is what I have so far:
$('.vertical-bars .progress-fill span').each(function() {
var percent = $(this).html();
var pTop = 100 - (percent.slice(0, percent.length - 1)) + "%";
$(this).parent().css({
'height': percent,
'top': pTop
});
$(this).parent().animate({
height: "toggle"
}, {
duration: 900,
specialEasing: {
height: "swing"
}
});
});
I have prepared a JSFiddle with my progress bars HERE.
The right behavior is to fill the progress-bars upwards, like in THIS example.
You need to animate both the height and top of your bars, or you need to construct them such that they are pinned to the horizontal axis when you change the height. The second takes a little more thought, but the first, although not as elegant, is straight forward.
To animate top you can't use toggle (as it is animating to 100% and back, not to 0), so you will need to animate both the shrink and grow separately using done to trigger a second animation. Taking the same style as you used above:
$('.vertical-bars .progress-fill span').each(function () {
var percent = $(this).html();
var pTop = 100 - ( percent.slice(0, percent.length - 1) ) + "%";
$(this).parent().css({
'height': percent,
'top': pTop
});
var self=this;
$(this).parent().animate({
height: 0,
top: "100%"
}, {
duration: 900,
specialEasing: {
height: "swing",
top: "swing"
},
done:function(){
$(self).parent().animate({
height: percent,
top: pTop
}, {
duration: 900,
specialEasing: {
height: "swing",
top: "swing"
}
})
}
});
});
Of course you can also achieve the same thing using css animation. If I have time to figure it out I'll post an edit.
I don't know what height: "toggle" does but you basically want to set 0 height and 100% offset from top and then start animation that adjusts both styles.
The 100ms between animations is done simply by using setTimeout and incrementing the timeout.
https://jsfiddle.net/kss1su0b/1/
var elm = $(this).parent();
elm.css({
'top': '100%',
'height': 0
});
setTimeout(function() {
elm.animate({
'height': percent,
'top': pTop
}, {
duration: 900,
specialEasing: {
height: "swing"
},
});
}, animOffset);
animOffset += 100;
The simplest and most performing way to invert the animation direction is to align the bars at the bottom with css:
vertical-bars .progress-fill {
position: absolute;
bottom: 0;
}
Then you don't need to set the top any more with jQuery and set a delay:
$('.vertical-bars .progress-fill').each(function (index) {
var percentage = $(this).find('.percentage').html();
$(this).delay(index * 100).animate(
{
height: percentage
}, {
duration: 900,
done: function () {
$(this).find('span').show("normal");
}
}
);
});
jsfiddle
Issue:
When I click the skill-set link for the first time, the animation occurs but does not occur when you scroll down. Each circle is to start it's own animation when the user scrolls down the page. If you click the skill-set link twice though, everything works as its supposed to.
So my question at hand is, why doesn't the animation on scroll occur on the first time the skill-set link is clicked?
Here is a DEMO of what I am talking about, please excuse the terrible layout. Once you click on the skill-set link, you see the animation happen, but when you scroll down, the animation is already completed...However, if you click the skill-set link twice, and then scroll down, you see each circle animate when you scroll down. This is what should happen on the first time the link is clicked, but for some odd reason it isn't.
JS:
$('#skill-set-link').click(function () {
function animateElements(index, element) { // (e, init)
if (element) { // (init)
$('.progressbar').data('animate', false);
}
$('.progressbar').each(function () {
var elementPos = $(this).offset().top;
var topOfWindow = $(window).scrollTop();
var percent = $(this).find('.circle').attr('data-percent');
var percentage = parseInt(percent, 10) / parseInt(100, 10);
var animate = $(this).data('animate');
if (elementPos < topOfWindow + $(window).height() + 10 && !animate) {
$(this).data('animate', true);
$(this).find('.circle').circleProgress({
startAngle: -Math.PI / 2,
value: percent / 100,
thickness: 2, // Change this for thickness
fill: {
color: '#16A085'
}
}).on('circle-animation-progress', function (event, progress, stepValue) {
$(this).find('.percent').text((stepValue * 100).toFixed(0) + "%"); // NOTE: Change '.toFixed(0)' to '.toFixed(1)' to get 1 decimal place to the right...
}).stop();
}
});
}
animateElements({}, true);
$('.about_body_wrapper').scroll(animateElements);
});
=========================================================================
Any idea as to why the animation on scroll doesn't occur the first time the link is clicked?
The behavior is occurring because everything in the skill-set-link DIV is still hidden when it runs the first time, so the top position of all of the progressbar elements is zero. Since they are zero, they are meeting the criteria of the if statement and the animation is being enabled on all of them.
To fix it, I added a call to show() the progressbar elements, including the parameter to run animateElements when show() is complete.
I moved the call to set "animate" to false to the menu item click function as it didn't really serve any purpose in animateElements. I also removed the animateElements function from the click event handler to simplify reading the code.
function animateElements(index, element) { // (e, init)
$('.progressbar').each(function () {
var elementPos = $(this).offset().top;
var topOfWindow = $(window).scrollTop();
var percent = $(this).find('.circle').attr('data-percent');
var percentage = parseInt(percent, 10) / parseInt(100, 10);
var animate = $(this).data('animate');
if (elementPos < topOfWindow + $(window).height() + 10 && !animate) {
$(this).data('animate', true);
$(this).find('.circle').circleProgress({
startAngle: -Math.PI / 2,
value: percent / 100,
thickness: 2, // Change this for thickness
fill: {
color: '#16A085'
}
}).on('circle-animation-progress', function (event, progress, stepValue) {
$(this).find('.percent').text((stepValue * 100).toFixed(0) + "%"); // NOTE: Change '.toFixed(0)' to '.toFixed(1)' to get 1 decimal place to the right...
}).stop();
}
});
}
$('#skill-set-link').click(function () {
$('.progressbar').data('animate', false);
$('#skill-set').fadeIn(animateElements);
});
$(window).scroll(animateElements);
Thanks to the help from Tony Hinkle - Here is the answer.
Due to the main div being hidden - we needed to show() the main div beforehand...However, adding $('#skill-set').show(0, animateElements); as suggested by Tony, didn't quite work right - so instead $('#skill-set').fadeIn(animateElements) replaced that along with taking out the 0 which seemed to do the trick.
Many thanks to Tony though for steering me in the right direction!
Here is the final snippet used to make this work as desired:
function animateElements(index, element) { // (e, init)
$('.progressbar').each(function () {
var elementPos = $(this).offset().top;
var topOfWindow = $(window).scrollTop();
var percent = $(this).find('.circle').attr('data-percent');
var percentage = parseInt(percent, 10) / parseInt(100, 10);
var animate = $(this).data('animate');
if (elementPos < topOfWindow + $(window).height() + 10 && !animate) {
$(this).data('animate', true);
$(this).find('.circle').circleProgress({
startAngle: -Math.PI / 2,
value: percent / 100,
thickness: 2, // Change this for thickness
fill: {
color: '#16A085'
}
}).on('circle-animation-progress', function (event, progress, stepValue) {
$(this).find('.percent').text((stepValue * 100).toFixed(0) + "%"); // NOTE: Change '.toFixed(0)' to '.toFixed(1)' to get 1 decimal place to the right...
}).stop();
}
});
}
$('#skill-set-link').click(function () {
$('.progressbar').data('animate', false);
$('#skill-set').fadeIn(animateElements);
});
$(window).scroll(animateElements);
And here is the final iteration: DEMO
Don't mind the layout... :)
I made a little function that allows to click on a text element which then flys (animated top/left offset with absolute position) to a specific location and disappears.
Here is a fiddle of the problem.
Here is my code from the click handler (in coffescript):
var hoveringSelection = $ "<div class='flying cm-variable'>#{selection}</div>"
var dropdownToggle = $ '#watchlist-dropdown'
hoveringSelection.css({
position: 'absolute'
top: window.mouse.y
left: window.mouse.x
display: 'block'
opacity: 1
})
.appendTo('body')
.animate({
top: dropdownToggle.offset().top
left: dropdownToggle.offset().left
opacity: 0.0
},
{
duration: 1500
easing: 'easeOutCubic'
complete: () ->
hoveringSelection.remove()
updateQueueSize()
}
as you can see it should be at opacity 0 and then removed. The problem is that it shows for a split second (with a ~50% chance) before it gets removed.
I tested it with alerts before the .remove() is called so that the javascript execution halts, but it still did it before the alert was executed. Therefore the issue has to appear right before the completion callback of animate() is called.
I could not observe such behaviour in Firefox.
How can I avoid it?
I have seen that this is a bug (http://www.brycecorkins.com/blog/jquery-fadein-opacity-bug-in-chrome-and-ie-8/). The problem is the opacity. I made a few changes to your script to get your goal. At the end of animation I set opacity to 0.01 and then on complete I execute function that remove the element. I hope that this help you.
http://jsfiddle.net/XjesX/1/
$(function () {
$.extend($.easing, {
easeOutCubic: function (x, t, b, c, d) {
return c * ((t = t / d - 1) * t * t + 1) + b;
}
});
var mouseListener = function (event) {
if (!window.mouse) window.mouse = {
x: 0,
y: 0
};
window.mouse.x = event.clientX || event.pageX;
window.mouse.y = event.clientY || event.pageY;
};
document.addEventListener('mousemove', mouseListener, false);
var fly = function() {
var hoveringSelection = $("<div class='flying'>A word</div>");
var dropdownToggle = $('#flytome');
hoveringSelection.css({
position: 'absolute',
top: window.mouse.y,
left: window.mouse.x,
display: 'block',
opacity: 1.0
})
.appendTo('body')
.animate({
top: dropdownToggle.offset().top,
left: dropdownToggle.offset().left,
opacity: 0.01
}, 1500, 'easeOutCubic' ,function(){
alert($('.flying').length);
$('.flying').remove();
alert($('.flying').length);
});
};
$('#flyBtn').click(fly);
});
I have added alert($('.flying').length); before and after remove to show that that element is removed from the DOM. If you remove that 2 lines you'll see in a better way that there is no flickering effect.
Given a div "square"
and given I already have a touchmove function on that div and I can detect the position X in real time:
how can I implement the rubber band effect?
I mean: tap and drag to the left until the resistance reach the limit and if ou release the finger the square div goes back to its initial position with an easing animation
there is a simple math for that? or a plugin?
UPDATE
w/o jquery would be better if possible
Store its original position somewhere.
Then on the dragend event:
$(this).animate({
top: original_top,
left: original_left
}, 'slow');
Demo: http://jsfiddle.net/maniator/T8zYt/
Full code (with jQuery draggable):
(function($) {
$.fn.rubber = function(resist) {
var self = this,
position = $(this).position(),
selfPos = {
top: position.top,
left: position.left,
maxTop: resist + position.top,
maxLeft: resist + position.left,
minTop: resist - position.top,
minLeft: resist - position.left
};
self.draggable({
drag: function() {
var position = $(this).position(), width = $(this).width(), height = $(this).height();
if (position.left > selfPos.maxLeft || (position.left - width) < selfPos.minLeft || position.top > selfPos.maxTop || (position.top - height) < selfPos.minTop) {
return false;
}
},
stop: function() {
$(this).animate({
top: selfPos.top,
left: selfPos.left
}, 'slow');
}
})
};
})(jQuery)
$('selector').rubber(10);
I have a list of items & they are holding images, each image is 800w x 600 H. The original div height is 800 W x 300 H. I figured out how to expand the div when it is clicked, but i want to know how to collapse it when you clicked it while it is already expanded. Right now i just expands the div even further
$('.expand').bind('click', function() {
var currHeight = $(this).css('height').replace(/px/,'');
currHeight = currHeight * 1;
var newHeight = currHeight + 500;
$(this).animate({
height: newHeight
},1000);
});
any idea on how to create an if else statement that would say, IF the div is already expanded then collapse on click, or if the div is collapse, then expand to # of px.
You can detect the current height and branch:
$('.expand').bind('click', function() {
var $this = $(this),
height = $this.height();
if (height > 500) {
height -= 500;
}
else {
height += 500;
}
$this.animate({
height: height
},1000);
});
I've done a couple of other things in there. You can use height rather than css('height') to get the value without units, and no need for the * 1 trick. I've also done the $(this) once and reused it, since there are multiple function calls and an allocation involved when you call the $() function. (It doesn't matter here, but it's a good habit to get into provided you're not caching it longer than you mean to [via a closure or such].)
Alternately, you can remember that you've done it another way (using the data feature):
$('.expand').bind('click', function() {
var $this = $(this),
height = $this.height(),
expanded = $this.data('expanded');
if (expanded) {
height -= 500;
}
else {
height += 500;
}
$this.data('expanded', !expanded);
$this.animate({
height: height
},1000);
});
Or combine those to store the original height in case it gets influenced by something else:
$('.expand').bind('click', function() {
var $this = $(this),
height = $this.height(),
prevHeight = $this.data('prevHeight');
if (prevHeight) {
height = prevHeight;
$this.data('prevHeight', undefined);
}
else {
$this.data('prevHeight', height);
height += 500;
}
$this.animate({
height: height
},1000);
});
Take your pick!
I would use CSS to set the height of the div in both original and expanded versions, and then when the div is clicked, toggle a class to change the height:
/* CSS for the height */
.expand {
height: 300px;
}
.expand.expanded {
height: 600px;
}
and then in the click method, just:
$(this).toggleClass("expanded");
A few pointers with jQ:
.click(function(){})
.css({height: "300*1px"}) // Why are you multiplying Anything by ONE?!
And you can use
if($(this).css("height") == "300px") {
Do stuff
} else {
Do other stuff
}
Edit: But other options above are far better.
You can check the .height() at the time of the click event and .animate() the height += or -= accordingly (something .animate() supports), like this:
$('.expand').click(function() {
$(this).animate({
height: $(this).height() > 300 ? "+=500px" : "-=500px"
}, 1000);
});
Or, use .toggle(), like this:
$('.expand').toggle(function() {
$(this).animate({ height: "+=500px" }, 1000);
}, function() {
$(this).animate({ height: "-=500px" }, 1000);
});
Pure Jquery, check the documentation Jquery Animate
$( "#clickme" ).click(function() {
$( "#book" ).animate({
height: "toggle"
}, {
duration: 5000,
specialEasing: {
height: "linear"
},
complete: function() {
//DO YOUR THING AFTER COMPLETE
}
});
});