i have e video that i am scrolling with my mousewheel.
Everything is working fine but i would like to check if the user is still scrolling and if it is not so after 10 seconds i would like the video/scrollposition to set-back to zero.
The set-back is working but it is not happing after the "Interval". It is jumping back immediately.
This is my code so far:
const action = document.querySelector(".action");
const video = action.querySelector("video");
//Scroll Magic scenes
const controller = new ScrollMagic.Controller();
let scene = new ScrollMagic.Scene({
duration: 200000, //64000
triggerElement: action,
triggerHook: 0
})
.addIndicators()
.setPin(action)
.addTo(controller);
//Scroll Magic video animation
let accelAmount = 0.1;
let scrollpos = 0;
let currentTime = video.currentTime;
let lastcurrentTime;
let delay = 0;
scene.on("update", e => {
scrollpos = e.scrollPos / 3000; //the higher the number, the slower
});
//Move
setInterval(() => {
delay += (scrollpos - delay) * accelAmount;
video.currentTime = delay;
console.log(video.currentTime + " reading");
lastcurrentTime = video.currentTime;
}, 33.3);
//check if is still scrolling after x seconds
setInterval(checkTime, 10000);
//funktion to execute the check
function checkTime() {
console.log("waiting for new reading");
console.log(video.currentTime + " newreading");
currentTime = video.currentTime;
if (currentTime === lastcurrentTime) {
//is not scrolling -- go to start
//video.currentTime = 0;
window.scrollTo(0, 0);
}
}
You are updating the lastcurrentTime every 33.3ms and you're calling checkTime every 10s. So almost every time you call the checkTime, the lastcurrentTime will be synced with the video.currentTime.
I think that I would try something like this:
let setbackTimeout
scene.on("update", e => {
clearTimeout(setbackTimeout);
scrollpos = e.scrollPos / 3000; //the higher the number, the slower
setbackTimeout = setTimeout(checkTime, 10000);
});
every time you get an update you clear the countdown to setback and create another that you execute 10s later.
Listen to the scroll event listener. With a debounce-like function you can start a timeout that will run whenever the use stops scrolling.
The example below uses the aformentioned technique and shows a message whenever the user stops scrolling for 1 second.
function scrollTracker(delay, callback) {
let timeout = null;
return function(...args) {
if (timeout !== null) {
clearTimeout(timeout);
timeout = null;
}
timeout = setTimeout(callback, delay, ...args)
};
}
const tracker = scrollTracker(1000, () => {
console.log('User stopped scrolling. Handle your video here');
});
window.addEventListener('scroll', tracker);
#page {
height: 20000px;
}
<div id="page"></div>
I think your concept is wrong. I made a little demo how to implement it. It will print the message after 4 sec "inactivity" on button.
const btn = document.querySelector("button")
let myInterval = setInterval(myPrint, 4000)
btn.addEventListener("click", () => {
clearInterval(myInterval)
myInterval = setInterval(myPrint, 4000)
})
function myPrint(){
clearInterval(myInterval)
console.log("programming <3")
}
<button>Click me!</button>
Related
when i click my button, a timer is supposed to display a countdown timer. But the button does not work.
let timerCounter = document.getElementById("timer-counter");
let timer;
let timerCount;
function startTimer() {
timer = setInterval(function() {
timerCount--;
timerElement.textContent = "Time; " + timerCount;
if (timerCount === 0) {
clearInterval(timer);
}
});
}
startButton.addEventListener("click", startTimer);
This is what I found so far:
You are decrementing the timerCount, need to specify the initial value for it to work.
You're using timerElement instead of timerCounter that you've declared.
You must pass the second args to the setInterval which is delay.
const timerCounter = document.getElementById('timer-counter');
const startButton = document.getElementById('start-button');
let timer;
let timerCount = 30;
startButton.addEventListener('click', startTimer);
function startTimer() {
timer = setInterval(function () {
timerCount--;
timerCounter.textContent = 'Time; ' + timerCount;
if (timerCount === 0) {
clearInterval(timer);
}
}, 1000);
}
<div id="timer-counter"></div>
<button id="start-button">Start</button>
Here's a slightly different approach that avoids some of the problems with global variables. The function the listener calls initialises the count, and then returns a new function (a closure) that is called when the button is clicked. It also uses setTimeout which I find more easy to understand.
// Cache your elements
const counter = document.querySelector('#counter');
const startButton = document.querySelector('button');
// Initialise your count variable
function startTimer(count = 30) {
// Return a function that is called from
// the listener
return function loop () {
// Disabled the button once it's been clicked
if(!startButton.disabled) startButton.disabled = true;
counter.textContent = `Time: ${count}`;
if (count > 0) {
setTimeout(loop, 500, --count);
}
}
loop();
}
// Call startTimer to initialise the count, and return
// a new function that is used as the listener
startButton.addEventListener('click', startTimer(), false);
<div id="counter"></div>
<button>Start</button>
I'm sure this could be improved.
In this example we don't go below 0.
We don't allow timeout collisions ( timeouts don't stack causing weird counting speeds ).
We can reset to the original number when on 0.
const c = document.getElementById('timer-counter')
const b = document.getElementById('start-button')
let timer = false
let timerCount = 30
b.addEventListener('click', start)
function decrement() {
if(timerCount < 0) {
timerCount = 30
timer = false
return
}
c.innerText = `Count: ${timerCount}`
timerCount--
timer = setTimeout(decrement, 200)
}
function start() {
if(timer) return
decrement()
}
<div id="timer-counter"></div>
<button id="start-button">Start</button>
I have the following countdown function in javascript:
countdownTimer() {
// exit method if it is active
if(this.isCountdownActive == true){
return;
}
// first time set true
this.isCountdownActive = true
this.countdown = 10
// Define the work to be done
var doWork = () => {
if(this.countdown <= 0) {
ticker.stop();
this.countdown = 10
this.isCountdownActive = false
if (this.thisUser.captain) {
Store.submitTurnEnd();
}
}
this.countdown -= 1;
};
// Define what to do if something goes wrong
var doError = function() {
console.warn('The drift exceeded the interval.');
};
// (The third argument is optional)
var ticker = new Util.AdjustingInterval(doWork, 1000, doError);
ticker.start()
},
Here is the adjusting interval function
function AdjustingInterval(workFunc, interval, errorFunc) {
var that = this;
var expected, timeout;
this.interval = interval;
this.start = function() {
expected = Date.now() + this.interval;
timeout = setTimeout(step, this.interval);
}
this.stop = function() {
clearTimeout(timeout);
}
function step() {
var drift = Date.now() - expected;
if (drift > that.interval) {
// You could have some default stuff here too...
if (errorFunc) errorFunc();
}
workFunc();
expected += that.interval;
timeout = setTimeout(step, Math.max(0, that.interval-drift));
}
}
I believe this should work, however the timer still runs too fast occasionally and doesn't reset correctly. I would say 75% of the time it works fine, but after I click it gets "jumpy" and goes too fast. Also, the timer doesn't stop correctly. So that it cycles continuously.
Thanks for the help.
Just adjust the ms to however precise you want
const span = document.getElementById("t");
let d = new Date();
d.setHours(d.getHours()+1); // demo time
let tId = setInterval(() => span.innerText = new Date(d.getTime()-new Date().getTime()).toLocaleTimeString(),100)
<span id="t"></span>
I'd like to be able to have a function attached to an element which will only run once a click has been held on that element for a given amount of time.
There are several (1, 2, 3) questions relating to handling mouse holds in javascript; but these questions either use jQuery or relate to a more particular use case.
I'd like to implement something more general, and I feel like there ought to be a good answer on stack overflow for this problem.
Here is what I get using window.setTimeout :
var mouseTimer;
var myVar;
function mouseDown() {
mouseTimer = window.setTimeout(myFunction,500); //set timeout to fire in 2 seconds when the user presses mouse button down
}
function myFunction(){ myVar = true;}
var div = document.getElementById("testBtn");
testBtn.addEventListener("mousedown", mouseDown);
document.body.addEventListener("mouseup", removeTimer);
function removeTimer(){
if(myVar) console.log("a");
if (mouseTimer) window.clearTimeout(mouseTimer);
myVar = false;
}
<button id="testBtn">Test</button>
function handleClickHold(el, timeout, callback) {
let startTime;
const mouseDown = () => (startTime = Date.now());
const mouseUp = (e) => Date.now() - startTime > timeout && callback(Date.now() - startTime);
el.addEventListener("mousedown", mouseDown);
el.addEventListener("mouseup", mouseUp);
}
const timeout = 500 // time in ms
const btn = document.querySelector('button');
const callback = (ms) => console.log('Held Button for ' + ms + ' ms')
handleClickHold(btn, timeout, callback);
<button>Click</button>
If you want something more general use, you could use a function like this. Takes an element, a timeout, and a callback, so you can apply it in different situations. You can pass the event to the callback as well.
Here's one way of doing it, though it feels rather verbose:
const addHoldListener = (element, f, timeout) => {
let down = 0;
let up = 0;
const upListener = () => {
up = Date.now();
const diff = up - down;
if (diff >= timeout) {
f();
}
element.removeEventListener('mouseup', upListener);
};
const downListener = () => {
down = Date.now();
element.addEventListener('mouseup', upListener);
};
element.addEventListener('mousedown', downListener);
};
addHoldListener(
document.querySelector('button'),
() => console.log('a'),
500
)
<button>Test</button>
I'm working on a little "web app" for a quiz.
Each slide has got a certain amount of time to be answered (or 0 to infinite time).
I find JS here to do the countdown:
function Countdown(options) {
var timer,
instance = this,
seconds = options.seconds || 10,
updateStatus = options.onUpdateStatus || function () {},
counterEnd = options.onCounterEnd || function () {};
function decrementCounter() {
updateStatus(seconds);
if (seconds === 0) {
counterEnd();
instance.stop();
}
seconds--;
}
this.start = function () {
clearInterval(timer);
timer = 0;
seconds = options.seconds;
timer = setInterval(decrementCounter, 1000);
};
this.stop = function () {
clearInterval(timer);
};
}
var myCounter = new Countdown({
seconds: timetogo, // number of seconds to count down
onUpdateStatus: function (sec) {
elapsed = timetogo - sec;
$('.progress-bar').width(((elapsed / timetogo) * 100) + "%");
}, // callback for each second
onCounterEnd: function () {
//alert('counter ended!');
} // final action
});
myCounter.start();
I made a jsfiddle here :
https://jsfiddle.net/mitchum/kz2400cc/2/
But i am having trouble when you go to the next slide, the progress bar "bump".
after looking into "live source panel from chrome" I saw it's like the first "counter" is not stopped and still runs.
Do you have any tips or hint to help me to solve my bug ?
Thanks
You must pay attention to the scope of the variables. I change the "var myCounter" under document ready in "var myCounterFirst". Check the updated JSFiddle.
var timetogoFirst = $('.current').attr("data-time");
var myCounterFirst = new Countdown({
seconds: timetogoFirst, // number of seconds to count down
onUpdateStatus: function (sec) {
elapsed = timetogoFirst - sec;
$('.progress-bar').width(((elapsed / timetogoFirst) * 100) + "%");
}, // callback for each second
onCounterEnd: function () {
alert('counter ended!');
} // final action
});
myCounterFirst.start();
How do I pause and resume the setInterval() function using Javascript?
For example, maybe I have a stopwatch to tell you the number of seconds that you have been looking at the webpage. There is a 'Pause' and 'Resume' button. The reason why clearInterval() would not work here is because if the user clicks on the 'Pause' button at the 40th second and 800th millisecond, when he clicks on the 'Resume' button, the number of seconds elapsed must increase by 1 after 200 milliseconds. If I use the clearInterval() function on the timer variable (when the pause button is clicked) and then using the setInterval() function on the timer variable again (when the resume button is clicked), the number of seconds elapsed will increase by 1 only after 1000 milliseconds, which destroys the accuracy of the stopwatch.
So how do I do that?
You could use a flag to keep track of the status:
var output = $('h1');
var isPaused = false;
var time = 0;
var t = window.setInterval(function() {
if(!isPaused) {
time++;
output.text("Seconds: " + time);
}
}, 1000);
//with jquery
$('.pause').on('click', function(e) {
e.preventDefault();
isPaused = true;
});
$('.play').on('click', function(e) {
e.preventDefault();
isPaused = false;
});
h1 {
font-family: Helvetica, Verdana, sans-serif;
font-size: 12px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h1>Seconds: 0</h1>
<button class="play">Play</button>
<button class="pause">Pause</button>
This is just what I would do, I'm not sure if you can actually pause the setInterval.
Note: This system is easy and works pretty well for applications that don't require a high level of precision, but it won't consider the time elapsed in between ticks: if you click pause after half a second and later click play your time will be off by half a second.
You shouldn't measure time in interval function. Instead just save time when timer was started and measure difference when timer was stopped/paused. Use setInterval only to update displayed value. So there is no need to pause timer and you will get best possible accuracy in this way.
While #Jonas Giuro is right when saying that:
You cannot PAUSE the setInterval function, you can either STOP it (clearInterval), or let it run
On the other hand this behavior can be simulated with approach #VitaliyG suggested:
You shouldn't measure time in interval function. Instead just save time when timer was started and measure difference when timer was stopped/paused. Use setInterval only to update displayed value.
var output = $('h1');
var isPaused = false;
var time = new Date();
var offset = 0;
var t = window.setInterval(function() {
if(!isPaused) {
var milisec = offset + (new Date()).getTime() - time.getTime();
output.text(parseInt(milisec / 1000) + "s " + (milisec % 1000));
}
}, 10);
//with jquery
$('.toggle').on('click', function(e) {
e.preventDefault();
isPaused = !isPaused;
if (isPaused) {
offset += (new Date()).getTime() - time.getTime();
} else {
time = new Date();
}
});
h1 {
font-family: Helvetica, Verdana, sans-serif;
font-size: 12px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h1>Seconds: 0</h1>
<button class="toggle">Toggle</button>
Why not use a simpler approach? Add a class!
Simply add a class that tells the interval not to do anything. For example: on hover.
var i = 0;
this.setInterval(function() {
if(!$('#counter').hasClass('pauseInterval')) { //only run if it hasn't got this class 'pauseInterval'
console.log('Counting...');
$('#counter').html(i++); //just for explaining and showing
} else {
console.log('Stopped counting');
}
}, 500);
/* In this example, I'm adding a class on mouseover and remove it again on mouseleave. You can of course do pretty much whatever you like */
$('#counter').hover(function() { //mouse enter
$(this).addClass('pauseInterval');
},function() { //mouse leave
$(this).removeClass('pauseInterval');
}
);
/* Other example */
$('#pauseInterval').click(function() {
$('#counter').toggleClass('pauseInterval');
});
body {
background-color: #eee;
font-family: Calibri, Arial, sans-serif;
}
#counter {
width: 50%;
background: #ddd;
border: 2px solid #009afd;
border-radius: 5px;
padding: 5px;
text-align: center;
transition: .3s;
margin: 0 auto;
}
#counter.pauseInterval {
border-color: red;
}
<!-- you'll need jQuery for this. If you really want a vanilla version, ask -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p id="counter"> </p>
<button id="pauseInterval">Pause</button></p>
I've been looking for this fast and easy approach for ages, so I'm posting several versions to introduce as many people to it as possible.
i wrote a simple ES6 class that may come handy.
inspired by https://stackoverflow.com/a/58580918/4907364 answer
export class IntervalTimer {
callbackStartTime;
remaining = 0;
paused = false;
timerId = null;
_callback;
_delay;
constructor(callback, delay) {
this._callback = callback;
this._delay = delay;
}
pause() {
if (!this.paused) {
this.clear();
this.remaining = new Date().getTime() - this.callbackStartTime;
this.paused = true;
}
}
resume() {
if (this.paused) {
if (this.remaining) {
setTimeout(() => {
this.run();
this.paused = false;
this.start();
}, this.remaining);
} else {
this.paused = false;
this.start();
}
}
}
clear() {
clearInterval(this.timerId);
}
start() {
this.clear();
this.timerId = setInterval(() => {
this.run();
}, this._delay);
}
run() {
this.callbackStartTime = new Date().getTime();
this._callback();
}
}
usage is pretty straightforward,
const interval = new IntervalTimer(console.log('aaa'), 3000);
interval.start();
interval.pause();
interval.resume();
interval.clear();
My simple way:
function Timer (callback, delay) {
let callbackStartTime
let remaining = 0
this.timerId = null
this.paused = false
this.pause = () => {
this.clear()
remaining -= Date.now() - callbackStartTime
this.paused = true
}
this.resume = () => {
window.setTimeout(this.setTimeout.bind(this), remaining)
this.paused = false
}
this.setTimeout = () => {
this.clear()
this.timerId = window.setInterval(() => {
callbackStartTime = Date.now()
callback()
}, delay)
}
this.clear = () => {
window.clearInterval(this.timerId)
}
this.setTimeout()
}
How to use:
let seconds = 0
const timer = new Timer(() => {
seconds++
console.log('seconds', seconds)
if (seconds === 8) {
timer.clear()
alert('Game over!')
}
}, 1000)
timer.pause()
console.log('isPaused: ', timer.paused)
setTimeout(() => {
timer.resume()
console.log('isPaused: ', timer.paused)
}, 2500)
function Timer (callback, delay) {
let callbackStartTime
let remaining = 0
this.timerId = null
this.paused = false
this.pause = () => {
this.clear()
remaining -= Date.now() - callbackStartTime
this.paused = true
}
this.resume = () => {
window.setTimeout(this.setTimeout.bind(this), remaining)
this.paused = false
}
this.setTimeout = () => {
this.clear()
this.timerId = window.setInterval(() => {
callbackStartTime = Date.now()
callback()
}, delay)
}
this.clear = () => {
window.clearInterval(this.timerId)
}
this.setTimeout()
}
The code is written quickly and did not refactored, raise the rating of my answer if you want me to improve the code and give ES2015 version (classes).
I know this thread is old, but this could be another solution:
var do_this = null;
function y(){
// what you wanna do
}
do_this = setInterval(y, 1000);
function y_start(){
do_this = setInterval(y, 1000);
};
function y_stop(){
do_this = clearInterval(do_this);
};
The following code, provides a precision way to pause resume a timer.
How it works:
When the timer is resumed after a pause, it generates a correction cycle using a single timeout, that will consider the pause offset (exact time when the timer was paused between cycles). After the correction cycle finishes, it schedules the following cycles with a regular setInteval, and continues normally the cycle execution.
This allows to pause/resume the timer, without losing the sync.
Code :
function Timer(_fn_callback_ , _timer_freq_){
let RESUME_CORRECTION_RATE = 2;
let _timer_statusCode_;
let _timer_clockRef_;
let _time_ellapsed_; // will store the total time ellapsed
let _time_pause_; // stores the time when timer is paused
let _time_lastCycle_; // stores the time of the last cycle
let _isCorrectionCycle_;
/**
* execute in each clock cycle
*/
const nextCycle = function(){
// calculate deltaTime
let _time_delta_ = new Date() - _time_lastCycle_;
_time_lastCycle_ = new Date();
_time_ellapsed_ += _time_delta_;
// if its a correction cicle (caused by a pause,
// destroy the temporary timeout and generate a definitive interval
if( _isCorrectionCycle_ ){
clearTimeout( _timer_clockRef_ );
clearInterval( _timer_clockRef_ );
_timer_clockRef_ = setInterval( nextCycle , _timer_freq_ );
_isCorrectionCycle_ = false;
}
// execute callback
_fn_callback_.apply( timer, [ timer ] );
};
// initialize timer
_time_ellapsed_ = 0;
_time_lastCycle_ = new Date();
_timer_statusCode_ = 1;
_timer_clockRef_ = setInterval( nextCycle , _timer_freq_ );
// timer public API
const timer = {
get statusCode(){ return _timer_statusCode_ },
get timestamp(){
let abstime;
if( _timer_statusCode_=== 1 ) abstime = _time_ellapsed_ + ( new Date() - _time_lastCycle_ );
else if( _timer_statusCode_=== 2 ) abstime = _time_ellapsed_ + ( _time_pause_ - _time_lastCycle_ );
return abstime || 0;
},
pause : function(){
if( _timer_statusCode_ !== 1 ) return this;
// stop timers
clearTimeout( _timer_clockRef_ );
clearInterval( _timer_clockRef_ );
// set new status and store current time, it will be used on
// resume to calculate how much time is left for next cycle
// to be triggered
_timer_statusCode_ = 2;
_time_pause_ = new Date();
return this;
},
resume: function(){
if( _timer_statusCode_ !== 2 ) return this;
_timer_statusCode_ = 1;
_isCorrectionCycle_ = true;
const delayEllapsedTime = _time_pause_ - _time_lastCycle_;
_time_lastCycle_ = new Date( new Date() - (_time_pause_ - _time_lastCycle_) );
_timer_clockRef_ = setTimeout( nextCycle , _timer_freq_ - delayEllapsedTime - RESUME_CORRECTION_RATE);
return this;
}
};
return timer;
};
let myTimer = Timer( x=> console.log(x.timestamp), 1000);
<input type="button" onclick="myTimer.pause()" value="pause">
<input type="button" onclick="myTimer.resume()" value="resume">
Code source :
This Timer is a modified and simplified version of advanced-timer, a js library created by myself, with many more functionalities.
The full library and documentation is available in NPM and GITHUB
let time = document.getElementById("time");
let stopButton = document.getElementById("stop");
let timeCount = 0,
currentTimeout;
function play() {
stopButton.hidden = false;
clearInterval(currentTimeout);
currentTimeout = setInterval(() => {
timeCount++;
const min = String(Math.trunc(timeCount / 60)).padStart(2, 0);
const sec = String(Math.trunc(timeCount % 60)).padStart(2, 0);
time.innerHTML = `${min} : ${sec}`;
}, 1000);
}
function pause() {
clearInterval(currentTimeout);
}
function stop() {
stopButton.hidden = true;
pause();
timeCount = 0;
time.innerHTML = `00 : 00`;
}
<div>
<h1 id="time">00 : 00</h1>
<br />
<div>
<button onclick="play()">play</button>
<button onclick="pause()">pause</button>
<button onclick="stop()" id="stop" hidden>Reset</button>
</div>
</div>