Kind of custom animation - delay doesn't work - javascript

It is custom animation attempt, which works with background-position.
var i = 5;
while(i !== 0) {
setTimeout(function() {
$("#feedback-toad").css("height","+=50");
$("#feedback-toad").css("background-position", "50% "+i*(-20)+"%");
}, 1000);
i = i - 1;
}
It works in terms of final result - finally element looks like I want.
But it doesn't look like animation, it happens instantly - looks like delay (timeout) doesn't work.
Why? How I can fix that?
P.S. I don't need plugin suggestion - I would like to do it with my own code. Thanks.

You are setting all callbacks to run at the same time (1000 ms from now).
It should be something like this so it happens 1 animation per second:
var i = 5;
while (i !== 0) {
setTimeout(function () {
$("#feedback-toad").css("height", "+=50");
$("#feedback-toad").css("background-position", "50% " + i * (-20) + "%");
}, i * 1000);
i = i - 1;
}

Related

Number is not incrementing by 1 in JavaScript

I have this code where if the opacity is less than or equal to 0, the message number is suppose to go up by 1, but when I run the code, the message number increases by 77 or 152 or 66, etc. Could you help me?
My code:
//variables
var x = 0;
var opacity = 0;
var messageNumber = 0;
var talk1 = ["hello", "welcome to idle ball", "potato"];
var lol = 1;
//set opacity to 1
function opacitySet1(speed) {
document.getElementById("talk").style.opacity = opacity;
opacity += speed;
}
//set opacity to 0
function opacitySet0(speed) {
document.getElementById("talk").style.opacity = opacity;
opacity -= speed;
}
function IntervalManager(flag, animate, time, para1) {
if (flag) {
var intervalSet = setTimeout(animate, time, para1)
}
}
function IntervalManagerII(flag, animate, time, para1) {
if (flag) {
var intervalSetII = setTimeout(animate, time, para1)
}
}
//to delay time
function nothing() {}
function message(startPart) {
document.getElementById("talk").innerHTML = messageNumber;
if (opacity >= 0 && lol == 0) {
setTimeout(nothing, 1);
IntervalManagerII(true, opacitySet0, 300, 0.005);
IntervalManager(false)
}
if (opacity <= 1 && lol == 1) {
IntervalManager(true, opacitySet1, 300, 0.005);
IntervalManagerII(false)
}
if (opacity <= 0) {
lol = 1;
IntervalManagerII(false);
messageNumber += 1;
} //this is the part that is not working
if (opacity >= 1) {
lol = 0;
IntervalManager(false);
}
};
setInterval(function() {
message(0)
});
New answer
After discussing in the comments, it turns out you think JavaScript timers are blocking the execution of the main thread. It does not work this way. Consider the following example (2 is printed almost instantly, and 1 is printed after one second).
> | setTimeout(function(){console.log(1)}, 1000);
| setTimeout(function(){console.log(2)}, 0);
< | ...
| 2
| 1
Also read this article from jQuery's creator.
Since your code is based on a wrong assumption, I think it makes no sense to work on your question any longer.
Old answer
Your setInterval timer is running a lot faster than your setTimeout timers, meaning that it queues a lot of setTimeout timers before starting to increment the opacity. During this time, the message is incremented and printed at interval max speed. After a couple of ms, all setTimeout timers start firing one after the other with almost no delay between them, and interleaving with setInterval timers, which leads to an (almost) unpredictable mess.

How to recursively change color of element

I want to create a program that recursively changes the color of text.
I've already created a for(){} loop with the random color code in it in order to attempt to recurse it.
for(var x = 0; x > -1; x++){
document.getElementById("k").style.color = '#'+(0x1000000+
(Math.random())*0xffffff).toString(16).substr(1,6)}
<p id="k">Yeet</p>
The actual result is that the color stays at it's default, black. I would like it to be that every time it runs (infinitely), it will change colors.
You have to use setInterval() method to run asynchronously without blocking the main execution.
setInterval(() => document.getElementById("k").style.color = '#' + (0x1000000 +(Math.random()) * 0xffffff).toString(16).substr(1, 6),500)
<p id="k">Yeet</p>
If you want to stop at some point then use clearInterval() method to clear the interval.
let i = 0;
const inter = setInterval(() => {
document.getElementById("k").style.color = '#' + (0x1000000 + (Math.random()) * 0xffffff).toString(16).substr(1, 6);
// to clear after 10 colors
if (i++ === 10) clearInterval(inter);
}, 500)
<p id="k">Yeet</p>
/** Bad recursive pattern.
*
* All recursive methods should have a base case,
* I assume you want the change to stop at some point,
* if not you have an infinite loop running.
*
* This function can still time out if it takes too long to hit "theLastColor"
*/
function recursiveColorChange(id, currentColor) {
const theLastColor = '#some-value';
if (currentColor === theLastColor) {
return;
}
const randomColor = '#' + (0x1000000 + (Math.random()) * 0xffffff).toString(16).substr(1, 6);
document.getElementById(id).style.color = randomColor;
return recursiveRandomColor(id, randomColor);
}
However, using the pure recursive code prevents controlling color change Z_BEST_SPEED.
As mentioned by #pranav-c-balan, I think it is better to use setTimeout.
You can still have a base case where you stop changing colors by using clearTimeout();
/** Better setTimeOut pattern.
*
* You can pass a lastColor value if you want it to stop if it reaches a color.
* Or you can just pass it an id and a speed (in milliseconds) and it will run forever without breaking your code.
*/
function repeatColorChange(id, speed, lastColor) {
const myTimeOut = setTimeout(() => {
const randomColor = '#' + (0x1000000 + (Math.random()) * 0xffffff).toString(16).substr(1, 6);
document.getElementById(id).style.color = randomColor;
if (randomColor === lastColor) {
clearTimeout(myTimeOut);
}
}, speed);
}

Why does this Increment not work?

I'm having some problems with this code. My problem is that with the code below, it doesn't plus the detection-ratio text with the 'incr'. It just enters the incr, but doesn't plus.
This is my code.
(function loop() {
var rand = Math.round(Math.random() * (3000 - 500)) + 500;
var incr=Math.floor(Math.random()*6);
setTimeout(function() {
document.getElementById('detection-ratio').innerText = '0 / '+ ++incr;
loop();
}, rand);
}());
The 'detection-ratio' text looks like this as default:
0 / 0
Then, lets say 'incr' generates the number '3', then it should increase the last 0 with 3, so it would look like this:
0 / 3
Then, lets say it's going to generate a new 'incr', lets say '5'. Then it would look like this:
0 / 8
---> But right now, it doesn't do that. It just writes the 'incr' into the 'detection-ratio' without increasing it.
Hope this code would help you to get the expected output, let me know if something breaks. Also stop iteration once it reaches > 26
var incr = 0;
(function loop() {
var rand = Math.round(Math.random() * (3000 - 500)) + 500;
incr += Math.floor(Math.random()*6);
setTimeout(function() {
console.log('0 / '+ incr);
loop();
}, rand);
}());
Thanks for the explanation and patience.
I am assuming you are trying to append text to detection-ratio
if so you need to
document.getElementById('detection-ratio').innerText += '0 / '+ incr;
++ before a variable is a pre-increment operator, since you are generating random numbers i am assuming that is not actually what you want.
Since you're calling the loop recursively anyway, you may want to consider a more functional approach:
(function loop(startctr) {
var rand = Math.round(Math.random() * (3000 - 500)) + 500;
nextctr = startctr + Math.floor(Math.random()*6);
setTimeout(function() {
console.log('0 / '+ nextctr);
loop(nextctr);
}, rand);
}(0));

jQuery - make div get wider every second

First of all, I have to warn you, my English is not so good...
OK, here is my problem: I have a progress bar that gets wider every second based on percents.
Every second I want to add 1.67 / [max] percent.
[max] = 100% (how many minutes it'll take).
(if [max] = 10 - the progress bar will take 10 minutes)
My code WORKS, but only if the number (after dividing) is bigger than 0.3 (something like that).
So it means that if the progress bar will take 1 minute ([max] = 1) the code will work, because the number is 1.67 after dividing.
But if I make max to be 15 minutes, it wont work - WHY?!
This is my code: (I added some comments to make it easier)
<script>
function updateProgress() {
/*Get Progress Width (in percents)*/ var progress = ( 100 * parseFloat($('#arena_bar').css('width')) / parseFloat($('#arena_bar').parent().css('width')) );
var corrent = progress;
var max = 1; // one minute
var add = 1.67 / max;
if (progress < 100) {
corrent += add;
$("#arena_bar").css("width", corrent + "%");
setTimeout(updateProgress, 1000); // update every second
}
}
updateProgress();
</script>
Please Help !
The problem is that you're not making the width grow enough for the percentage to change in CSS, so it stays constant (at least that's what it looks like). The thing is, you don't really need all that.
Here's a js fiddle of your code, changed to work. I changed the time delay to make it run faster, you can change it back to 1000ms if you want.
And the code:
HTML:
<div style="width:400px; background-color:black">
<div id="arena_bar" style="background-color:navy; width:10px"> </div>
</div>
JS:
/*Get Progress Width (in percents)*/ var corrent = ( 100 * parseFloat($('#arena_bar').css('width')) / parseFloat($('#arena_bar').parent().css('width')) );
function updateProgress() {
var max = 15; // one minute
var add = 1.67 / max;
if (corrent < 100) {
corrent += add;
$("#arena_bar").css("width", corrent + "%");
setTimeout(updateProgress, 50); // update every second
}
}
updateProgress();
For the record, jQuery is not setting your computed widths as real percentage values, it sets them using pixel values.
In this example you can see your written widths and the read ones.
function updateProgress() {
var progress = (parseInt($('#arena_bar').css('width')) / parseInt($('#arena_bar').parent().css('width')))*100;
$('.progress').text('Read: ' + progress + ' %');
var max = 1;
var add = 1.67 / max;
if (progress < 100) {
progress += add;
$('.debug').html('Written: ' + progress + ' %');
$("#arena_bar").css("width", progress + "%");
setTimeout(updateProgress, 1000); // update every second
}
}
updateProgress();
http://jsfiddle.net/9PjqZ/1/
As you can see there is a difference to the values when you read them in the next function call. There are no problems when the difference between written and read values is above an unknown limit. When they come too close to each other your idea won't work anymore.
You need to save the current percentage outside the css and only write to css but not read from css.

Implementing jQuery's shake effect with animate

I've been given a cut down subset of the jQuery lib one of the key features I'm missing is the .effect functions. I do however have .animate. I was wondering if anyone would have any ideas how I could go about reproducing the animation functions.
I am particularly consious of making this only a few lines as I need to keep the code size down. Which is why the jquery lib is as small as it is and doesnt have the effects functions.
TLDR - I'm trying to replace
$("#"+id_string).effect( "shake", {}, "fast" );
With something using .animate within jQuery.
So far I have something like this ..
jQuery.fn.shake = function(intShakes, intDistance, intDuration) {
this.each(function() {
$(this).css("position","relative");
for (var x=1; x<=intShakes; x++) {
$(this).animate({left:(intDistance*-1)}, (((intDuration/intShakes)/4)))
.animate({left:intDistance}, ((intDuration/intShakes)/2))
.animate({left:0}, (((intDuration/intShakes)/4)));
}
});
return this;
};
I like #phpslightly solution so much, I keep using it. So here it is updated to basic jquery plugin form which will return your element
jQuery.fn.shake = function(interval,distance,times){
interval = typeof interval == "undefined" ? 100 : interval;
distance = typeof distance == "undefined" ? 10 : distance;
times = typeof times == "undefined" ? 3 : times;
var jTarget = $(this);
jTarget.css('position','relative');
for(var iter=0;iter<(times+1);iter++){
jTarget.animate({ left: ((iter%2==0 ? distance : distance*-1))}, interval);
}
return jTarget.animate({ left: 0},interval);
}
You would then use it like a regular plugin:
$("#your-element").shake(100,10,3);
Or use the default values (100, 10, 3):
$("#your-element").shake();
It's actually already implemented this way under the covers, you can see exactly how in jquery.effects.shake.js, if you wanted to copy only that functionality you can.
Another approach to think about: if you're using multiple effects, I'd recommend downloading jQuery UI with only the effects you want. For this effect, without copying the functionality yourself, you would just need jquery.effects.core.js and jquery.effects.shake.js.
This is probably irrelevant now but I've ported jQ UI's shake effect as a standalone jQuery plugin. All you need is jQuery and it will work exactly like the one provided in jQ UI.
For those who want to use the effect without actually bloating their project with unnecessary jQ UI core files.
$('#element').shake({...});
It can be found here with instruction: https://github.com/ninty9notout/jquery-shake
Thought I'd leave this here for future reference.
This is a more clean and smooth way to do the animation.
jQuery.fn.shake = function(shakes, distance, duration) {
if(shakes > 0) {
this.each(function() {
var $el = $(this);
var left = $el.css('left');
$el.animate({left: "-=" + distance}, duration, function(){
$el.animate({left: "+=" + distance * 2}, duration, function() {
$el.animate({left: left}, duration, function() {
$el.shake(shakes-1, distance, duration); });});
});
});
}
return this;
};
I don't understand all the complexity being thrown into reproducing the shake effect with solely animate. Here's my solution in just a couple lines.
function shake(div,interval=100,distance=10,times=4){
$(div).css('position','relative');
for(var iter=0;iter<(times+1);iter++){
$(div).animate({ left: ((iter%2==0 ? distance : distance*-1))}, interval);
}//for
$(div).animate({ left: 0},interval);
}//shake
EDIT: Updated code to return element to original position. Still believe this is the lightest and best solution to the problem.
I wrote some time ago a few simple jquery animations:
https://github.com/yckart/jquery-custom-animations
/**
* #param {number} times - The number of shakes
* #param {number} duration - The speed amount
* #param {string} easing - The easing method
* #param {function} complete - A callback function
*/
jQuery.fn.shake =
jQuery.fn.wiggle = function (times, duration, easing, complete) {
var self = this;
if (times > 0) {
this.animate({
marginLeft: times-- % 2 === 0 ? -15 : 15
}, duration, easing, function () {
self.wiggle(times, duration, easing, complete);
});
} else {
this.animate({
marginLeft: 0
}, duration, easing, function () {
if (jQuery.isFunction(complete)) {
complete();
}
});
}
return this;
};
This is not perfect, but functional
// Example: $('#<% =ButtonTest.ClientID %>').myshake(3, 120, 3, false);
jQuery.fn.myshake = function (steps, duration, amount, vertical) {
var s = steps || 3;
var d = duration || 120;
var a = amount || 3;
var v = vertical || false;
this.css('position', 'relative');
var cur = parseInt(this.css(v ? "top" : "left"), 10);
if (isNaN(cur))
cur = 0;
var ds = d / s;
if (v) {
for (i = 0; i < s; i++)
this.animate({ "top": cur + a + "px" }, ds).animate({ "top": cur - a + "px" }, ds);
this.animate({ "top": cur }, 20);
}
else {
for (i = 0; i < s; i++)
this.animate({ "left": cur + a }, ds).animate({ "left": cur - a + "px" }, ds);
this.animate({ "left": cur }, 20);
}
return this;
}
Based on #el producer solution, I added some multiply logic and make it look like a random shake.
jQuery.fn.shake = function (interval, distance, times) {
interval = typeof interval == "undefined" ? 100 : interval;
distance = typeof distance == "undefined" ? 10 : distance;
times = typeof times == "undefined" ? 3 : times;
var jTarget = $(this);
jTarget.css('position', 'relative');
for (var iter = 0; iter < (times + 1) ; iter++) {
jTarget.animate({ top: ((iter % 2 == 0 ? distance * Math.random() : distance * Math.random() * -1)), left: ((iter % 2 == 0 ? distance * Math.random() : distance * Math.random() * -1)) }, interval);
}
return jTarget.animate({ top: 0 , left: 0 }, interval);
}
Position had to be absolute on my side, so I changed it to:
jQuery.fn.shake = function(interval, distance, times) {
interval = typeof interval == "undefined" ? 100 : interval;
distance = typeof distance == "undefined" ? 10 : distance;
times = typeof times == "undefined" ? 3 : times;
var jTarget = $(this);
for (var iter=0;iter<(times+1);iter++) {
jTarget.animate({ 'padding-left': ((iter%2==0 ? distance : distance*-1))}, interval);
}
return jTarget.animate({ 'padding-left': 0 } ,interval);
}

Categories