i'm using mousemove event and as it executes each time possible, I want to optimize this and I think about these two possibilities:
ok = true;
function mousemove(e)
{
if(ok == true)
{
ok = false;
window.setTimeout(function(){ ok = true; }, 1000/60);//60 FPS
//Code here
}
}
and
lastTime = +new Date();
function mousemove(e)
{
if(+new Date() - lastTime > 1/60)//60 FPS
{
lastTime = +new Date();
//Code here
}
}
So is it better to use window interval (or timeout) or use a timestamp?
(if you have another idea, I take!)
Thank you!
Within the event I would simply update a variable (e.g. position of the mouse) and within a second event (e.g. requestAnimationFrame) I would read that variable and reset it. So in the next loop I check if it has a valid value and compute it again (cause the user moved the mouse further) or it is still not set (cause the user didn't move the mouse any further).
let currentPosition = null;
function onPaint() {
if(currentPosition !== null) {
let paintPosition = currentPosition;
currentPosition = null;
// ToDo: update visualization by using paintPosition...
}
window.requestAnimationFrame(onPaint);
}
function onMouseMove(e) {
currentPosition = e.position;
}
onPaint();
Using an interval means having a function that is executed 60 times per second, even if the mouse is not being used.
Checking if the right period has passed within the mouse event callback is only executed when needed.
So I'd discard the interval approach (same for timeout):
(function () {
var time=+new Date();
myElement.addEventListener('mousemove', function () {
if (time- (+new Date()) < 60) return;
time=+new Date();
/* Here my logic*/
});
})()
Related
I was wondering if there is a nicer object oriented way of creating this timer? (without global vars!)
let secondsPassed = 0;
let timerId;
function startTimer() {
clearInterval(timerId);
timerId = setInterval(function() {
const seconds = twoDigits((Math.floor(secondsPassed )) % 60);
const minutes = twoDigits(Math.floor(secondsPassed / 60) % 60);
const hours = Math.floor(secondsPassed / 60 / 60);
$('#timer').text(`${hours}:${minutes}:${seconds}`);
secondsPassed++;
}, 1000);
$(window).blur(function() {
clearInterval(timerId) // stop timer when user leaves tab
});
$(window).focus(function() {
startTimer(); // continue timer when user comes back
});
}
Your current implementation is actually wrong. Every time you call startTimer, it installs startTimer as a new window focus event handler, leading to multiple started intervals when you focus the window the second time; growing exponentially. The onfocus handler should only run the timerId = setInterval(…) line - put that in a nested helper function to call only that.
This also makes it unnecessary to declare the variables globally.
function createTimer() {
let secondsPassed = 0;
let timerId;
function resume() {
if (timerId) return; // prevent multiple intervals running at the same time
timerId = setInterval(() => {
const seconds = twoDigits((Math.floor(secondsPassed )) % 60);
const minutes = twoDigits(Math.floor(secondsPassed / 60) % 60);
const hours = Math.floor(secondsPassed / 60 / 60);
$('#timer').text(`${hours}:${minutes}:${seconds}`);
secondsPassed++;
}, 1000);
}
function pause() {
clearInterval(timerId);
timerId = undefined;
}
$(window).blur(pause); // stop timer when user leaves tab
$(window).focus(resume); // continue timer when user comes back
resume(); // now start the timer
}
Now how to make that object-oriented? Just return an object from createTimer. Put resume and pause as methods on that object. Maybe add some more methods for starting, stopping, resetting, whatever you need. Maybe use a property on the object instead of the secondsPassed local variable. Or expose the local variable using a getter.
And to make it reusable, of course you can make createTimer accept arguments, from the selector of the output element, to the output element itself, to a callback function that will be called with the current time on every tick.
Edit: With this answer, you have to implement the Timer class yourself first. The code only shows how you could name the methods of the timer, how you create the instance and call its functions. The timer should (principle "separation of concerns") only handle the counting and provide the functionalities needed, like starting and stopping.
If you want to have an OOP solution for your timer, you shouldn't let the Timer class know the ID of the DOM container (like one of your comments to your question suggested).
You should read into the topic using this:
https://appdividend.com/2019/05/22/javascript-class-example-how-to-use-class-in-javascript-tutorial/
Let us assume, that you already implemented the class. Your code above should look like the following:
// Create own scope for the function, so that variable are not assigned to windows-object.
(function() {
let secondsPassed = 0;
let timer = new Timer();
// events, if necessary
timer.onTick((seconds) => { secondsPassed = seconds });
timer.onStop(() => { secondsPassed = 0; })
// Called by a button
function startTimer() {
timer.start();
}
// Example: Display alert with current timer seconds on click
function displaySecondsOfTimer() {
alert(timer.getSeconds());
}
$(window).blur(function() {
timer.stop(); // stop timer when user leaves tab
});
$(window).focus(function() {
timer.start(); // continue timer when user comes back
});
})();
So I think, you have a good example to code your first Timer class in native JavaScript! :)
I'm a bit of a beginner with Javascript and am struggling to figure out how to use a function of one instance to trigger a function in another instance, and vice versa, both of the same class. Let me explain what I mean.
My project is to build a Pomodoro Clock in Javascript. Pomodoro is a process where you work for a specified time (25 minutes, for example), then take a short break (5 mins), and then repeat. The clocks should run back to back, indefinitely or until the user stops it.
I need the completion of one clock to trigger the beginning of the other one, and vice versa.
I built a working program using completely separate, slightly varying functions for each timer (with much redundancy). I tried to simplify my code by creating a class of Timers and building each one from that. That's where I've got stuck.
I have a function in my Timer class which, when the timer reaches zero, needs to call the other timer's countdown to begin (line 126 on Codepen). How can I do that?
Thank you for any help you can offer.
Here's my project on Codepen: https://codepen.io/lieberscott/pen/baRpgx?editors=1010
And my Javascript code below:
let session; // session Timer object instance
let btimer; // break Timer object instance
let s_off; // boolean for whether session timer is off or on
let s_timer; // reference to session timer HTML element
let s_stop; // reference to session stop HTML button
let s_increase; // reference to session increase HTML button
let s_decrease; // reference to session decrease HTML button
// same variables as above for break timer
let b_off;
let b_timer;
let b_stop;
let b_increase;
let b_decrease;
$(document).ready(function() {
s_off = true;
s_timer = $("#timer");
s_stop = $("#stop");
s_increase = $("#increase");
s_decrease = $("#decrease");
b_off = true;
b_timer = $("#breaktimer");
b_stop = $("#breakstop");
b_increase = $("#breakincrease");
b_decrease = $("#breakdecrease");
session = new Timer(1, 60, s_off, s_timer, s_stop, s_increase, s_decrease);
btimer = new Timer(5, 60, b_off, b_timer, b_stop, b_increase, b_decrease);
// increase session minutes
$(s_increase).on("click", function() {
if (session.off) {
session.min++;
session.sec = 00;
s_timer.html(session.min + ":" + session.sec);
}
});
// decrease session minutes
$(s_decrease).on("click", function() {
if (session.off) {
if (session.min > 1) {
session.min--;
}
session.sec = 00;
s_timer.html(session.min + ":" + session.sec);
}
});
// increase break minutes
$(b_increase).on("click", function() {
if (btimer.off) {
btimer.min++;
btimer.sec = 00;
b_timer.html(btimer.min + ":" + btimer.sec);
}
});
// decrease break minutes
$(b_decrease).on("click", function() {
if (btimer.off) {
if (btimer.min > 1) {
btimer.min--;
}
btimer.sec = 00;
b_timer.html(btimer.min + ":" + btimer.sec);
}
});
// begin session timer by clicking on the timer itself
$(s_timer).on("click", function() {
session.time();
});
// stop session timer
$(s_stop).on("click", function() {
session.off = true;
session.stopClock(session.intervalFunction);
});
// stop break timer
$(b_stop).on("click", function() {
btimer.off = true;
btimer.stopClock(btimer.intervalFunction);
});
});
class Timer {
constructor(min, sec, off, disp, stopButton, increaseButton, decreaseButton) {
this.min = min; // minutes
this.minsSet = min; // minutes again, this will be used to reset the timer
this.sec = sec;
this.off = off; // boolean saying whether timer is off or not
this.disp = disp; // HTML display
this.stopButton = stopButton;
this.increaseButton = increaseButton;
this.decreaseButton = decreaseButton;
this.func;
}
time() { // function fired when the timer is clicked
if (this.off) {
this.off = false;
this.func = this.intervalFunc();
}
}
intervalFunc() { // set the interval of the timer
setInterval(function() {this.countdown();}, 1000); // ERROR HERE
}
countdown() { // interval to complete for duration of timer
// check if clock reaches zero
if (this.sec == 0) {
this.min--;
this.sec = 60;
if (this.min < 0) {
this.min = this.minsSet;
this.sec = 0;
this.off = true;
this.time(); // this needs to trigger, not this.time(), but the OTHER object's time() function
this.stopClock(this.func); // clearInterval() function below
}
}
// if clock is not at 0:00, display new time
this.sec--;
let m = this.min.toString();
let s;
if (this.sec < 10) {
s = "0" + this.sec.toString()
}
else {
s = this.sec.toString();
}
this.disp.html(m + ":" + s);
}
stopClock() {
clearInterval(this.func);
}
}
1) I tried your code and fixed some bugs, your setInterval issue is because "this" points to window object there.
2) for calling the other object time() method, first off you need something to know which object you are working with, so I have added a type variable to the class and then in the countdown function i have added a check.
Changes are in this pen :
https://codepen.io/yaduvanshi/pen/dJRdeR?editors=0010
intervalFunc() { // set the interval of the timer
var that =this;
setInterval(function() {that.countdown();}, 1000); // ERROR HERE
}
I think the solution that you are looking for, is the .bind() or the .call() that Javascript provides you with (Function.prototype.bind()). For example, the .bind() function takes the specific object instance as argument. You can read up on the same here
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_objects/Function/bind
I've made a jQuery player for images Demo Link.
It changes the screens with provided intervals and draws touches on it. Now, I want to implement pouse, play functionality.
When I click on play button to stop screen playing, I call FlowPlaye.stop() method:
FlowPlayer.prototype.stop = function() {
$(".fp-pause").removeClass("fp-pause").addClass("fp-play");
clearInterval(this.screenIntervalId);
clearInterval(this.timeIntervalId);
clearInterval(this.touchIntervalId);
$('.fp-progress').stop();
this.isAnimated = false;
return false;
}
And at the second time FlowPlayer.play():
FlowPlayer.prototype.play = function() {
var fp = this; // Obj refers to the FlowPlayer itself such as "this"
fp.isAnimated = true;
console.log(typeof this.screenIndex)
console.log(this.screenIndex)
fp.screenIndex = typeof this.screenIndex == 'number' ? this.screenIndex : 0;
fp.render(fp.screens[fp.screenIndex]);
fp.initTimeline(fp.duration);
fp.screenIntervalId = setInterval(function() {
if (fp.screenIndex == fp.screens.length - 1) {
console.log("the end of screens");
clearInterval(fp.screenIntervalId)
return;
}
++fp.screenIndex;
fp.render(fp.screens[fp.screenIndex]);
}, fp.screens[fp.screenIndex].delay)
}
The problem is that when I do this, the screen playing intervals are messing (try to stop video at 20th second and restore). I need to save state of player, but I don't know how.
I think using 3 different timers is making this unnecessary difficult. If you refactor it into 1 unified timer, pausing (and other playback controls) would be quite easy.
Separate your keyframe events into separate functions:
function setImage(img) {...}
function showTouch(x, y) {...}
function hideTouch() {...}
On startup, convert your screens array to something like this:
var keyframes = [
{ time:0, func:setImage, args:['http://...']},
{ time:1000, func:showTouch, args:[10, 30]},
{ time:3000, func:hideTouch, args:[]},
...
];
Set up a single timer for playback:
var time = 0,
next = 0,
isPaused = false,
interval;
function timer() {
if (isPaused) {
return;
}
var nextKeyframe = keyframes[next];
time += 100;
if (time >= nextKeyframe.time) {
nextKeyframe.func.apply(this, nextKeyframe.args);
next += 1;
if (next === keyframes.length) {
clearInterval(interval);
}
}
}
Now, you have an easily controllable playback:
// play / replay - reset time and next, then start the timer
time = 0;
next = 0;
interval = setInterval(timer, 100);
// seek - just set a new time, and find the next keyframe
time = 1500;
for (next = 0; keyframes[next].time < time && next < keyframes.length; next++) {}
// pause - the timer stays on, but won't do anything
isPaused = true;
// stop
clearInterval(interval);
Note: The snippets are untested, may have some typos in them. I just wanted to demonstrate the process of making it cleaner / more controllable.
I have this code which works ok but I would like to stop polling and clearInterval if the user is inactive (no mouse move) after say 5 iterations rather than be in a continuous loop.
var i, active = new Date, iter = 1;
$(window).on('mousemove', function(e){
active = new Date;
});
i = setInterval(function(){
if(new Date().getTime() - active.getTime() < 1000){
console.log("sending active event to server.....");
iter = 1;
else{
iter++;
if(iter >= 5){
clearInterval(i);
}
}
}, 2000);
right now it checks every two seconds in this example. I would like to check the active date say 5 times and if its expired 5 iterations in a row, call clearInterval(i)... so something inside the mousemove callback should reinitialize the setInterval only if it's currently not running. How can I accomplish this? Thanks for any tips and samples. I'd like to keep using an anonymous function if possible.
Seperate the Interval function
function intFunc(){
if(new Date().getTime() - active.getTime() < 1000){
console.log("sending active event to server.....");
iter = 1;
else{
iter++;
if(iter >= 5){
clearInterval(i);
}
}
};
Now, call them on the two places you need
var i;
$(window).on('mousemove', function(e){
active = new Date;
i = setInterval(intFunc, 2000);
});
i = setInterval(intFunc, 2000);
One simple way would just be to remove the clearInterval call, and instead only poll the server when iter < 5.
But that's still a little wasteful, as the handler is still being called when it has nothing to do, which is bad when you want your laptop/phone to stay in powersaving mode. So what I'd do is basically what you have, but after calling clearInterval, set up a one-time mousemove handler that restarts polling.
I'm not seeing a way to do that without naming a function (I'm presuming you don't want to get into Y-combinators and the like), but you can hide its name – and the others – from the outside world by using an anonymous function around the whole thing:
(function () {
var i, active = new Date, iter = 1;
$(window).on('mousemove', function(e) {
active = new Date;
});
function startPolling() {
i = setInterval(function() {
if (new Date().getTime() - active.getTime() < 1000) {
console.log("sending active event to server.....");
iter = 1;
}
else{
iter++;
if (iter >= 5) {
clearInterval(i);
$(window).one('mousemove', function () {
startPolling();
});
}
}
}, 2000);
}
startPolling();
})();
Using Javascript/jQuery, how can I pause (or resume) the following loop when the "P" key is pressed?
(function() {
var arr = [...],
len = arr.length;
(function doProcess(i) {
if (i) {
console.log(len - i);
/* do something with arr[len - i] */
setTimeout(function() { doProcess(--i); }, 20000);
}
})(len);
})();
Pausing and resuming is fairly complex. What you really have to do is this:
Start a process and store its timeout ID.
Store the time when you ran that process.
On keypress, clear the timeout using its timeout ID.
Store the unfinished time in another variable.
On next keypress, set the timeout using that unfinished time.
Set the following timeouts to the original intended delay.
Here's a more generalized jsFiddle example I whipped up.
var counterOn = true;
var delay = 3000;
var lastRun;
var tempDelay;
var intervalId;
function decrementCounter() {
// do something
lastRun = new Date();
timeoutId = setTimeout(decrementCounter, delay);
}
function toggleCounter() {
var curTime = new Date();
counterOn = !counterOn;
if (counterOn) {
lastRun = curTime.valueOf() + tempDelay - delay;
timeoutId = setTimeout(decrementCounter, tempDelay);
} else {
clearTimeout(timeoutId);
tempDelay = delay - (curTime.valueOf() - lastRun);
}
}
$(document).keydown(function(e) {
if (e.which === 80) {
toggleCounter();
}
});
decrementCounter();
You'll want to keep track of how much time has passed with your timer (see here: javascript: pause setTimeout();) and call clearTimeout on whatever event you want to stop, then call setTimeout again with your remaining time left once whatever event unpauses is fired again.