How to freeze Javascript timeouts and BS4/jQuery animations? - javascript

I have an HTML page that has timeouts. I want to freeze them when you press a button (#pauseButton) and then resume when you press it again, preferably freezing all BS4 and jQuery animations also.
<button id="pauseButton"></button>
<script>
$(document).ready(function(){
setTimeout(function() {
alert("This is an alert")
},10000);
$("#pauseButton").click(function(){
// Pause timeouts and page
});
});
</script>
EDIT
I have been notified that there is a possible duplicate answer, so I am now focusing on pausing animations and other page elements.
That answer shows how to pause timeouts only.

There are many ways to solve this issue. Many of them are mentioned in this question as mentioned by #EmadZamout in the comments.
But, if you are looking for an easy and maybe an alternate way to solve this. Try this. Here I am using requestAnimationFrame to solve the issue
let ran = Date.now(); // contains the last updated time
let time = 0; // time in seconds
let paused = false; // store the state
const func = () => {
if (!paused && Date.now() - ran > 1000) {
time++;
ran = Date.now();
console.log('now')
}
if (time === 8)
return alert('This works!');
requestAnimationFrame(func);
}
func();
document.querySelector('button').addEventListener('click', () => paused = !paused);
<button>Change state</button>
For stopping all the animations of the website, you need to manually stop each animation.
For stopping a jQuery animation, you can use .stop() helper. An example:
let paused = false; // state of the animation
let dir = 'down'; // to store the direction of animation so that the next time continues in the correct direction
let timeDown = 2000; // to animate properly after resuming
let timeUp = 2000; // to animate properly after resuming
// the initial calling of the animation
(function() {
slideDown();
})();
// function which resumes the animation
function animate() {
switch (dir) {
case 'up':
slideUp();
break;
case 'down':
slideDown();
break;
}
}
// a function to animate in the uppward direction
function slideUp() {
dir = 'up'; // setting direction to up
timeDown = 2000; // resetting the duration for slideDown function
$('div').stop().animate({
left: 0
}, {
duration: timeUp,
complete: slideDown, // calling slideDown function on complete
progress: function (animation, progress, ms) {
timeUp = ms; // changing the duration so that it looks smooth when the animation is resumed
}
}); // actual animation
}
// a function to animate in the downward direction
function slideDown() {
dir = 'down'; // setting direction to down
timeUp = 2000; // resetting the duration for slideDown function
$('div').stop().animate({
left: 200
}, {
duration: timeDown,
complete: slideUp, // calling slideUp function on complete
progress: function (animation, progress, ms) {
timeDown = ms; // changing the duration so that it looks smooth when the animation is resumed
}
}); // actual animation
}
// button click event listener
$('button').click(function() {
if (paused)
animate(); // to resume the animation
else
$('div').stop(); // to stop all the animations on the object
paused = !paused; // toggling state
});
div {
position: relative;
width: 100px;
height: 100px;
background: dodgerblue;
}
<button>Pause</button>
<div></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
For bootstrap, I don't think you have any bootstrap animations which needed to be paused in this scenario which you have mentioned since bootstrap animations depend on user interactions. If you want to prevent user interaction, you can put an overlay over the website says "Paused". Or, if you don't want to do that you can use CSS property pointer-events: none to disable all the pointer events.
Now for CSS animations, you can set a property called animation-play-state to paused.
If you want to change the state of the animations to paused when the user is not on the page (As I understood for your updated questions) you can use the new visibilityState API for that. There is an event visibilitychange which is fired when there a change in visibility.
document.addEventListener("visibilitychange", function() {
console.log( document.visibilityState );
document.querySelector('div').innerHTML = document.visibilityState;
});
<div>
Try opening a different tab or change the focus to another app
</div>

Related

Animation glitches when function is triggered again before slideUp animation ends

I use the following script (along with external animate.css) which i call when I want to show a specific notification. The idea is simple; when triggered, animate - (slide in down) the notification box, along with changed notification message. When time is out, animate again (slide out up). Everything works fine, except for when i re-trigger notification function right at the time the previous call is animating out (between the timeout and start of slide up animation - see note in code).
// notification
var timer = '';
var notif = $('#notif');
var notif_txt = $('#notif #notif_txt');
var notif_orig = '#notif';
function err_notif(text = 'Prišlo je do napake!') {
clearTimeout(timer);
notif[0].style.display = 'none';
notif_txt.text(text);
notif[0].style.display = 'inline-block';
anim(notif_orig, 'slideInDown', function() {
timer = setTimeout(function(){
// if user re-triggers the notif() function in time between mark 1 and 2, it glitches out the notification system
// mark 1
anim(notif_orig, 'slideOutUp', function(){
// mark 2
notif[0].style.display = 'none';
});
}, 1500);
});
}
Other code resources:
Animate.css for css animations
https://raw.githubusercontent.com/daneden/animate.css/master/animate.css
anim() function (from animate.css's github page)
function anim(element, animationName, callback) {
const node = document.querySelector(element)
node.classList.add('animated', animationName)
function handleAnimationEnd() {
node.classList.remove('animated', animationName)
node.removeEventListener('animationend', handleAnimationEnd)
if (typeof callback === 'function') callback()
}
node.addEventListener('animationend', handleAnimationEnd)
}
If you want to prevent a concurring animation, you can check if the element already has the animated class.
if(document.querySelector(notif_orig).classList.contains("animated")){
console.log("Concurrent animation prevented.")
}else{
anim(notif_orig, 'slideInDown', function() {
//...
}

How to fade out div when user is inactive

this should be easy but I just can't get it to work as it should... I have a div element serving as a button. I do not want the button to be visible all the time, just to appear when the user touches the screen (with finger or mouse), stays visible for a period of time (say 2 seconds) after inactivity and then disappear.
I do not want it to disappear 2 seconds after it was made visible (for that I could just use jQuery delay), I want it to disappear 2 seconds after the user has stopped interacting with the screen (i.e. the #grid element in my case). As long as the user is touching the screen or moving the mouse the button is visible, when he stops that activity 2 seconds passes and the button goes away.
The following I have, it does not work:
var grid = $('#grid');
grid.bind('mousemove touchmove tap swipeleft swipeup swipedown swiperight', function(e) {
var timer;
var circle= $('.circle-button');
if (!circle.is(":visible")) {
//button is not visible, fade in and tell it to fade out after 2s
$('.circle-button').fadeIn('slow');
timer = setTimeout(function(){ $('.circle-button').fadeOut('slow') }, 2000);
}
else {
//button is visible, need to increase timeout to 2s from now
if (timer) clearTimeout(timer);
timer = setTimeout(function(){ $('.circle-button').fadeOut('slow') }, 2000);
}
});
Even if the above would work it seems very inefficent to me, to reinitiate a timer for each mousemove (not sure this is a real issue though). If someone could help me with a working, reasonably efficient solution, it would be much appreciated.
Cheers!
--- EDIT -----
Thanks for the replies, they are all good. I ended up using Rohan Veer's suggestion below since it seems the most elegant solution to me, not having to reinitiate a timer at each mouse move.
try this --
<script type="text/javascript">
var idleTime = 0;
$(document).ready(function () {
//Increment the idle time counter every minute.
var idleInterval = setInterval(timerIncrement, 60000); // 1 minute
//Zero the idle timer on mouse movement.
$(this).mousemove(function (e) {
idleTime = 0;
});
$(this).keypress(function (e) {
idleTime = 0;
});
});
function timerIncrement() {
idleTime = idleTime + 1;
if (idleTime > 2) { // 2 minutes
// fade out div
}
}
</script>
You can set a timeout of desire time and on elapse, hide it. Following is a Reference JSFiddle.
Code
var interval = null;
function initInterval(){
if(interval)
clear();
showElement();
interval = setTimeout(function(){
$(".btn").fadeOut();
clear();
},2000);
}
function clear(){
window.clearInterval(interval);
interval = null;
}
function showElement(){
$(".btn").fadeIn();
}
function registerEvents(){
console.log("Events registering");
$(document).on("mousemove", function(){
initInterval();
});
}
(function(){
registerEvents();
})()
.btn{
width:200px;
background:blue;
color:#fff;
padding:5px;
text-align:center;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div class="btn">Will hide in 2 secs</div>
Your code seems pretty close to a working solution, I've made only a couple of slight changes. Pretty much the only way to do this is to set a timer on every move, but also clear the previous timer.
The code below works according to your description.
var timer;
var grid = $('#grid');
grid.bind('mousemove touchmove tap swipeleft swipeup swipedown swiperight', function(e) {
var circle= $('.circle-button');
if (timer) clearTimeout(timer);
if (!circle.is(":visible")) {
circle.fadeIn('slow');
}
timer = setTimeout(function(){ circle.fadeOut('slow') }, 2000);
});
#grid{
width:200px;
height: 100px;
border: 1px solid black
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="grid"></div>
<button class="circle-button">A button</button>

prevent animation double click issue

Hi I have problem with my slider please visit this site and check http://paruyr.bl.ee/
after click on my arrows it becomes work in an asynchronous way, ones it changes very fast and then slow and it repeats.
I think it is from start slider and stop slider.
var sliderPrev = 0,
sliderNext = 1;
$("#slider > img").fadeIn(1000);
startSlider();
function startSlider(){
count = $("#slider > img").size();
loop = setInterval(function(){
if (sliderNext>(count-1)) {
sliderNext = 0;
sliderPrev = 0;
};
$("#slider").animate({left:+(-sliderNext)*100+'%'},900);
sliderPrev = sliderNext;
sliderNext=sliderNext+1;
},6000)
}
function prev () {
var newSlide=sliderPrev-1;
showSlide(newSlide);
}
function next () {
var newSlide=sliderPrev+1;
showSlide(sliderNext);
}
function stopLoop () {
window.clearInterval(loop);
}
function showSlide(id) {
stopLoop();
if (id>(count-1)) {
id = 0;
} else if(id<0){
id=count-1;
}
$("#slider").animate({left:+(-id)*100+'%'},900);
sliderPrev = id;
sliderNext=id+1;
startSlider();
};
$("#slider, .arrows").hover(function() {
stopLoop()
}, function() {
startSlider()
});
function onlyNext () {
var newSlide=sliderPrev+1;
onlyShowSlide(newSlide);
}
function onlyShowSlide(id) {
if (id>(count-1)) {
id = 0;
} else if(id<0){
id=count-1;
}
$("#slider").animate({left:+(-id)*100+'%'},900);
sliderPrev = id;
sliderNext=id+1;
};
I think the best option would be to check if the animation is in progress and prevent the action if it is, something like this:
function prev () {
if(!$('#slider').is(":animated"))
{
var newSlide=sliderPrev-1;
showSlide(newSlide);
}
}
function next () {
if(!$('#slider').is(":animated"))
{
var newSlide=sliderPrev+1;
showSlide(sliderNext);
}
}
To illustrate the difference between this and just sticking a stop() in, check this JSFiddle. You will notice some choppy movements if you click multiple times in the stop() version.
What I would do is add a class to your slider when the animation starts and remove the class when it finishes:
$("#slider").animate({left:+(-id)*100+'%'}, {
duration: 900,
start: function() {
$('#slider').addClass('blocked');
},
complete: function() {
$('#slider').removeClass('blocked');
}
});
Now check on each click event if the slider is blocked or not:
function next () {
if (!$('#slider').hasClass('blocked')) {
var newSlide=sliderPrev+1;
showSlide(sliderNext);
}
}
This is a very simple solution, I'm sure there is a better one.
EDIT: As marcjae pointed out, you could stop the animations from queuing. This means when you double click, the slideshow still will move 2 slides. With my approach the second click will be ignored completely.
You can use a variable flag to control if the animation is still being done, or simply use .stop() to avoid stacking the animation.
$("#pull").click(function(){
$("#togle-menu").stop().slideToggle("slow");
});
It is occurring because your animations are being queued.
Try adding:
.stop( true, true )
Before each of your animation methods. i.e.
$("#slider").stop( true, true ).animate({left:+(-id)*100+'%'},900);
The answers about stop are good, but you have a bigger issue that is causing the described behavior. The issue is here:
$("#slider, .arrows").hover(function() {
stopLoop()
}, function() {
startSlider()
});
You have bound this to the .arrows as well as the #slider and the arrows are contained within the slider. So, when you mouse out of an arrow and then out of the entire slider, you are calling start twice in a row without calling stop between. You can see this if you hover onto the arrow and then off of the slider multiple times in a row. The slides will change many times after 6 seconds.
Similarly, consider the case of a single click:
Enter the `#slider` [stopLoop]
Enter the `.arrows` [stopLoop]
Click the arrow [stopLoop]
[startSlider]
Leave the `.arrows` [startSlider]
Leave the `#slider` [startSlider]
As you can see from this sequence of events, startSlider is called 3 times in a row without calling stopLoop inbetween. The result is 3 intervals created, 2 of which will not be stopped the next time stopLoop is called.
You should just have this hover on the #slider and more importantly, add a call to stopLoop as the first step in startSlider. That will ensure that the interval is always cleared before creating a new one.
$("#slider").hover(function() {
stopLoop()
}, function() {
startSlider()
});
function startSlider(){
stopLoop();
/* start the slider */
}

Non-linear "animation" of numeric value with JavaScript/jQuery

I want to make an animation like this one in Google Analytics on active users.
I saw some scripts that do the animation, but they do it in linear mode, like, the speed is the same from 0 to XXX, I want it to start slowly, gain speed, and finish slowly again.
How would I do this in javascript/jquery?
As requested, what I tried:
<span id="counter">0</span>
$(function ()
{
var $counter = $('#counter'),
startVal = $counter.text(),
currentVal,
endVal = 250;
currentVal = startVal;
var i = setInterval(function ()
{
if (currentVal === endVal)
{
clearInterval(i);
}
else
{
currentVal++;
$counter.text(currentVal);
}
}, 100);
});
But I don't think it's the way to go...
I would use jQuery's built-in animation for this.
If you pass a function to the step option for .animate(), it will be fired for each tick during animation. That way, jQuery will handle all of the easing and what not for you. You just need to handle the data.
$({countValue:0}).animate(
{countValue:346},
{
duration: 5000, /* time for animation in milliseconds */
step: function (value) { /* fired every "frame" */
console.log(value);
}
}
);
In the console, you will see values between 0 and 346, complete with easing.

Detecting when the mouse is not moving

I am able to find the cursor position. But I need to find out if the mouse is stable. If the mouse wasn't moved for more than 1 minute, then we have to alert the user.
How its possible, are there any special events for this? (Only for IE in javascript)
Set a timeout when the mouse is moved one minute into the future, and if the mouse is moved, clear the timeout:
var timeout;
document.onmousemove = function(){
clearTimeout(timeout);
timeout = setTimeout(function(){alert("move your mouse");}, 60000);
}
Here's a one-and-done function that can check any element for movement:
function mouse (element, delay, callback) {
// Counter Object
element.ms = {};
// Counter Value
element.ms.x = 0;
// Counter Function
element.ms.y = function () {
// Callback Trigger
if ((++element.ms.x) == delay) element.ms.callback(element, element.ms);
};
// Counter Callback
element.ms.callback = callback;
// Function Toggle
element.ms.toggle = function (state) {
// Stop Loop
if ([0, "off"][state]) clearInterval(element.ms.z);
// Create Loop
if ([1, "on"][state]) element.ms.z = setInterval(element.ms.y, 1);
};
// Function Disable
element.ms.remove = function () {
// Delete Counter Object
element.ms = null; return delete element.ms;
};
// Function Trigger
element.onmousemove = function () {
// Reset Counter Value
element.ms.x = -1;
};
// Return
return element.ms;
};
Usage:
mouse(element, delay, callback)
Examples:
Make a video player hide the mouse after 5 seconds when idle and fullscreen
let x = mouse(video, 5000, function (a) {
if (document.webkitIsFullScreen) video.style.cursor = "none";
});
x.toggle(1); addEventListener("mousemove", function () {
video.style.cursor = "auto";
});
Chat Room AFK (45 Seconds) (assuming you have a chat box and a send message function):
let x = mouse(chatBox, (45e3), function (a) {
chatBox.send({ text: chatBox.username + " is AFK.", italic: true });
});
x.toggle(1); x.addEventListener("mousemove", function () {
chatBox.send({ text: chatBox.username + " is no longer AFK", italic: true });
});
Is there not a way to set a timer to start incrementing after every mouse movement event?
If it gets to a minute then pop up the message box, but every time the mouse moves the timer gets reset.
Use a timer that resets its value on mousemove event.
If timer reaches 1 minute --> Do something.
More info on timer here http://www.w3schools.com/js/js_timing.asp
And more info on catchin mouse events here http://www.quirksmode.org/js/events_mouse.html
Yes, you have a onmousemove event in Javascript, so to achieve what you need you just have to do code something like this:
startTimer();
element.onmousemove = stopTimer(); //this stops and resets the timer
You can use it on the document body tag for instance.
UPDATE: #Marius has achieved a better example than this one.
You can use the onmousemove event. Inside it, clearTimeout(), and setTimeout(your_warning, 1 minute).
You could use this script/snippet to detect the mouse pointer position and "remember" it. Then use a timer "setTimeout(...)" to check the position let's say every second and remember that time.
If more than one minute passed and the position hasn't changed, you could alert the user.

Categories