Stopping a Function After It Has Fulfilled a Requirement - javascript

That is the best way I could describe it. Basically, I have a jpg that I have scrolling across the screen onLoad. I should be able to stop the image, and make it hidden after it has made 3 descending passes across the screen. I have been at it for a couple hours now, but can't figure out how to make it work without the use of an onClick button. I need it to stop on its own. Here is the script:
function moveit()
{
dom=document.getElementById("roman").style;
dom.top= parseInt(dom.top)+tinc+"px";
dom.left= startleft+"px";
dom.visibility= "visible";
startleft=startleft+linc;
if (startleft<= 20)
{linc=linc*-1;
window.document.roman.src="roman.jpg"; }
if (startleft>= window.screen.width-10)
{linc=linc*-1;
window.document.roman.src="roman.jpg"; }
to=setTimeout("moveit();", 100) ;
}
And the body looks like this:
<body onload="moveit()">
<div id="roman" style="position:absolute; top: 0px; left: 0px; visibility: hidden;">
<img name="roman" src="roman.jpg"/>
</div>
<form>
<div id="button" style="position:absolute; top: 315px; left: 10px;">
<input type="button" value="stop" onClick="clearTimeout(to)">
</div>
</form>
I have tried several ways, but have not been able to remove the button aspect. I would like the image to makes 3 full passes from right to left, stop, and then become hidden. Any thoughts oh how to do it without the button?

You want to use a counter variable to keep track of how many times your image bounces horizontally. Once that counter reaches a certain number, don't reset the timeout:
var passes = 0;
var imageStyle = document.getElementById("roman").style;
imageStyle.visibility = "visible";
// Referring to the image with document.roman isn't recommended; give it an ID
// and use getElementById instead.
window.document.roman.src = "roman.jpg";
function moveit() {
imageStyle.top = parseInt(dom.top) + tinc + "px";
imageStyle.left = startleft + "px";
startleft = startleft + linc;
if (startleft <= 20 || startleft >= window.screen.width - 10) {
linc = linc * -1;
passes++;
}
if (passes < 3) {
to = setTimeout(moveit, 100);
}
}
Notice that I removed some redundant code. You don't have to set the visibility or image source every time the timer fires. Just do it at the beginning.

Related

How can I have a slot machine effect using jQuery and CSS

I want to make a slot machine. I am taking random index from array and populating it inside my div. But the only issue is that I want to have a slot machine effect. I mean that the effect should be like numbers are dropping from top to bottom. This is my code so far.
var results = [
'PK12345',
'IN32983',
'IH87632',
'LK65858',
'ND82389',
'QE01233'
];
// Get a random symbol class
function getRandomIndex() {
return jQuery.rand(results);
}
(function($) {
$.rand = function(arg) {
if ($.isArray(arg)) {
return arg[$.rand(arg.length)];
} else if (typeof arg === "number") {
return Math.floor(Math.random() * arg);
} else {
return 4; // chosen by fair dice roll
}
};
})(jQuery);
// Listen for "hold"-button clicks
$(document).on("click", ".wheel button", function() {
var button = $(this);
button.toggleClass("active");
button.parent().toggleClass("hold");
button.blur(); // get rid of the focus
});
$(document).on("click", "#spin", function() {
// get a plain array of symbol elements
var symbols = $(".wheel").not(".hold").get();
if (symbols.length === 0) {
alert("All wheels are held; there's nothing to spin");
return; // stop here
}
var button = $(this);
// get rid of the focus, and disable the button
button.prop("disabled", true).blur();
// counter for the number of spins
var spins = 0;
// inner function to do the spinning
function update() {
for (var i = 0, l = symbols.length; i < l; i++) {
$('.wheel').html();
$('.wheel').append('<div style="display: none;" class="new-link" name="link[]"><input type="text" value="' + getRandomIndex() + '" /></div>');
$('.wheel').find(".new-link:last").slideDown("fast");
}
if (++spins < 50) {
// set a new, slightly longer interval for the next update. Makes it seem like the wheels are slowing down
setTimeout(update, 10 + spins * 2);
} else {
// re-enable the button
button.prop("disabled", false);
}
}
// Start spinning
setTimeout(update, 1);
});
// set the wheels to random symbols when the page loads
$(function() {
$(".wheel i").each(function() {
this.className = getRandomIndex(); // not using jQuery for this, since we don't need to
});
});
.wheel {
width: 25%;
float: left;
font-size: 20px;
text-align: center;
}
.wheel .fa {
display: block;
font-size: 4em;
margin-bottom: 0.5em;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css">
<div id="wheels">
<div class="wheel clearfix">
</div>
<!-- add more wheels if you want; just remember to update the width in the CSS -->
</div>
<p class="text-center">
<button id="spin" type="button" class="btn btn-default">Spin</button>
</p>
I managed to create a similar effect by using prepend() rather than append(), and adding a set height and hiding the overflow of the wheel.
CSS:
.wheel {
...
height: 34.4px;
overflow: hidden;
}
JS:
$('.wheel').prepend('<div style="display: none;" class="new-link" name="link[]"><input type="text" value="' + getRandomIndex() + '" /></div>');
//Using "first-of-type" rather than "last"
$('.wheel').find(".new-link:first-of-type").slideDown("fast");
See it working here.
Like so many animations it's a lot easier to fake this animation by reversing what appears to be happening, rather than making it work "correctly".
Use the code you have now to generate a result. Then create an animation for a "spinning wheel", you could shuffle divs, or you could make a 3d wheel in css. While the faces are spinning, do some calculations to decide where the wheel should stop to match your results. Then work backwards from there: You'll want to trigger your "stopping" animation so that the face is showing. Your stopping animation would be a predetermined amount of rotation and speed so that a face can be reliably shown. Depending on how fast your wheel spins, the user may lose track, if this is acceptable it may not matter when you trigger as no one could see the wheel jump.
A simulation on the other hand would use a physics model...

How to make setInterval of a progress bar work when a tab is inactive

I can see there are a couple of similar questions asked here but unfortunately I couldn't find the answer I expect.
I am quite new to Programming and trying my hands on Javascript Progress Bar. I have a counter to countdown whenever the progress bar runs out of width but i got the problem, when the tab in focus is inactive, the progress bar pauses thereby keeping counter not to countdown.
I got the idea of using web workers http://www.tutorialspoint.com/html5/html5_web_workers.htm but I couldn't get that to work. I would appreciate any form of help I get here.
Below is my Code.
<!DOCTYPE html>
<html>
<head>
<script
src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/
jquery.min.js">
</script>
<style>
#progressContainer {
position: relative;
width: 97%;
height: 25px;
background-color: #ddd;
margin-bottom: 15px;
}
#progressBar {
position: absolute;
width: 0%;
height: 100%;
background-color: #A9A9A9;
}
#container{
width: 200px;
height: 200px;
border: 1px solid black;
padding: 10px
}
</style>
<script>
$(document).ready(function(){
$("#countDownBtn").click(function(){
var cdNumber = $("#countDownId").val();
var id = setInterval(frame, 100);
var elem = document.getElementById("progressBar");
var progressBarWidth = 101;
function frame() {
if (progressBarWidth === 0) {
clearInterval(id);
cdNumber--;
$("#countDownId").val(cdNumber);
console.log(cdNumber);
if (cdNumber === 0) {
clearInterval(id);
}
else {
elem.style.width = '100%';
progressBarWidth = 100;
//alert("Hi");
}
}
else {
progressBarWidth--;
elem.style.width = progressBarWidth + '%';
}
}
});
});
</script>
</head>
<body>
<div id="container">
<div>
<input type="text" id="countDownId" value="">
<button id="countDownBtn" class="btn">Click</button>
</div><br>
<div id="progressContainer">
<div id="progressBar"></div>
</div>
</div>
</body>
You'll always run into such problems when depending on the precision of some interval; even requestAnimationFrame. They ain't precise.
The better approach (not just in this case, but pretty much every time you have to transition a value over time) is to save the startTime and compute the passed time in the interval (as #Nosyara already suggested).
When dealing with scaling-factors and/or pausing of this stuff, things can get messy again. Here a utility for this task:
// The basic concept of this "Clock" is a linear equation over Date.now()
// plus the logic to make this equation pausable.
// therefore it's completely independant of **any** interval; it's just math.
function Clock(v){
var p=true,m=1,b=+v||0,n=Clock.now;
return Object.assign(Object.create(Clock.prototype),{
// getter() / setter(value)
// I don't use real getter and setter, because this syntax
// allows/implements method-chaining
value(v){return arguments.length?(b=(+v||0)-(!p&&m*n()),this):b+(!p&&m*n())},
speed(v){return arguments.length?(v=+v||0,p||v===m||(b+=n()*(m-v)),m=v,this):m},
paused(v){return arguments.length?(((v=!!v)===p)||(b+=n()*((p=v)?m:-m)),this):p},
});
}
Object.assign(Clock.prototype,{
valueOf(){return this.value()},
//aliases for setting the paused() state; doesn't matter if you call them repeatedly.
start(){return this.paused(false)},
stop(){return this.paused(true)},
});
Clock.now=Date.now; //the function used for timing
//Clock.now=performance&&performance.now?performance.now.bind(performance):Date.now;
Now to your code:
$(function(){
function frame(){
//yes, countDown get's converted to Number
var value = Math.max(0, countDown);
var count = Math.ceil(value);
var progress = value % 1;
$progressBar.width( progress * 100 + "%" );
//so that I don't update $countDown.val() on every frame, but only if necessary
//on the other hand, it wouldn't be that bad.
if(count !== lastCount) $countDown.val( lastCount = count );
//either stop the countDown or request the next frame.
if(value > 0) requestAnimationFrame(frame);
else countDown.stop();
}
//create a Clock and set speed. Clock is paused by default.
var countDown = Clock().speed( -1 / 10000/*ms*/ );
var $progressBar = $("#progressBar");
var $countDown = $("#countDownId");
var lastCount;
$("#countDownBtn").click(function(){
//if !countDown.paused() then there already is a pending `requestAnimationFrame(frame)`
//from the last call of frame()
if(countDown.paused()) requestAnimationFrame(frame);
countDown.value( $countDown.val() ).start();
});
})
I had similar problems in one of my projects in Chrome browser. The root of problem, that Chrome allows up to 1 timer event per second (setTimeout or setInterval) if tab is not active. In case there more than 1 call per second - it creates queue, than behavior of page depends on logic inside events and may look not as expected. One of solutions is to check visibility of the page and manage intervals Check here
As #ManoDestra pointed out in the comment, you should use requestAnimationFrame instead of setInterval.
The best solution I would propose is utilizing the HTML5 Visibility API to detect when a tab becomes active again after being inactive, and then update the progress bar accordingly. Perhaps you could store the timestamp of the countdown when it is initialized, and when the tab becomes active again, you look at the new timestamp and make a comparison.
You can use setTimeout instead and use recursion. It could look something like this:
var stopInterval = false;
frame();
function frame() {
if(stopInterval) return;
// Your stuff to run in interval HERE
setTimeout(frame, 100);
}
Webworkers are not supported in older browsers. Also browsers specify how they handle setTimeout and setInterval on inactive tab individually, so the behavior may differ. Chrome seems to slow down the recursion alot (1 per second?).
Specifically in your case you can use clock time to represent right progress. When you don't want trust browser about interval events.
Let say you want 10 sec countdown:
var tm = new Date().getTime() + 10000; // 10 sec in milliseconds
setInterval(function(){
var secondsPassed = (tm - new Date().getTime()) / 1000;
// update UI
}, 100); // You can use variable here in different visibility modes

Why won't my HTML Images fade

I'm trying to create a simple slideshow effect. I have 10 images, and I've created a basic HTML page with 2 buttons to go to the right or left image. On clicking the button, the images change.
Now, I'm trying to add a basic fade functionality to the changing image. But the fade effect isn't getting displayed. When I put alerts, I notice that the fade is taking place, but without the alerts it is too fast to be visible. Also, it is happening on the previous image, instead of the next one.
<html>
<head>
<style>
.main {
text-align: center;
}
.centered {
display: inline-block;
}
#image {
border: solid 2px;
width: 500px;
height: 500px;
}
#number {
font-size: 30px;
}
</style>
<script>
function goLeft() {
var image = document.getElementById("image");
var pos = document.getElementById("number");
if(Number(pos.innerHTML)==1) {
image.src = "Images\\10.jpg"
pos.innerHTML = 10;
} else {
image.src = "Images\\" + (Number(pos.innerHTML)-1).toString() + ".jpg"
pos.innerHTML = (Number(pos.innerHTML)-1).toString();
}
for (var i=0; i<25; i++) {
setTimeout(changeOpacity(image, i), 1000);
}
}
function changeOpacity(image, i) {
alert(parseFloat(i*4/100).toString());
image.style.opacity = (parseFloat(i*4/100).toString()).toString();
}
function goRight() {
var image = document.getElementById("image");
var pos = document.getElementById("number");
if(Number(pos.innerHTML)==10) {
image.src = "Images\\1.jpg"
pos.innerHTML = 1;
} else {
image.src = "Images\\" + (Number(pos.innerHTML)+1).toString() + ".jpg"
pos.innerHTML = (Number(pos.innerHTML)+1).toString();
}
for (var i=0; i<25; i++) {
setTimeout(changeOpacity(image, i), 1000);
}
}
</script>
</head>
<body>
<div class="main">
<div class="centered">
<img id="image" src="Images\1.jpg">
</div>
</div>
<div class="main">
<div class="centered">
<span id="number">1</span>
</div>
</div>
<div class="main">
<div class="centered">
<button onclick="goLeft()" style="margin-right:50px;">Go Left</button>
<button onclick="goRight()" style="margin-left:50px;">Go Right</button>
</div>
</div>
</body>
The problem is this block of code that is in your goLeft method, and goRight method:
for (var i=0; i<25; i++) {
setTimeout(changeOpacity(image, i), 1000);
}
You are creating 25 timers that, and each timer will execute approximately 1 second later.
Creating animations is best left to the CSS.
In your CSS add:
#image {
transition: opacity 0.5s ease;
}
And then in your JavaScript, simply: image.style.opacity = 1.0;
When the opacity changes, CSS will automatically transition the opacity length at the speed defined in the css, e.g 0.5s. Feel free to experiment.
I also added a jsfiddle here: http://jsfiddle.net/dya7L8wq/
You misunderstood setTimeout and the for loop.
Norman's answer provides a good solution with CSS, but he doesn't talk too much about why your code is not working. So I'd like to explain.
for (var i=0; i<25; i++) {
setTimeout(changeOpacity(image, i), 1000);
}
You assumption is:
invoke changeOpacity(image, 0) after 1 second
invoke changeOpacity(image, 1) 1 second after step 1
invoke changeOpacity(image, 2) 1 second after step 2
invoke changeOpacity(image, 3) 1 second after step 3
....
And the last step is invoking changeOpacity(image, 24) 1 second after previous step.
What actually happens is:
The loop is finished almost immediately!
In each iteration, setTimeout queues an asynchronous function invocation, and it's done! That says, it will return right away, rather than wait until changeOpacity returns.
And then, after about 1 second, changeOpacity fires 25 times almost at the same time, because you queued it 25 times in the for loop.
Another problem here is: in changeOpacity invocations, passed-in parameter i are not 1, 2, 3...., they all have the same value that causes for loop to exit (1 second ago) - 25, because JS doesn't have a block scope prior to ES6 (in ES6 we have keyword let for it).
In a pure JS solution, to ensure the time sequence we'd usually queue next invocation at the end of every step:
function changeOpacity() {
// do something here
// before the function returns, set up a future invocation
setTimeout(changeOpacity, 1000)
}
Here's an example to print a list of numbers from 1 to 5:
var go = document.getElementById('go')
var op = document.getElementById('output')
var i = 0
function printNum() {
var p = document.createElement('p')
p.innerHTML = ++i
op.appendChild(p)
// next step
if(i < 5) {
setTimeout(printNum, 500)
}
}
go.onclick = printNum
<button id="go">GO</button>
<div id="output"></div>
Why use pure JavaScript?
Use jQuery.
It has a pretty neat fadeTo() function and a useful fadeIn() function.
Might wanna use that ;)

how can I display the text in an ordered list using javascript

Could someone please check my code? Thank you
Here is the fiddle site if you want to test:
http://jsfiddle.net/66QYr/
I would like to have the first 3 text to appear on the left (vertically)
and then the next 3 text appear on the right (vertically)
then the next 2 text appear on the lower right bottom (vertically)
and the last 2 text appear on the lower left bottom (vertically)
http://i197.photobucket.com/albums/aa253/tintingerri/Test/pic001.png
<html>
<head>
<title>tintin</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<style type="text/css">
#tintin{
position: relative;
top: 211px;
left: 12px;
font-size:14pt;
font-weight:bold;
font-family: Calibri;
color:red;
filter:alpha(opacity=0);
opacity:0;}
.image{
height:350px;
width: 855px;}
</style>
<script type="text/javascript">
var txt=['text1','text2', 'text3', 'text4', 'text5', 'text6', 'text7', 'text8', 'text9', 'text10'], init=0,i=0,k=0,speed=20,el;
var loopCount=1000;
var j=0;
//var padd = 20; //set this to an approriate increment
function fade(){
init==0?i++:i--;
el.filters?el.style.filter='alpha(opacity='+i+')':el.style.opacity=i/100;
el.firstChild.nodeValue=txt[k];
if(i==100)init=1;
if(i==0) {init=0;k++;j++;
el.style.paddingLeft=20*k;
}
if(k==txt.length)k=0;
if (j<loopCount) setTimeout('fade()',speed);
}
window.onload=function(){
el=document.getElementById('tintin');
fade();
}
</script>
</head>
<body>
<div id="tintin"> </div>
<div class="image" style="background-image:url('pic007s.jpg')">;
</div>
</body>
</html>
There are two problems you're trying to solve here:
Positioning the text in the appropriate places
Getting them to fade in
Step One
The first problem can be solved with some simple CSS. Start out with a container:
#container {
position:relative;
width:150px;
height:150px;
}
<div id="container"></div>
The width and height can be anything, but you do have to tell it something. We're going to be putting our text in this container, but then use position:absolute. This will take them out of the normal document flow, and collapse the container if we have told it an explicit height.
The next step is the text. You're going to want four divs, with the text inside as paragraphs:
<div class="text" id="text1">
<p>text 1</p>
<p>text 2</p>
<p>text 3</p>
</div>
Do this for each of the four blocks of text that you want to have. Use the same class name on each one, but give each their own, unique ID (text2, text3, etc.).
Finally, just use (as I said earlier) absolute positioning to place them where you'd like:
.text { position:absolute; }
#text1 { top:0; left:0; }
#text2 { top:0; right:0; }
...and so on. When you're done, you should have something that looks like this:
Step Two
Fading elements in requires animation. You kind of have a basic animation function, but I suggest you read Robert Penner's article on tweening and animation. It was written for ActionScript, but the exact same principles apply.
For now, here's a good general-purpose JavaScript method that will take an element and fade it in:
function fadeIn(totalTime, elem, after) {
var cos = Math.cos,
PI = Math.PI,
startTime = +new Date(),
endTime = startTime + totalTime,
timer;
elem.style.opacity = 0;
elem.style.filter = 'alpha(opacity=0)';
timer = setInterval(function () {
var currentTime = +new Date();
currentTime = currentTime > endTime ? 1 : (currentTime - startTime) / totalTime;
var distance = (1 - cos(currentTime * PI)) / 2;
elem.style.opacity = distance;
elem.style.filter = 'alpha(opacity=' + distance * 100 + ')';
if (currentTime === 1) {
clearInterval(timer);
if (after) {
after();
}
}
}, 40);
}
You tell this function how long you want the animation to last (in milliseconds), and you can also give it a function to execute when the fading is done (if you want; it's not necessary).
If I understood your question correctly, you want all the texts to start invisible, and then fade in, one at a time, clockwise from the top. We can make them invisible with CSS, but then if the user has JS disabled, the page will appear blank. So you need to first "get" all of the elements (either with some kind of getByClass function or with four different calls to getElementById) and set their opacity to 0.
So you can make the first group of texts fade in by doing the following:
var text1 = document.getElementById('text1');
fadeIn(1000, text1);
The problem is, by doing this, there's no way to tell when to start the next animation. So we need to make a function, with the help of closures, to help keep track of things (this assumes that you've already gotten the elements in JS and made them invisible):
var tracker = (function () {
var texts = [text1, text2, text3, text4],
i = 0;
return function () {
var text = texts[i];
if (text) {
fadeIn(1000, text, tracker);
i += 1;
}
};
}());
This function cycles through each element and fades it in when the previous one is done. (It's okay if the code doesn't make a lot of sense; closures are tricky things.)
Here is the final result, in JSFiddle. Good luck.

How can I invoke a javascript function div upon divs overlapping

I have 2 divs, one positioned absolutely right: 0 and the other relatively positioned center screen. When the window's width is too small, they overlap. How can I invoke a javascript function when this happens?
Thanks.
Mike
Edited to make clearer.
To check for overlapping div's you might wanna do a check once the page is loaded, and whenever the window is resized:
window.onload = checkOverlap;
window.onresize = checkOverlap;
And then use some offset-checking:
function checkOverlap() {
var centerBox = document.getElementById('centerDiv');
var rightBox = document.getElementById('rightDiv');
console.log("centerbox offset left: " + centerBox.offsetLeft);
console.log("centerbox width: " + centerBox.offsetWidth);
console.log("rightbox offset left: " + rightBox.offsetLeft);
if ((centerBox.offsetLeft + centerBox.offsetWidth) >= rightBox.offsetLeft) {
centerBox.style.display = "inline-block";
} else {
centerBox.style.display = "block";
}
}
You might wanna do some more checks in the function, e.g. to see if the box is already displayed inline, and such. But that should give you a good place to start.
edit: added some diagnostics and fixed error
Part 1:
Do it like this:
<script type="text/javascript">
document.getElementById('example').style.display = "inline";
</script>
...
<div id="example"> ... </div>
document.getElementById('div_id').style.display = 'inline-block'
document.getElementById('div_id').offsetWidth gives us width of div
offsetHeight, offsetLeft, offsetTop are useful also.

Categories