Javascript better way to code this animation [closed] - javascript

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 9 years ago.
Improve this question
I first off all tried passing around the value of y back on the function but this caused the browser to slow as if an infinite loop had been created, the external frame variable stops this but I'd prefer to keep all the variables inside functions, is there a way I can achieve this without getting 'feedback'?
var frame=0;
function launch(){
var el=document.getElementById("selection");
setInterval(function(){ drawer(el,frame);},300);
}
function drawer(el,y){
if(y<20){
frame++;
el.style.top=20+frame+"px";
setInterval(function(){ drawer(el,frame);},300);
}

Use a closure, you also want to be using setTimeout or alternatively killing the interval when it's done:
function launch(){
var animator = function(el) {
var frame = 0;
var _this = {
draw : function() {
frame += 1;
el.style.top=20+frame+"px";
if(frame < 20) {
setTimeout(_this.draw, 300);
}
}
}
return _this;
}(document.getElementById("selection"));
setTimeout(animator.draw, 300);
}

Here's the updated code. You only have to create an interval once. Store it in a variable and clear it when the maximum is reached.
var frame = 0;
var running = null;
var max = 20;
var e = document.getElementById("selection");
function drawer() {
++frame
e.style.top = 20 + frame + "px";
if (frame == max) {
clearInterval(running);
running = null;
}
}
running = setInterval(drawer, 300);
Demo
Try before buy
Edit
As you said in your question you want to keep all variables inside the function, you can use this:
function drawer(e, frame) {
if ('undefined' == typeof e) {
var e = document.getElementById("selection");
}
if ('undefined' == typeof frame) {
var frame = 0;
}
++frame
e.style.top = 20 + frame + "px";
if (frame <= 20) {
setTimeout(function() { drawer(e, frame); }, 300);
}
}
drawer();
Demo
Try before buy

Here are few advices for you to improve your coding style:
Try to make a meaningful functions
Try to parametize numbers with meaningful and clear names
Write clean codes
Let me give you my version of what I understand you are trying to do.
As you can see, it is more clean and easy to read/understand for others.
I included a live demo at the bottom for you to fiddle with.
function launch() {
var el = document.getElementById('selection'),
maxY = 300,
stepY = 20,
interval = 100;
animateY(el, maxY, stepY, interval);
}
function moveToY(el, y) {
el.style.top = y + "px";
}
function animateY(el, maxY, stepY, interval) {
var y = 0;
var id = setInterval(function () {
if (y < maxY) {
y += stepY;
moveToY(el, y);
}
else {
clearInterval(id);
}
}, interval);
}
Here's a live demo: http://jsfiddle.net/A53sy/2/

Related

setInterval to fade in then fade out pure javascript no jquery or css

I am trying to implement a fade in/fade out feature that runs on a button click depending if some data was changed. I am using angular but the ngAnimate I could not get to work so I want to do it with pure js. What I currently have will flash the text for a second, then do nothing. This is inside my controller.
var warningText = document.getElementById('warningText');
warningText.style.display = 'inline'
$scope.warningText = "Warning: No Data was updated.";
var op = 0.0;
var fadeIn = setInterval(function() {
if (op >= 1) {
clearInterval(fadeIn);
fadeOut(op);
}
warningText.style.opacity = op;
op += op * 0.1;
}, 50);
var fadeOut = function(op) {
setInterval(function() {
if (op <= 0.1) {
clearInterval(fadeOut);
warningText.style.display = 'none';
}
warningText.style.opacity = op;
op -= op * 0.1;
}, 50);
}
Your calculation of op is wrong as that will always be zero. Secondly the second function does not return the value from setInterval, so you'll never be able to clear that interval.
Here is how you could do it with just one interval, where the sign of the increments to the opacity is reversed every time the boundary value is reached:
var warningText = document.getElementById('warningText');
function flickerMessage(msg) {
var op = 0.1;
var increment = +0.1;
warningText.textContent = msg;
warningText.style.opacity = 0;
warningText.style.display = 'inline';
var timer = setInterval(function() {
op += increment;
warningText.style.opacity = op;
if (op >= 1) increment = -increment;
if (op <= 0) {
warningText.style.display = 'none';
clearInterval(timer); // end
}
}, 50);
}
flickerMessage('Warning you');
<div id="warningText" style="display:none; opacity: 0">warning text</div>
<hr>

How to change the speed of setInterval in real time

I would like to know how to change the speed of setInterval in real time e.g:
if (score < 10)
repeater = setInterval(function() {
spawnEnemy();
}, 1000);
if (score => 10)
repeater = setInterval(function() {
spawnEnemy();
}, 500);
I know this method doesn't work, but is there a way that I can achieve this some other way?
jsFiddle Demo
There is no way to change the interval speed itself once running. The only way to do it is to have a variable for the speed, and then clear the interval and start a new one with the new speed.
var speed = 500;
var changeSpeed = speed;
repeater = setInterval(repeaterFn, speed);
function repeaterFn(){
spawnEnemy();
if( changeSpeed != speed ){
clearInterval(repeater);
speed = changeSpeed;
repeater = setInterval(repeaterFn, speed);
}
}
function changeRepeater(){
changeSpeed = 700;
}
Another way would be to just use setTimeout rather than setInterval. Do the check every time so you can keep your speed logic in a seperate function.
var game_over = false;
var score = 0;
function getSpeedFromScore(score)
{
if (score > 20) {
game_over = true;
}
if (score < 10) {
return 1000;
} else {
return 500;
}
}
function spawnEnemyThenWait() {
if (!game_over) {
spawnEnemy();
var speed = getSpeedFromScore(score);
setTimeout(spawnEnemyThenWait, speed);
}
}
JS Fiddle http://jsfiddle.net/bq926xz6/
You can use clearInterval:
if (score < 10) {
clearInterval(repeater);
repeater = setInterval(spawnEnemy, 1000);
}
if (score => 10) {
clearInterval(repeater);
repeater = setInterval(spawnEnemy, 500);
}
But it depends on the context. If this snippet is executed more often, than it has to be, you will need some kind of mechanism to prevent it from resetting your interval all the time.
But there is (as I wrote in the comment to the question) no way to use clearInterval and change the interval itself. At least not without replacing it by a new interval as shown above.
You can use a game loop and track the spawn state in an enemy class:
// press f12 so see console
function Enemy() {
this.spawned = false;
this.spawnOn = 20;
this.tick = function () {
this.spawnOn = this.spawnOn - 1;
if (this.spawnOn == 0) {
this.spawned = true;
}
}
this.goBackToYourCage = function () {
this.spawnOn = Math.floor(Math.random() * 50) + 1;
this.spawned = false;
}
}
var enemy = new Enemy();
window.setInterval(function () {
enemy.tick();
if (enemy.spawned) {
console.log('spawned');
enemy.goBackToYourCage();
console.log('Next spawin in :' + enemy.spawnOn);
}
}, 100);
http://jsfiddle.net/martijn/qxt2fe8y/2/

Button to randomize pictures won't work the second time

var fnr = function (a,b) {
// random integer on closed interval
var y = a + Math.floor((b - a + 1) * Math.random());
return y
}
var foo;
var myTimer;
function toRandom() {
var x = fnr(0, PICTURES.length - 1);
var y = SERVER + FOLDER + PICTURES[x] + SUFFIX;
document.getElementById("img").src = y;
document.getElementById("filename").value = PICTURES[x].toUpperCase();
}
function toRandomize() {
if(!foo) {
foo = true;
document.getElementById("random").value = "stop shuffle";
myTimer = setInterval("toRandom()", 600);
} else {
foo = false;
document.getElementById("random").value = "start shuffle";
clearInterval(myTimer);
}
}
window.onload = function () {
document.getElementById("random").onclick = toRandomize;
}
// button in the body of the HTML portion of my code
<input id="random" type="button" value="start shuffle">
I have a button to randomize photos, but it won't work the second time. Do you guys have any insight in reading the code? I know fnr stands for first-non-repeating, but I'm not too familiar with how to "reset it," or if it requires a reset to get it to work again. I have the same setup for other functions and they work fine.
It's cut so it will only show the parts relevant to the question. I apologize if it's confusing.

How to use localStorage on this example? [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 9 years ago.
Improve this question
Can you apply localStorage on this? If I click "Deduct", the id "slot" will reduce to 1. What I want to learn is how to apply localStorage. If the slot is 9, and if I will refresh/close the browser or restart the computer and run the program again, the slot will remain 9. I'm having a hard time on learning about localStorage, need help masters.
<script>
var availableSlot1 = 10;
var reduceSlot1 = function(valueToDeduct1){
availableSlot1 = availableSlot1 - valueToDeduct1;
document.getElementById('slot').innerHTML = availableSlot1;
var x = storage.key(availableSlot1);
if (availableSlot1 == 0){
document.getElementById('x').innerHTML = "FULL";
document.getElementById("button1").style.display = "none";
}
};
</script>
<p id="slot">10</p>
Deduct
EDITED:
<script>
var slot = localStorage.getItem("slot");
if (typeof slot == "undefined") {
slot = 10;
}
document.getElementById("slot").innerHTML = slot;
var reduceSlot1 = function reduceSlot(by)
{
if (slot >= by) {
slot -= by;
document.getElementById("slot").innerHTML = slot;
localStorage.setItem("slot", slot);
}
else {
document.getElementById('slot').innerHTML = "FULL";
document.getElementById("button1").style.display = "none";
}
}
</script>
I followed the code but, it is not working.
Live Demo of Local Storage
you can use local storage using localStorage.name, in place of name you can use any property.
var i = 1;
localStorage.name = i;
http://jsfiddle.net/flocsy/mrhwK/
var slot = localStorage.getItem("slot");
if (slot == null) {
slot = 10;
}
document.getElementById("slot").innerText = slot;
function reduceSlot() {
if (slot >= 1) {
slot --;
document.getElementById("slot").innerText = slot;
localStorage.setItem("slot", slot);
} else {
document.getElementById('x').innerText = "FULL";
document.getElementById("button1").style.display = "none";
}
}
document.getElementById("button1").onclick = reduceSlot;

Looking for thoughts on improvement of my javascript (jquery) code. Recursive function

I have made this code that makes some visual "tiles" that fades in and out.
But at the moment I'm having a little performance problem.
Though most browers are running the code okay (especially firefox), some like safari have problems after a while (a while = like 15 seconds).
I think its due to my recursive function (the function named changeopacity that calls itself forever on a delay)? or is it?
But anyways the problem is that this code is really heavy for most browsers. Is there, or more how can I make this code perform any better? any thoughts? (code examples would be nice) thanks :-)
The actual code:
$(document).ready(function () {
var aniduration = 2000;
var tilesize = 40;
createtable(tilesize);
$(".tile").each(function (index, domEle) {
var randomdelay = Math.floor(Math.random() * 3000);
setTimeout(function () {
changeopacity(aniduration, domEle);
}, randomdelay);
});
$("td").click(function () {
clickanimation(this, 9);
});
$("td").mouseenter(function () {
var element = $(this).find("div");
$(element).clearQueue().stop();
$(element).animate({opacity: "0.6"}, 800);
});
$("td").css("width", tilesize + "px").css("height", tilesize + "px");
});
function createtable(tilesize) {
var winwidth = $(window).width();
var winheight = $(window).height();
var horztiles = winwidth / tilesize;
var verttiles = winheight / tilesize;
for (var y = 0; y < verttiles; y++)
{
var id = "y" + y;
$("#tbl").append("<tr id='" + id + "'></tr>");
for (var x = 0; x < horztiles; x++)
{
$("#" + id).append("<td><div class='tile' style='opacity: 0; width: " + tilesize + "px; height: " + tilesize + "px;'></div></td>");
}
}
}
function changeopacity(duration, element){
var randomnum = Math.floor(Math.random() * 13);
var randomopacity = Math.floor(Math.random() * 7);
var randomdelay = Math.floor(Math.random() * 1000);
if ($(element).css("opacity") < 0.3)
{
if (randomnum != 4)
{
if ($(element).css("opacity") != 0)
animation(element, 0, duration, randomdelay);
}
else
{
animation(element, randomopacity, duration, randomdelay);
}
}
else
{
animation(element, randomopacity, duration, randomdelay);
}
setTimeout(function () {
return changeopacity(duration, element);
}, duration + randomdelay);
}
function animation(element, randomopacity, duration, randomdelay){
$(element).clearQueue().stop().delay(randomdelay).animate({opacity: "0." + randomopacity}, duration);
}
function clickanimation(column, opacitylevel) {
var element = $(column).find("div");
$(element).clearQueue().stop();
$(element).animate({"background-color": "white"}, 200);
$(element).animate({opacity: "0." + opacitylevel}, 200);
$(element).delay(200).animate({opacity: "0.0"}, 500);
//$(element).delay(600).animate({"background-color": "black"}, 500);
}
The number one issue is that you are creating one setTimeout for every single cell on your page. The only browser capable of handling that is Internet Explorer, and then it fails due to the many CSS changes causing slow redraws.
I would strongly suggest programming your own event scheduler. Something like this, which I used in a university project:
var timer = {
length: 0,
stack: {},
timer: null,
id: 0,
add: function(f,d) {
timer.id++;
timer.stack[timer.id] = {f: f, d: d, r: 0};
timer.length++;
if( timer.timer == null) timer.timer = setInterval(timer.run,50);
return timer.id;
},
addInterval: function(f,d) {
timer.id++;
timer.stack[timer.id] = {f: f, d: d, r: d};
timer.length++;
if( timer.timer == null) timer.timer = setInterval(timer.run,50);
return timer.id;
},
remove: function(id) {
if( id && timer.stack[id]) {
delete timer.stack[id];
timer.length--;
if( timer.length == 0) {
clearInterval(timer.timer);
timer.timer = null;
}
}
},
run: function() {
var x;
for( x in timer.stack) {
if( !timer.stack.hasOwnProperty(x)) continue;
timer.stack[x].d -= 50;
if( timer.stack[x].d <= 0) {
timer.stack[x].f();
if( timer.stack[x]) {
if( timer.stack[x].r == 0)
timer.remove(x);
else
timer.stack[x].d = timer.stack[x].r;
}
}
}
}
};
Then, instead of using setTimeout, call timer.add with the same arguments. Similarly, instead of setInterval you can call timer.addInterval.
This will allow you to have as many timers as you like, and they will all run off a single setInterval, causing much less issues for the browser.
Nice animation :-) However, I found some bugs and possible improvements:
Your table is not rebuilt on window resizes. Not sure if bug or feature :-)
Use delegated events. You have a lot of elements, and every event handler is costly. Sadly, this won't work for the non-bubbling mouseenter event.
It would be nice if you would not use inline styles for with and height - those don't change. For the divs, they are superflouos anyway.
I can't see a reason for all those elements to have ids. The html-string building might be more concise.
Cache the elements!!! You are using the jQuery constructor on nearly every variable, building a new instance. Just reuse them!
Your changeopacity function looks a bit odd. If the opacity is lower than 0.3, there is 1-in-13-chance to animate to zero? That might be expressed more stringent. You also might cache the opacity to a variable instead of reading it from the dom each time.
There is no reason to pass the duration and other constants as arguments, they do never change and can be used from the global scope.
Instead of using the timeout, you should use the complete callback of the animate method. Timeouts are never accurate, they may even interfere here causing (minor) problems.
var duration = 2000,
tilesize = 40,
clickopacity = 0.9;
$(document).ready(function () {
filltable($("#tbl"), tilesize)
.on("click", "td", clickanimation);
$(".tile").each(function() {
changeopacity($(this));
});
$("#tbl div").mouseenter(function () {
$(this).clearQueue()
.stop()
.animate({opacity: "0.6"}, 800);
});
});
function filltable(tbl, tilesize) {
var win = $(window).width();
var horztiles = win.width() / tilesize;
var verttiles = win.height() / tilesize;
for (var y = 0; y < verttiles; y++) {
var tr = "<tr>";
for (var x = 0; x < horztiles; x++)
tr += "<td style='width:"+tilesize+"px;height:"+tilesize+"px;'><div class='tile' style='opacity:0;'></div></td>");
tbl.append(tr+"</tr>");
}
return tbl;
}
function changeopacity(element) {
var random = Math.floor(Math.random() * 13);
var opacity = Math.floor(Math.random() * 7);
var delay = Math.floor(Math.random() * 1000);
if (element.css("opacity") < 0.3 && random != 4)
opacity = 0;
element.clearQueue().stop().delay(delay).animate({
opacity: "0." + opacity
}, duration, function() {
changeopacity(element);
});
}
function clickanimation() {
$(this.firstChild)
.clearQueue()
.stop()
.animate({"background-color": "white"}, 200)
.animate({opacity: "0." + clickopacity}, 200)
.delay(200).animate({opacity: "0.0"}, 500);
//.delay(600)
//.animate({"background-color": "black"}, 500);
}

Categories