Change jquery animate to manually control function - javascript

I want to change jquery animate function, to a function which would let me control everything manualy. Currently code increases multiple numbers with delay. Here's the code (https://jsfiddle.net/6mrLv1ma/8/):
var ammount = 10;
var duration = 0.5;
var delay = (1-duration)/ammount; // 0.05
curDelay = 0;
for(var i=0;i<ammount;i++) {
$( "#container" ).append("<div id='output"+i+"'>0</div>");
setTimeout(
(function(i) {
return function() {
animate(i, duration);
}
})(i, duration),curDelay*1000);
curDelay += delay;
}
function animate(i, duration){
$({value: 0}).animate({value: 1}, {
duration: duration*1000,
step: function() {
var placement = "output"+i;
document.getElementById(placement).innerHTML = this.value;
}
})
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="container"></div>
Here's graph how it's working:
So if currentProc = 0.2, the output should be something like this:
0.3
0.16
0.07
0
0
0
0
0
0
0
I already started but need help with the formula(https://jsfiddle.net/18kd85hn/1/):
var ammount = 10;
var duration = 0.5;
var delay = (1-duration)/ammount; // 0.05
function myFunction(currentProc){ // currentProc value 0 - 1
var values = [];
for(var i = 0; i<ammount;i++){
var currentPos = i*delay; // formula
document.getElementById("output").innerHTML += "<br>"+currentPos;
}
}
myFunction(0.2)
<div id="output">output:</div>

Got it (https://jsfiddle.net/18kd85hn/7/)
var value = 1-(i*delay+duration-currentProc)/duration;
if (value<0){value =0}
if (value>1){value =1}
And replaced jquery animate - https://jsfiddle.net/18kd85hn/8/

Related

svgjs how to replay and control series of animations using slider

I am using svg.js and would like to play a series of animations and then provide a slider for the user to control the animation to look at it more closely. It looks like when an animation is complete, svg.js cleans up the resources so the situation gets nulled out so I can't reset it. I have a fiddle with a simplified example. After the animation plays once, I would like to then be able to use the slider and use the at method to reposition the animations.
https://jsfiddle.net/voam/xeajbhea/2/
var draw = SVG('drawing'),
rectA1 = draw.rect(50, 50).fill('#f06'),
rectA2 = draw.rect(50, 50).fill('#60f').translate(200),
rectB1 = draw.rect(50, 50).fill('#ce0').translate(0, 100).opacity(0),
rectB2 = draw.rect(50, 50).fill('#0ec').translate(200, 100).opacity(0);
var animation1 = rectA1.animate().move(200).opacity(0);
var animation2 = rectA2.animate().move(-200).opacity(0).during(function(pos) {
document.getElementById("range_animation").value = pos;
});
var animation3, animation4;
animation2.after(function(situation) {
animation3 = rectB1.animate().move(200).opacity(1);
animation4 = rectB2.animate().opacity(1).move(-200)
.during(function(pos) {
document.getElementById("range_animation").value = 1 + pos;
});
console.log('animations', animation1, animation2, animation3, animation4);
});
document.getElementById("range_animation").addEventListener("change", function(el) {
///console.log('change', el, );
var val = this.value;
if (animation1.situation) {
if (val < 1) {
animation1.at(val).pause();
animation2.at(val).pause();
} else {
animation3.at(val - 1).pause();
animation4.at(val - 1).pause();
}
}
}, false);
HTML below:
<div id="drawing">
</div>
<input type="range" id="range_animation" min="0.01" max="2" step=".01" value=".01">
One approach that works is as #Fuzzyma mentioned is to make the animations loop so they don't get terminated. Then one has to manage the transition sets from one to another. Any animations that don't get run immediately start in a paused state. In my real project not this demo there are 5 animation sets each with subanimations so lets see if this turns out to be manageable!
var atEnd = function (pos) {
var rounded = pos.toFixed(2);
return (rounded == .99 || rounded == 1.00);
};
var draw = SVG('drawing')
, rectA1 = draw.rect(50, 50).fill('#f06')
, rectA2= draw.rect(50, 50).fill('#60f').translate(200)
, rectB1 = draw.rect(50, 50).fill('#ce0').translate(0, 100).opacity(0)
, rectB2 = draw.rect(50, 50).fill('#0ec').translate(200, 100).opacity(0);
var animation3, animation4, lastTotalRange;
var animation1 = rectA1.animate().move(200).opacity(0).loop();
var animation2 = rectA2.animate().move(-200).opacity(0).loop();
animation2
.during(function( pos ) {
document.getElementById("range_animation").value = pos;
if ( atEnd (pos) ) {
animation2.pause();
animation3 = rectB1.animate().move(200).opacity(1).loop();
animation4 = rectB2.animate().opacity(1).move(-200).loop();
animation4
.during(function( pos ) {
if (pos > 0) {
document.getElementById("range_animation").value = 1 + pos;
}
if (atEnd (pos)) {
animation4.pause();
}
});
animation3.during( function(pos) {
if (atEnd (pos)) {
animation3.pause();
}
});
}
});
animation1.during(function( pos ) {
if (atEnd (pos)) {
animation1.pause();
}
});
$('input#range_animation').on('input', function() {
$(this).trigger('change');
})
$('input#range_animation').on('change', function() {
var val = $( this).val(),
delta = 0;
if (lastTotalRange) {
delta = val - lastTotalRange;
}
if (val < 1) {
animation1.at(val).pause();
animation2.at(val).pause();
if (lastTotalRange > 1) {
animation3.at(.01);
animation4.at(.01);
}
}
else {
animation3.at(val - 1).pause();
animation4.at(val - 1).pause();
if (lastTotalRange < 1) {
animation1.at(.99);
animation1.at(.99);
}
}
// console.log('totalAnimationRange:change', delta, val);
lastTotalRange = val;
})
and the html
<div id="drawing"></div>
<input type="range" id="range_animation" min="0.00" max="1.99" step=".01" value=".01">
and the jsfiddle:
jsfiddle.net/voam/ob2bjfur/1/

Replace background image given an array of images

I am trying to set a background image rotation for a div with a image already in it, doesn't seem to change anything, thought I would check if someone could see the problem since no errors come up.
<script>
$( document ).ready(function() {
setInterval(function() {
var tips = [
"header.jpg",
"header2.jpg",
"header3.jpg",
"header4.jpg",
"header5.jpg",
];
var i = 0;
if (i == tips.length) --i;
$('.containercoloring').fadeTo('slow', 0.3, function()
{
$(this).css('background-image', 'url(' + tips[i] + ')');
}).delay(1000).fadeTo('slow', 1);
i++;
}, 5 * 1000);
});
</script>
<body>
<div class="containercoloring"> </div>
</body>
Below is the working(updated) code. I hope you are importing jquery lib separately.
<script>
$(document).ready(function() {
var i = 0;
setInterval(function() {
var tips = [
"header.jpg",
"header2.jpg",
"header3.jpg",
"header4.jpg",
"header5.jpg"
];
var imageIndex = ++i % tips.length;
$('.containercoloring').fadeTo('slow', 0.3, function() {
$(this).css('background-image', 'url(' + tips[imageIndex] + ')');
}).delay(1000).fadeTo('slow', 1);
}, 5 * 1000);
});
</script>
<body>
<div class="containercoloring" style="height: 100px;">Image Container</div>
</body>
You can test here in this jsfiddle - https://jsfiddle.net/nbq9f6bg/
Hope it helps!
So there were a couple of notes I had about your example. Please see the comments below. I used background color and removed the transitions to show it easier.
// declare variables
var tips = [
"#ff9000",
"#ff0000",
"#0099ff"
];
// counter
var i = 0;
// direction
var direction = 'forwards';
// time in between intervals
var timer = 2000;
function changeBackground() {
// Check the length minus 1 since your counter starts at 0
if (i == (tips.length - 1)) {
direction = 'backwards';
} else if(i == 0) {
// only set the direction back to forwards when the counter is 0 again
direction = 'forwards';
}
$('.containercoloring').css('background-color', tips[i]);
// add or subtract based on the direction
if(direction == 'forwards') {
i++;
} else {
i--;
}
}
$( document ).ready(function() {
// save interval to a variable so you can stop it later
var theInterval = setInterval(changeBackground, timer);
// run the function since the interval will take 2000ms to execute
changeBackground();
});
.containercoloring {
height: 100px;
width: 100px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="containercoloring"> </div>

jQuery animate get current value from associated array

I've got the following code:
var tot = 0;
var prices = {
"Price1:": 37152.32,
"Price2:": 54023,
"Price3:": 37261.75
};
var animationDuration = 2000; // Must be lower than carousel rotation
function animatePrices() {
var dur = animationDuration / Object.keys(prices).length;
for (var key in prices) {
tot += prices[key];
$({
p: 0
}).animate({
p: tot
}, {
duration: dur,
easing: 'linear',
step: function(now) {
document.getElementById("total").textContent = now.toFixed(2);
document.getElementById("what-for").textContent = key + " $" + prices[key];
}
});
}
}
window.onload = function() {
animatePrices();
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
$<span id="total"></span>
<br>
<span id="what-for"></span>
But as you can see, in the what-for span it shows Price3. But I want it to loop through the prices as the price goes up.
Ex:
When now is 37100 then what-for is Price1.
When now is 40000 then what-for is Price2.
When now is 100000 then what-for Price3.
What am I doing wrong?
The problem in you code is that it was calling animate inside a loop. That is messing up your the variable logic. You can use something like the following. I am using recursive call on animation end here.
var tot = 0;
var prices = {
"Price1:": 37152.32,
"Price2:": 54023,
"Price3:": 37261.75
};
var pricesArray = Object.keys(prices);
var animationDuration = 20000; // Must be lower than carousel rotation
var dur = animationDuration / pricesArray.length;
function animatePrices() {
key = pricesArray.shift();
start = tot;
tot += prices[key];
if(!key)
return false;
$({
p: start
}).animate({
p: tot
}, {
duration: dur,
easing: 'linear',
step: function(now) {
$("#total").text(now.toFixed(2));
$("#what-for").text(key + " $" + prices[key]);
},
done: animatePrices
});
}
window.onload = animatePrices;
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
$<span id="total"></span>
<br>
<span id="what-for"></span>
If you like, added a ratio for each animation in Rejith R Krishnan code.
I changed the object associated array though, to :
var prices = [
37152.32,
54023,
37261.75
];
and the ratios :
var ratios=[];
for(var i = 0; i < prices.length; i++){
ratios[i]=prices[i]/total;
}
https://jsfiddle.net/a6k9w9k7/

Adding an increase / decrease speed button to slideshow

I need to make a slideshow that has an 'increase speed', 'decrease speed' button as well as pause/play. Im having a hard time and getting confused with the timeout/interval usage.
script:
// creates array and holds images
var imageArray = ['img/br1.jpg', 'img/br2.jpg', 'img/br3.png', 'img/br4.gif', 'img/br5.jpeg', 'img/br6.jpeg', 'img/br7.jpeg'];
// set the array to start at 0
var i = 0;
// create function 'slideShow'
function slideShow() {
// creates variable 'div' to load images into a div selected using 'getElementById'
var div = document.getElementById('slideshowdiv');
div.innerHTML = '<img src="' + imageArray[i] + '" />';
//increment i by 1
i++;
// checks if i is greater than or equal to the length
if(i >= imageArray.length) {
// if true, resets value to 0
i = 0;
};
// every 2 seconds change image
timer = setTimeout('slideShow()', 2000);
};
function stopShow() {
clearTimeout(timer);
};
function playShow() {
timer = setTimeout('slideShow()', 2000);
};
function increase() {
};
function decrease() {
};
html:
<body onload="slideShow();">
<div id="slideshowdiv"></div>
<div class="change">
<button onclick="stopShow()">Stop</button>
<button onclick="playShow()">Play</button>
<button onclick="increase()">Speed up slideshow</button>
<button onclick="decrease()">Slow down slideshow</button>
</div>
you could try to change the fixed value with a var:
// creates array and holds images
var imageArray = ['img/br1.jpg', 'img/br2.jpg', 'img/br3.png', 'img/br4.gif', 'img/br5.jpeg', 'img/br6.jpeg', 'img/br7.jpeg'];
// set the array to start at 0
var i = 0;
var speed = 2000;
var minSpeed = 3000;
var maxSpeed = 0;
// create function 'slideShow'
function slideShow() {
// creates variable 'div' to load images into a div selected using 'getElementById'
var div = document.getElementById('slideshowdiv');
div.innerHTML = '<img src="' + imageArray[i] + '" />';
//increment i by 1
i++;
// checks if i is greater than or equal to the length
if(i >= imageArray.length) {
// if true, resets value to 0
i = 0;
};
// every 2 seconds change image
timer = setTimeout('slideShow()', speed);
};
function stopShow() {
clearTimeout(timer);
};
function playShow() {
timer = setTimeout('slideShow()', speed);
};
function increase() {
if(speed -100 > maxSpeed )
speed -= 100;
};
function decrease() {
if(speed +100 <= minSpeed)
speed += 100;
};
What about something like this?
function playShow(playspeed) {
timer = setTimeout('slideShow()', playspeed);
};
function increase() {
var increase_to=10000;
playshow(increase_to);
};
function decrease() {
var decrease_to=100;
playshow(decrease_to);
}

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