How to make JQuery wait inside a loop in rotation animation - javascript

I have a circle and in it a percentage text, starts with 0%. Once I hover the circle it goes from 0 to 100% (with another effect around the circle). As for now, the percentage goes from 0 to 100 directly and I want it to show the progress (0,1,2...,99,100) but I can't manage to make JQuery wait between each for iteration.
This is what I've tried: JSFiddle demo.
Note: My code works with chrome for now.
That's one iteration:
function actions(i){
var box = $('#box');
box.css('transform','rotate(' + i + ' deg)');
box.css('-ms-transform','rotate(' + i + 'deg)');
box.css('-webkit-transform','rotate(' + i + 'deg)');
prec = (100*(i + 135))/360;
$("div.prec").delay(100).html(Math.round(prec)+"%");
}
I understand that delay() needs to be queued and html() is not queued so I already tried setTimeout, but that it didn't work either. I also tried setInterval - see the next code snippet:
setInterval(function () {
$("div.prec").html(Math.round(prec)+"%");
},100);
To be more clear, I want the percentage to fit the effect progress - if the triangle that goes around travels half of the way, the percentage should be 50, and so, when I'm not hovering the circle anymore it should gradually go back to 0.

First, remove the surplus transition: all 1s css rule! This is making trouble in all the solutions.
1. Educational - using setTimeout
The for loop will not work as you expect, javascript is not build for active-wait loop like this:
for (var i = -135; i < 225; i++){
actions(i);
sleep(some time);
}
You have to use timeout and callbacks. Disable the .delay function call and rewrite your for loop to iterative setTimeout callback as shown here:
function loopit(dir, i){
if (typeof i == "undefined")
i = -135;
if (i >= 225)
return;
actions(i);
setTimeout(function () {
loopit(dir, i + 1);
}, 1);
}
The back-rotation would be written analogically - you can write it yourself as a homework :-)
http://jsfiddle.net/NNq3z/10/
2. Easy - using jQuery .animate()
The easiest way is to use jQuery .animate() function, that will do the animation "loop" with timing for you. To animate the percent text, use progress callback. Animating rotation is tricky though, you need to use special trick:
$({ deg: deg_from } ).animate({
deg: deg_to
}, {
duration: 1000,
progress: function (animation, progress) {
$("div.prec").html(Math.round(progress*100)+"%");
},
step: function(now) {
$('#box').css({
transform: 'rotate(' + now + 'deg)'
});
}
});
http://jsfiddle.net/q9VXC/1/

http://jsfiddle.net/NNq3z/13/
var i = -135,box = $("#box"),prec;
setTimeout(function(){
if($("#circle").is(":hover"))
loopit("c");
else
loopit("nc");
},1);
function loopit(dir){
if (dir=="c")
i++;
else
i--;
if(i<-135)
i=-135;
if(i>225)
i=225;
prec = (100*(i + 135))/360;
$(".prec").html(Math.round(prec)+"%");
box.css("transform","rotate("+i+"deg)")
.css("-ms-transform","rotate("+i+"deg)")
.css("-moz-transform","rotate("+i+"deg)")
.css("-webkit-transform","rotate("+i+"deg)");
setTimeout(function(){
if($("#circle").is(":hover"))
loopit("c");
else
loopit("nc");
},1);
}
Removed transition:all 1s.
In your code, the percentage of the box immediately change to 100% because you use for loop. For loop will be executed very fast as if it immediately change to 100% so it is not recommended.

You should use setInterval, and you have to increment percentage each time:
var percentage = 0;
var timer = setInterval(function() {
percentage++;
if (percentage > 100) {
clearInterval(timer);
} else {
$("div.prec").html(percentage + "%");
}
}
You could also call actions() inside there.

Related

Using Velocity.js to animate progressbar

I'm trying to switch from using jQuery animation to Velocity as it's supposed to have better performance across devices. From the documentation it seems like it should be fairly easy to do- I've downloaded the source code and added it to my js folder and in my function I should just be able to switch .animate() to .velocity(). Still not working though and I have no console log errors. (Also keep in mind that it worked before with jQuery animate. I've also tried downloading the src code and using the CDNs with no luck)
Here's the code:
// PROGRESSBAR
var show_complete_time = 2000; // time to show completed green progressbar and 100% text
function progressbar(progressbar, time) {
var progressbar = $(progressbar);
$({someValue: 0}).velocity({someValue: 99}, {
duration: time,
easing: 'linear',
step: function() {
var widthNumber = this.someValue;
var number = Math.floor(this.someValue+1);
progressbar.css({"width": widthNumber + "%"});
progressbar.html("<p>" + number + '%' + "</p>");
},
complete: function(){ // progressbar completed
progressbar.css({"width": "100%"});
progressbar.addClass("complete").html("<p>100%</p>");
}
});
};
It turns out the way the jQuery animate function was written wasn't in the correct format for velocity. Velocity takes a map of CSS properties and values as its first argument. An options object can optionally be passed in as a second argument.
Here is the new Velocity function:
// PROGRESSBAR
var show_complete_time = 2000; // time to show completed green progressbar and 100% text
function progressbar(progressbar, time) {
var progressbar = $(progressbar);
progressbar.velocity({"width": "100%"}, {
duration: time,
easing: "linear",
progress: function(elements, c, r, s, t) {
var number = Math.floor((c * 100) + 1);
progressbar.html("<p>" + number + '%' + "</p>");
},
complete: function(){ // progressbar completed
progressbar.addClass("complete").html("<p>100%</p>");
}
});
};

jQuery .Animate Opacity and .FadeOut/In Both Not Working Inside SetInterval

I trying to make what appears to the user to be an image fader. A string of images fade into each other. All the solutions that I found were complex, and normally required an for every image. I've come up with what should be a simple solution. It's working 90% on Firefox/Chrome/IE11 on Windows. On Android Chrome it's having issues.
Basically my idea is, I have two divs, absolutely positioned, one on top of the other. Both start with a background, sized to cover. The top one fades out, revealing the bottom one, and at the end of the animation, the background-image of the top one (current hidden) is changed to image 3. After a pause, it fades back in, and the background-image of the bottom one is changed to image 4. This repeats indefinitely.
HTML:
<div class="slideshow" id="slideshow-top"></div>
<div class="slideshow" id="slideshow-bottom"></div>
CSS:
.slideshow {
display:block;
background-size:cover;
width: 100%;
height: 100%;
position:absolute;
top:0;
left:0;
}
#slideshow-top {
z-index:-5;
background-image:url(http://www.andymercer.net/wp-content/uploads/2013/12/slider-1.jpg);
}
#slideshow-bottom {
z-index:-10;
background-image:url(http://www.andymercer.net/wp-content/uploads/2013/12/slider-2.jpg);
}
Javascript:
var url_array = [
'http://www.andymercer.net/wp-content/uploads/2013/12/slider-1.jpg',
'http://www.andymercer.net/wp-content/uploads/2013/12/slider-2.jpg',
'http://www.andymercer.net/wp-content/uploads/2013/12/slider-3.jpg',
'http://www.andymercer.net/wp-content/uploads/2013/12/slider-4.jpg',
'http://www.andymercer.net/wp-content/uploads/2013/12/slider-5.jpg',
'http://www.andymercer.net/wp-content/uploads/2013/12/slider-6.jpg',
'http://www.andymercer.net/wp-content/uploads/2013/12/slider-7.jpg',
'http://www.walldoze.com/images/full/2013/12/04/wallpapers-desktop-winter-nature-x-wallpaper-backgrounds-natureabstract-designs-interesting-hd-19045.jpg'
];
var count = 1;
setInterval(function() {
if (count%2) { // Fade In
jQuery('#slideshow-top').animate({opacity:0}, '200000', function() {
jQuery('#slideshow-top').css('background-image','url('+url_array[count]+')');
});
}
else { //Fade Out
jQuery('#slideshow-top').animate({opacity:1}, '200', function() {
jQuery('#slideshow-bottom').css('background-image','url('+url_array[count]+')');
});
}
count = (count == url_array.length-1 ? 0 : count + 1);
}, 2000);
http://jsfiddle.net/5eXy9/
As seen in the Fiddle above, this mostly works. However, it seems to ignore the length of the animation. Using .fadeOut has the same effect. I've tried going from 200 to 20000, and there doesn't seem to be a difference.
I'm not sure if this is tied into the other issue, which is that on Android (Galaxy S4, Chrome, Android 4.x), the animation doesn't occur at all. It simply changes images. Any ideas?
EDIT: Jan 10 - Timing problem is fixed, but the main issue (Android) is still unsolved. Any thoughts?
The interval keeps going, so when increasing the animation speed, you have increase the interval speed as well.
The way you've built this, you should always keep the speed of both animations equal to the interval, or if you need a delay, increase the interval compared to the animations so it at least has a higher number than the highest number used in the animations.
The reason changing the speed doesn't work at all for you, is because it should be integers, not strings, so you have to do
jQuery('#slideshow-top').animate({opacity:0}, 200000, function() {...
// ^^ no quotes
I would do something like this
var url_array = [
'http://www.andymercer.net/wp-content/uploads/2013/12/slider-1.jpg',
'http://www.andymercer.net/wp-content/uploads/2013/12/slider-2.jpg',
'http://www.andymercer.net/wp-content/uploads/2013/12/slider-3.jpg',
'http://www.andymercer.net/wp-content/uploads/2013/12/slider-4.jpg',
'http://www.andymercer.net/wp-content/uploads/2013/12/slider-5.jpg',
'http://www.andymercer.net/wp-content/uploads/2013/12/slider-6.jpg',
'http://www.andymercer.net/wp-content/uploads/2013/12/slider-7.jpg',
'http://www.walldoze.com/images/full/2013/12/04/wallpapers-desktop-winter-nature-x-wallpaper-backgrounds-natureabstract-designs-interesting-hd-19045.jpg'];
var count = 1;
var speed = 2000,
delay = 1000;
$.each(url_array, function(source) { // preload
var img = new Image();
img.src = source;
});
setInterval(function () {
if (count % 2) { // Fade In
jQuery('#slideshow-top').animate({
opacity: 0
}, speed, function () {
jQuery('#slideshow-top').css('background-image', 'url(' + url_array[count] + ')');
});
} else { //Fade Out
jQuery('#slideshow-top').animate({
opacity: 1
}, speed, function () {
jQuery('#slideshow-bottom').css('background-image', 'url(' + url_array[count] + ')');
});
}
count = (count == url_array.length - 1 ? 0 : count + 1);
}, speed + delay);
FIDDLE

jquery rotate callback not called if image not rotated

I have this piece of code rotating an image and then moving it - which works fine unless the image does not rotate - meaning the current angle of the image is equal to the angle it should rotate to - in this case the callback function is not called, so there is no animation. The rotate function comes from a plugin.
$("#Ship" + shipID).rotate({
duration: 500,
angle: $(this).getRotateAngle(),
animateTo: parseInt(rotate),
callback: function () {
$("#" + shipID).animate({
left: parseInt(moveX),
top: parseInt(moveY)
}, 500, function () {
});
}
})
If the animate function is written after the rotate - then the rotate and animation happen at the same time.
I don't think I can force the callback as I would have to edit the plugin (but im not sure about that). Is there a way to write the animate after the rotate, but wait for rotate to finish? I guess one way would be is to use delay, but is there a more proper way?
You have to check the rotation before you try to execute anything with the plugin. I'm not exactly sure how you'll check the current rotation, but:
var currentRotation = $("#Ship" + shipID).getRotateAngle();
if(currentRotation != rotate) {
$("#Ship" + shipID).rotate({
duration: 500,
angle: $(this).getRotateAngle(),
animateTo: parseInt(rotate),
callback: function () {
$(this).rotate({
animateTo: rotate
});
$("#" + shipID).animate({
left: parseInt(moveX),
top: parseInt(moveY)
}, 500);
}
});
}
Depending on your exact setup and how the plugin works, you may need to check your currentRotation inside of the plugin method. Or, you may need to set up some events to execute the if statement. Again, this would depend on how you're brinding this all together (how do you know when you want something to rotate?)

overflow: hidden; image scrollBy

I had an idea for like a bus window as a fixed frame, about 800px wide, with a parallax city with the content on billboards spaced out so when you scroll between them it allows the parallax to look like bus is moving. The content will be much bigger than the window like a sprite and I'll put forward and back buttons that will scrollBy (x amount, 0). I have a working parallax script and a rough cityscape of 3 layers that all work fine.
I have hit a wall. I am trying to clear a scrollBy animation after it scrolls 1000px. Then you click it again and it goes another 1000px. This is my function.
function scrollForward() {
window.scrollBy(5,0);
scrollLoop = setInterval('scrollForward()',10);
}
So far I can only clear it when it gets to 1000. I tried doing 1000 || 2000 ect but after the first one it goes really fast and won't clear.
Excelsior https://stackoverflow.com/users/66580/majid-fouladpour wrote a great piece of code for someone else with a different question. It wasn't quite right for what the other guy wanted but it is perfect for me.
function scrollForward() {
var scrolledSoFar = 0;
var scrollStep = 75;
var scrollEnd = 1000;
var timerID = setInterval(function() {
window.scrollBy(scrollStep, 0);
scrolledSoFar += scrollStep;
if( scrolledSoFar >= scrollEnd ) clearInterval(timerID);
}, 10);
}
function scrollBack() {
var scrolledSoFar = 0;
var scrollStep = -75;
var scrollEnd = -1000;
var timerID = setInterval(function() {
window.scrollBy(scrollStep, 0);
scrolledSoFar += scrollStep;
if( scrolledSoFar <= scrollEnd ) clearInterval(timerID);
}, 10);
}
Now for step two figuring out how to put this content animation behind a frame.
Not quite sure what your asking here. Perhaps you could provide more relevant code?
I do see a potential issue with your code. You call setInterval('scrollForward()', 10) which will cause scrollForward to be called every 10ms. However, each of those calls to scrollForward will create more intervals to scrollForward creating a sort of explosion of recursion. You probably want to use setTimeout or create your interval outside of this function.
Also, as an aside you can change your code to simply: setInterval(scrollForward, 10). Removing the quotes and the parens makes it a littler easier to read and manager. You can even put complex, lambda functions like:
setInterval(function() {
scrollForward();
// do something else
}, 10);
edit:
So if you know that scrollForward moves the item 10px, and you want it to stop after it moves the item 1000px, then you simply need to stop it has moved that much. I still don't know how your code is actually structured, but it might look something like the following:
(function() {
var moved_by = 0;
var interval = null;
var scrollForward = function() {
// move by 10px
moved_by += 10;
if (moved_by === 1000 && interval !== null) {
clearInterval(interval);
}
};
var interval = setInterval(scrollForward, 10);
})();
If you want to clear it after 1000 or 2000, you simply adjust the if statement accordingly. I hope that helps.

JavaScript animation

I am trying to animate a div moving 200px horizontally in JavaScript.
The code below makes it jump the pixels, but is there a way to make it look animated without using jQuery?
function () {
var div = document.getElementById('challengeOneImageJavascript');
div.style.left = "200px";
}
Here is a basic animation setup:
function animate(elem,style,unit,from,to,time) {
if( !elem) return;
var start = new Date().getTime(),
timer = setInterval(function() {
var step = Math.min(1,(new Date().getTime()-start)/time);
elem.style[style] = (from+step*(to-from))+unit;
if( step == 1) clearInterval(timer);
},25);
elem.style[style] = from+unit;
}
To use:
animate(
document.getElementById('challengeOneImageJavascript'),
"left","px",0,200,1000
);
This example will animate the given element to slide linearly from 0px to 200px over a time of 1 second (1000 ms).
You can easily do this through CSS3-Transition :
#challengeOneImageJavascript {
-webkit-transition: left .2s;
-moz-transition: left .2s;
-o-transition: left .2s;
transition: left .2s;
}
Though, it is not supported by IE9 and earlier browser versions.
I did a ton of research, and I finally learned how to do it really well.
I like to place my program in a window.onload function, that way it dosn't run the code until the page has finished loading.
To do the animation, make a function(I'll call it the draw function) and call it what ever you want except reserved words, then at the very end of the draw function call the requestAnimationFrame function and give it the name of the function to be called next frame.
Before the requestAnimationFrame function can be used it must be declared.
See the code below:
window.onload = function() {
function draw() { // declare animation function
context.fillStyle = "white";
context.fillRect(0, 0, 400, 400);
requestAnimationFrame(draw); // make another frame
}
var requestAnimationFrame = // declare the
window.requestAnimationFrame || // requestAnimationFrame
window.mozRequestAnimationFrame || // function
window.webkitRequestAnimationFrame ||
window.msRequestAnimationFrame;
draw(); // call draw function
}
Note: Nothing after the line that calls the draw function will run, so you need to put everything you want to run before the line that calls the draw function.
You would have to use a javascript timeout function, and change the css value a little at a time. The easiest way would be to increment by a set amount each time until a threshold is reached, which would give you a linear animation, which would look clunky and amateurish compared to jQuery's default swing animation which follows a bezier curve approximately like an s-curve.
Untested code should do the linear animation
var lefty = 0;
var animate = function(){
lefty += 20;
var div = document.getElementById('challengeOneImageJavascript');
div.style.left = lefty +"px";
if(lefty < 200)
setTimeout(animate(),100);
}
animate()
n.b. there are lots of improvements to make to that block of code, but it should get you going...
With JavaScript, you will have to use setInterval function or this is how it can be done in jQuery:
$('#challengeOneImageJavascript').animate({left: '=-5'});
Adust value (5) as per your needs as well as direction via =- or =+
With Vanilla JavaScript:
var interval;
var animate = function(id, direction, value, end, speed){
var div = document.getElementById(id);
interval = setInterval(function() {
if (+(div.style) === end) {
clearInterval(interval);
return false;
}
div.style[direction] += value; // or -= as per your needs
}, speed);
}
And you can use it like:
animate('challengeOneImageJavascript', 'left', 5, 500, 200);
To stop animation any time, you would do:
clearInterval(interval);
Note: This just a very quick way to do it to give you an idea.
Simplest way via css.
https://jsfiddle.net/pablodarde/5hc6x3r4/
translate3d uses hardware acceleration running on GPU.
http://blog.teamtreehouse.com/increase-your-sites-performance-with-hardware-accelerated-css
HTML
<div class="movingBox"></div>
CSS
.movingBox {
width: 100px;
height: 40px;
background: #999;
transform: translate3d(0,0,0);
transition: all 0.5s;
}
.moving {
transform: translate3d(200px,0,0);
background: #f00;
}
JavaScript
const box = document.getElementsByClassName('movingBox')[0];
setTimeout(() => {
box.className += ' moving';
}, 1000);
CustomAnimation is a small libary for animating html elements which is written in pure js.You can use this libary.

Categories