I'm trying to animate the backgroundPositionY of an <div> element. What I try to do is to animate it linearly. Every step should be in between a fixed range.
For example:
step1: backgroundPositionY: 0 (192*0)
step2: backgroundPositionY: 192 (192*1)
step3: backgroundPositionY: 384 (192*2)
... and so on
My current code looks like the following:
$curtains = $("#unshredder-curtains");
$curtains.data('backgroundPositionY', -3456);
$curtains.animate({
backgroundPositionY: 3648
}, {
duration: 2000,
easing: "linear",
step: function(now, fx) {
var newTop = Math.floor(now / 192) * 192;
$(fx.elem).css('backgroundPositionY', newTop+'px');
}
});
But that does nothing. It just animates the background as it usually does. The steps are just ignored.
Does anyone know how to do that special animation?
Thanks :)
I'd probably do something like:
var step = 192;
setInterval(function(){
$('#unshredder-curtains').css('backgroundPositionY', function(i, val){
var bgPosY = (parseInt(val) + step) + 'px';
$(this).html('New position: ' + bgPosY);
return bgPosY;
});
}, 100);
You can see it in action here: http://jsfiddle.net/pr7cc/
Related
I'm simulating a bowling game in jQuery. So, I throw the ball (through animate function) and when it hits a pin I simulate it falling over (also through animate function). But I can only make it rotate. I want it to fall over in place, to the left or to the right. Like this. This is the jQuery code for the pin animation:
this.animation = function (pinID) {
var rdm = ((Math.random()*10)+1);
var degree;
if (rdm <= 5)
degree = -90;
else
degree = 90;
$(pinID).animate({ deg: degree },
{
duration: 1000,
step: function (now) {
// In the step-callback (that is fired each step of the animation),
// the pin will rotate to the current degree of the animation
$(pinID).css({
transform: 'rotate(' + now + 'deg)'
});
},
complete: function () {
// When the animation is complete, removes the pin from play
$(pinID).css({
"left": -200, "top": -200
});
}
});
}
I tried to have a left and top in the beginning of the animate function the it just simulated a weird movement.
I'd appreciate it if anyone could help me. Thanks.
EDIT Here's the result (thanks to Zs V):
this.animation = function (pinID) {
if (knockedPins.indexOf(pinID) == -1) {
var rdm = ((Math.random() * 10) + 1);
var degree;
var transFormOrigin;
if (rdm <= 5) {
degree = -90;
transFormOrigin = '0% 100%';
}
else {
degree = 90;
transFormOrigin = '100% 100%';
}
$(pinID).css({ "transform-origin": transFormOrigin });
knockedPins.push(pinID);
$(pinID).animate({ deg: degree },
{
duration: 1000,
step: function (now) {
// In the step-callback (that is fired each step of the animation),
// the pin will rotate to the current degree of the animation
$(pinID).css({
transform: 'rotate(' + now + 'deg)'
});
},
complete: function () {
// When the animation is complete, removes the pin from play
$(pinID).css({
"left": -200, "top": -200
});
}
});
}
}
I added the transform-origin property and had to make use of an array to store the ids of the pins that were already knocked, otherwise it would shake until it was put to rest. hehe
You have to rotate the object (the picture) around its corner instead of the center. Here it is a discussion how to do that: https://stackoverflow.com/a/6633676/6624619
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... :)
This is my code:
http://jsfiddle.net/7cXZj/
var callback = function () {
$('.progress-bar').width($('.progress-bar').parent().width() - 190);
$(".mainpart-background").animate({ width: "80%" }, 800 , function(){
var sidepartposition = $(".progress-bar").width() * 0.1 + $(".sidepart-content").width() * 0.5 ;
$(".sidepart").animate({ "margin-right": - sidepartposition }, 100);
});
};
$(document).ready(callback);
$(window).resize(callback);
var sidepartpositionResize = $(".progress-bar").width() * 0.1 + $(".sidepart-content").width() * 0.5 ;
$(window).resize(function(){
$(".sidepart").css( "margin-right", "sidepartpositionResize" );
});
This is the problem:
The span showing "20%" disappears when you resize the window. Why? Inspecting it with Firebug you will see jQuery won't stop calculate the 80%, it goes 80.00213 to 79.1241 to 79.12523 ... and so on. Suddenly after 1-4 seconds it's done with this strange process. Then the span contents the 20% appears.
Please note: This code should work on responsive websites.
I'm a JS beginner. Thank you so much for help!
Try this code: http://jsfiddle.net/7cXZj/5/
This code lets you bind to the end of the resize event and not executing the function many times during the resize of the window.
var animate = function(){
console.log('animando');
$(".mainpart-background").css('width', 0);
$(".mainpart-background").animate({
width: "80%"
}, 800, function () {
var sidepartposition = $(".progress-bar").width() * 0.1 + $(".sidepart-content").width() * 0.5;
$(".sidepart").animate({
"margin-right": -sidepartposition
}, 10);
}
);
}
window.resizeEvt;
$(document).ready(function(){
animate();
$(window).resize(function()
{
clearTimeout(window.resizeEvt);
window.resizeEvt = setTimeout(function()
{
animate();
}, 250);
});
});
Hope it helps.
I have 5 divs layered and a object in the foreground I want to move across them. This is using the paralax effect. I have been sucessfully able to move the object using basic .animate in jQuery.
The problem I'm having is getting the background divs to animate properly - or at all. What happens is when I click on my trigger div - the div.cloud1 and div.cloud2 move BEFORE my object does. They also change positions despite my playing with the timing values.
All objects in the divs are absolutely positioned - the divs are relative for being able to use z-index.
Specifically I'm trying to move div.cloud1, div.cloud2, div.ground, div.Mountain all at different speeds so it gives the illusion of 3d.
The object I'm sending across is a different div.
I'm not sure what the problem is.
Here is my JSfiddle: http://jsfiddle.net/U6Mu6/
jQuery(document).ready(function () {
jQuery('#cloud-01').css({
backgroundPosition: '50 -180px'
});
jQuery('#cloud-02').css({
backgroundPosition: '0 -100px'
});
jQuery('#mountains-03').css({
backgroundPosition: '0 50px'
});
jQuery('#trees-04').css({
backgroundPosition: '0 50px'
});
jQuery('#ground').css({
backgroundPosition: 'left bottom'
});
jQuery('#branding').css({
backgroundPosition: 'center 0'
});
jQuery('#content').css({
backgroundPosition: 'center 0'
});
jQuery('#sec-content').css({
backgroundPosition: 'center 0'
});
jQuery('#footer').css({
backgroundPosition: 'center 0'
});
jQuery('#wrapper').css({
overflow: "hidden"
});
jQuery('#klicker').click(function () {
jQuery('#cloud-01').animate({
backgroundPosition: '(-100px -10px)'
}, 200000);
jQuery('#cloud-02').animate({
backgroundPosition: '(-400px 0px)'
}, 20000);
jQuery('#mountains-03').animate({
backgroundPosition: '(-2500px 50px)'
}, 20000);
jQuery('#ground').animate({
backgroundPosition: '(-5000px bottom)'
}, 20000);
startHim();
jQuery("#full-robot").animate({
left: "50%",
marginLeft: "-150px"
}, 2000);
setTimeout("leaveScreen()", 15000);
});
});
var num = 1;
function startHim() {
num++;
jQuery("#sec-content").animate({
top: "-=5px"
}, 150).animate({
top: "+=5px"
}, 150);
jQuery("#content,#branding").animate({
top: "-=" + num + "px"
}, 150).animate({
top: "+=" + num + "px"
}, 150);
if (num < 4) {
setTimeout("startHim()", 300);
} else {
setTimeout("bounceHim()", 300);
}
}
function bounceHim() {
jQuery("#sec-content,#branding").animate({
top: "-=4px"
}, 150).animate({
top: "+=4px"
}, 150);
jQuery("#content").animate({
top: "-=8px"
}, 150).animate({
top: "+=8px"
}, 150);
setTimeout("bounceHim()", 300);
}
function leaveScreen() {
jQuery("#full-robot").animate({
left: "100%",
marginLeft: "0px"
}, 2000);
}
Just FYI - some of the objects in the fiddle are not included on purpose. I just want to get things working first.
I did see a error in JSFIDDLE dealing with implied eval on my setTime expression. But I'm not sure how to fix it. I suppose I could pass the div as function and use .hide instead.
All help is welcome thanks!
EDIT:::
I forgot this:
/**
* v. 1.02
*/
(function($) {
$.extend($.fx.step,{
'background-position': function(fx) {
if (fx.state === 0 && typeof fx.end == 'string') {
var start = $.curCSS(fx.elem,'background-position');
start = toArray(start);
fx.start = [start[0],start[2]];
var end = toArray(fx.end);
fx.end = [end[0],end[2]];
fx.unit = [end[1],end[3]];
}
var nowPosX = [];
nowPosX[0] = ((fx.end[0] - fx.start[0]) * fx.pos) + fx.start[0] + fx.unit[0];
nowPosX[1] = ((fx.end[1] - fx.start[1]) * fx.pos) + fx.start[1] + fx.unit[1];
fx.elem.style.backgroundPosition = nowPosX[0]+' '+nowPosX[1];
function toArray(strg){
strg = strg.replace(/left|top/g,'0px');
strg = strg.replace(/right|bottom/g,'100%');
strg = strg.replace(/([0-9\.]+)(\s|\)|$)/g,"$1px$2");
var res = strg.match(/(-?[0-9\.]+)(px|\%|em|pt)\s(-?[0-9\.]+)(px|\%|em|pt)/);
return [parseFloat(res[1],10),res[2],parseFloat(res[3],10),res[4]];
}
}
});
})(jQuery);// JavaScript Document
I don't know if this is too obvious, but your trying to set the "background-position"attribute of the clouds by using backgroundPosition
You might just change them to
$("#cloud-01").css({'background-position': '50px -180px'})
Notice the background-position instead of backgroundPosition
If you want to stagger the time each cloud takes to move, you need to offset your animation durations, like
$('#cloud-01').animate({
'background-position' : '(-100px -10px)'
}, (1000) ); // 1 second duration
$('#cloud-02').animate({
'background-position' : '(-400px 0px)'
}, (2000) ); // 2 seconds
$('#mountains-03').animate({
'background-position' : '(-2500px 50px)'
}, (2000) ); // 3 seconds
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