pause and resume setInterval in javascript - javascript

I am trying to mimic the typing effect on the codility's home page in JavaScript.I have already achieved the typing and deleting effect
using setInterval().
Here's the jsfiddle of that:
https://jsfiddle.net/yzfb8zow/
var span=document.getElementById("content");
var strings=["hello world","how r u??"];
var index=0; //get string in the strings array
var chIndex=0; //get char in string
var type=true;
setInterval(function(){
if(index===strings.length)
index=0;
if(type){
typeIt();
}
else
deleteIt();
},200);
// type the string
function typeIt(){
if(chIndex<strings[index].length)
span.innerHTML=strings[index].substring(0,chIndex++);
else
type=false;
}
//delete the string
function deleteIt(){
if(chIndex===0){
index++;
type=true;
}
else
span.innerHTML=strings[index].substring(0,chIndex--);
}
the html
<span id="content"></span>
<span id="cursor">|</span>
the css
#cursor{
-webkit-animation: 1s blink step-end infinite;
-moz-animation: 1s blink step-end infinite;
animation: 1s blink step-end infinite;
}
#keyframes blink {
from, to {
opacity:0;
}
50% {
opacity: 1;
}
}
#-moz-keyframes blink {
from, to {
opacity:0;
}
50% {
opacity:1;
}
}
#-webkit-keyframes blink {
from, to {
opacity:0;
}
50% {
opacity:1;
}
}
What I can't get my head around is how could I pause the setInterval function at the beginning of string change and at the end to get a clear blinking cursor.
I have looked up other answers Pause and resume setInterval and How do I stop a window.setInterval in javascript? but I am unable to comprehend how to use it in this context.
Also it would be great if you could tell me how to improve my code.

I edited your code here is your jsfiddle back: https://jsfiddle.net/yzfb8zow/5/
Also a little code explanation
var typingFunction = function() {
if (index === strings.length) {
index = 0;
}
if (type) {
typeIt();
} else {
deleteIt();
}
}
I declared your previous typing function to later be able to use it as desired.
var x = setInterval(typingFunction.bind(typingFunction), 200);
Stored the interval so I can clear it later - stored in a global variable (not necessarily ok and there are other solutions but store it).
function typeIt() {
if (chIndex == -1) {
chIndex += 1;
clearAndRestartInterval(1000);
return;
}
if (chIndex <= strings[index].length) {
span.innerHTML = strings[index].substring(0, chIndex++);
} else {
clearAndRestartInterval(1000);
type = false;
return;
}
}
In the typeIt function I clear interval at the begining at chIndex = -1, wait a while so letting the cursor blink before I restart the interval. Than I clear it again at the end of the string. My chIndex starts from -1 so I know when to blink at the begining.
Rest of the code is self explanatory.
Feel free to edit the parameter of the clearAndRestartInterval function in order to set the time of the blinking at the begining and the end.
function clearAndRestartInterval(timeOfRestart) {
clearInterval(x);
setTimeout(function() {
x = setInterval(typingFunction.bind(typingFunction), 200);
}, timeOfRestart);
}
Last but not least the stop and restart function of the interval. This is quite simple and x - is the previously declared setInterval - globally which I clear and reset with a new interval ofter some time.
Hope this helps.
Cheers

You will have to use nested timeouts.
Call typeIt directly, as its a default action.
In typeIt, add a setTimeout which will call deleteIt on completion.
On completion of deleteIt, call typeIt again.
Sample Fiddle
You typeit will look something like this:
function typeIt() {
var t_interval = setInterval(function() {
if (chIndex <= strings[index].length)
span.innerHTML = strings[index].substring(0, chIndex++);
else {
type = false;
// This is to be executed once action is completed.
window.clearInterval(t_interval);
}
}, 20)
}

Related

Why does this webworker cause these animations to lag?

I have set up a webworker to run an intensive calculation. On my main page, I have a spinning icon that is animated. However, when the webworker runs, my spinning icon lags. It seems to be browser dependent as well. I believe this is because despite using a separate js thread, the browser is still demanding too many resources from the computation, but I am not sure. Does anyone know why this happens? Or how I can fix it? Short of using a backend, I have no idea what to do.
Full example: https://codesandbox.io/s/modest-bohr-56u5k7?file=/src/App.js
App.js:
import { FaBeer } from "react-icons/fa"; // an icon
function App() {
const myWorker = new Worker('Worker.js');
myWorker.onmessage = function (e) {
console.log('message received from worker', e.data);
};
function work() {
myWorker.postMessage(3000000);
}
return (
<div>
<FaBeer className="spinner"/>
</div>
);
}
Worker.js:
onmessage = function (e) {
let n = 0;
while (n < e.data) {
n++;
}
postMessage("done");
};
Styles.css:
.spinner {
animation: spin infinite 2s linear;
margin-left: 5px;
}
#keyframes spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}

Javascript Add fadeIn to the loading Div

I have this code which loads an external local page into a div:
function load(url) {
document.getElementById("myDiv").innerHTML='<object type="text/html" data="'+url+'"></object>';
return false;
}
How can I make it fadeIn instead of just appearing? I would prefer if it was pure javascript
I found a nice fadeIn function in this answer and applied it to your load function.
Fiddle
function load(url) {
var myDiv = document.getElementById("myDiv");
myDiv.innerHTML='<object type="text/html" data="'+url+'"></object>';
fadeIn(myDiv);
return false;
}
function fadeIn(el) {
el.style.opacity = 0;
var tick = function() {
el.style.opacity = +el.style.opacity + 0.01;
if (+el.style.opacity < 1) {
(window.requestAnimationFrame && requestAnimationFrame(tick)) || setTimeout(tick, 16)
}
};
tick();
}
load('foo');
The easiest way to do so is to add to your html jQuery:
<script src="http://code.jquery.com/jquery-1.9.1.js"></script>
And then in your js file use .fadeIn() method on myDiv.
Remember to link jQuery before you link the .js file
Or if you can't use jQuery create animate classes in css:
#keyframes fadeIn {
to {
opacity: 1;
}
}
.fade-in {
opacity: 0;
animation: fadeIn .5s ease-in 1 forwards;
}
.is-paused {
animation-play-state: paused;
}
and then use this code in js:
var el = document.querySelector('.js-fade');
if (el.classList.contains('is-paused')){
el.classList.remove('is-paused');
}
The last thing you need to do is to add to myDiv classes of js-fade and fade-in is-paused.
The aforementioned code is qute general so adapt it to your needs

jQuery increment variable in recursive function

There are other questions with almost the same title as this but they don't seem to answer my question.
I am trying to make a simple javascript function that uses jQuery to fade div blocks in and out one after the other, and goes back to the first div once it get's to the end. This should continue while the user is on the page, much like a slideshow.
There are 5 'slides' or divs to be shown and hidden in sequence. They have the classes 'content1', 'content2', 'content3' etc.
$(document).ready(function() {
var slide = 1;
nextSlide(slide);
function nextSlide(slide) {
$('.content' + slide.toString()).fadeTo(2000, 1.0).delay(5000).fadeTo(2000, 0.0).delay(2000);
if(slide > 5) {
slide = 1;
} else {
slide++;
}
nextSlide(slide);
};
});
This does not fade each div in and out in sequence, what it actually does is fade all of the slides in and out simultaneously.
How do I get it to reference each slides class in turn and then loop back to the first slide?
Many thanks.
Your function will recurse immediately, dispatching all of the animation requests around the same time.
To stagger them, you should recurse with a timeout:
$(document).ready(function() {
var slide = 1;
nextSlide();
function nextSlide() {
$('.content' + slide.toString()).fadeTo(2000, 1.0).delay(5000).fadeTo(2000, 0.0).delay(2000);
if(slide > 5) {
slide = 1;
} else {
slide++;
}
setTimeout(nextSlide, 2000); // second param is delay in ms
};
});
This will cause the next call to occur after 2000ms. Thanks to closure, your slide variable should be captured and will persist its value between calls.
No need for a timeout. Simply call the next iteration as a callback of the last fadeTo method:
$(document).ready(function() {
var slide = 1;
nextSlide(slide);
function nextSlide(slide) {
$('.content' + slide.toString())
.fadeTo(2000, 1.0)
.delay(5000)
.fadeTo(2000, 0.0, function(){
if(slide > 5) {
slide = 1;
} else {
slide++;
}
nextSlide(slide);
});
};
});
http://jsfiddle.net/3L09b505/

Avoid mouseover mouseleave conflicts

I'm working with this js fiddle here: http://jsfiddle.net/tPx6x/
The animation works like so
You hover over the text, a circle fades in & begins to pulse 1 second later for as long as your mouse is over the text.
When your mouse pointer leaves the text, the pulse stops after one second and the circle fades out.
The issue arises when you do this:
Put your mouse over the text, remove the pointer from the text, THEN place the pointer back over the text before the script has a chance to finish(1-1.4s).
You won't be able to make the circle appear properly agin...you will have to allow the script to reset. That is the problem.
What is the best way to tackle this issue?
Example code:
<div class='circle__title_project-management'>
<h1>project management</h1>
</div>
<div class='circle__project-management hidden'></div>
.circle__project-management, .circle__title_project-management
{
display: inline-block;
cursor: pointer;
}
.circle__project-management
{
margin-left: 8px;
vertical-align: -4.07px;
background-color: transparent;
border: 2px solid #00DBFF;
width: 30px;
height: 30px;
border-radius: 90px;
top: 280px;
left: 40px;
}
.hidden
{
visibility: hidden;
}
.visible
{
visibility: visible;
}
.animate-infinite
{
animation-iteration-count: infinite;
-moz-animation-iteration-count: infinite;
-webkit-animation-iteration-count: infinite;
}
var circleTitle = $('.circle__title_project-management h1');
var circle = $('.circle__project-management');
var initTimeout = 1000;
var initTimeoutPlus = 1400;
circleTitle.mouseover( function() {
circle.removeClass('hidden');
circle.addClass('animated fadeIn');
setTimeout( function() {
circle.addClass('pulse animate-infinite');
circle.removeClass('fadeIn');
}, initTimeout);
});
circleTitle.mouseleave( function() {
setTimeout( function() {
circle.stop().removeClass('pulse animate-infinite visibility');
circle.addClass('fadeOut');
}, initTimeout);
setTimeout( function() {
circle.removeClass('fadeOut');
circle.addClass('hidden');
}, 1400);
});
You should note that setTimeout has a return value. You want to clear previous timeouts before you start new ones; otherwise you can get a timeout queue which completely skews your animations. Something like this:
var myTimeout;
...
clearTimeout(myTimeout);
myTimeout = setTimeout(...);
Not sure if this is exactly what you were going for, but along these lines: http://jsfiddle.net/FYY38/
More info here: http://www.w3schools.com/js/js_timing.asp
Also, it looks like the circle.stop() call is doing nothing (as it's css-animated)
To avoid antagonist behaviours, maybe add a class to your element to tag it when the event is triggered and remove it when another is triggered.
That way you can stay in control of what's going on.
you can set time out to mouse over function to cover the time delay for mouseleave.
note that the first run must be without delay
var initTimeout = 1000;
var initTimeoutPlus = 1400;
var firstrun = true;
circleTitle.mouseover( function() {
if (firstrun) {
initTimeoutPlus = 0;
firstrun = false;
} else initTimeoutPlus = 1400;
setTimeout(function() {
circle.removeClass('hidden');
circle.addClass('animated fadeIn');
setTimeout( function() {
circle.addClass('pulse animate-infinite');
circle.removeClass('fadeIn');
}, initTimeout);
}, initTimeoutPlus);
});
Probably if you just add a key on mouseover, and toggle it after mouseleave, and before you trigger any mouseleave timeout events, check the key, if it is set, ignore, else go ahead and execute mouseleave
this way if the key is "on" it means a mouse over occurred, if it was off, it means the mouseleave occurred and it is still occurring
var key = false;
circleTitle.mouseover( function() {
key = true;
circle.removeClass('hidden');
circle.addClass('animated fadeIn');
setTimeout( function() {
circle.addClass('pulse animate-infinite');
circle.removeClass('fadeIn');
}, initTimeout);
});
circleTitle.mouseleave( function() {
key = false;
setTimeout( function() {
if (!key){
circle.stop().removeClass('pulse animate-infinite visibility');
circle.addClass('fadeOut');
}
}, initTimeout);
setTimeout( function() {
if (!key){
circle.removeClass('fadeOut');
circle.addClass('hidden');
}
}, 1400);
});

Timer won't die javascript

I am doing some long polling.. and I have a function to make a button blink when a certain statement is true.. Here it is:
function blinking(object, x) {
console.log(x);
if(x>0){
var existing_timer = object.data('clock');
if (existing_timer){
clearInterval(existing_timer);
}
timer = setInterval(blink, 10);
function blink() {
object.fadeOut(400, function() {
object.fadeIn(400);
});
}
}
}
Now.. Notice the timer being set as 'timer'. When someone does something that makes the statement false (making x=0), I want the timer to stop making the button blink when it sees that x=0. This may sound easy but I have tried everything ha.
I've been researching and trying different things, but It doesn't seem to work.
If your variable timer is global, then you should be able to clear the interval using that:
clearInterval(timer);
The integer returned from the setInterval is unique to that timer, so that is what you need to clear.
Here's a simple implementation.
http://jsfiddle.net/AQgBc/
var $led = $('#led'),
blinkState = 0,
blinkTimer,
blinkDuration = 300,
blink = function () {
if (blinkState) {
$led.toggleClass('on');
}
console.log('blink');
blinkTimer = setTimeout(blink, blinkDuration);
};
$('button').on('click', function () {
blinkState = (blinkState) ? 0 : 1;
});
blink();
I just wrote my own without duplicating yours, but the most relevant issue, I think, is just using a setTimeout rather than worrying about a setInterval and clearing that interval.
Edit in response to comment:
If you want it to blink until a user action, then stop polling, it's even simpler.
http://jsfiddle.net/AQgBc/1/
var $led = $('#led'),
blinkState = 1,
blinkTimer,
blinkDuration = 300,
blink = function () {
if (blinkState) {
console.log('blink');
$led.toggleClass('on');
blinkTimer = setTimeout(blink, blinkDuration);
}
};
$('button').on('click', function () {
blinkState = (blinkState) ? 0 : 1;
});
blink();

Categories