Reputation: 46463
Context: For a kiosk application, and for out-of-topic reasons (see here), I don't rely on the OS screensaver, but rather implement a screensaver in JS : a black modal layer (full width, full height) with a logo appears after 5 minutes of user inactivity.
Is it sub-optimal (performance-wise) to clearTimeout
and setTimeout
in such a way, i.e. for each mousemove
(this can happends hundreds of times in a few seconds): is there a better way to handle this screensaver triggering?
Here is what I use, it works (here the delay is 2 seconds):
var screensaver_task = null;
var start_screensaver = () => {
document.querySelector(".screensaver-layout").classList.add("hidden");
if (screensaver_task) {
clearTimeout(screensaver_task);
screensaver_task = null;
}
screensaver_task = setTimeout(() => {
document.querySelector(".screensaver-layout").classList.remove("hidden");
}, 2 * 1000);
}
document.addEventListener("mousemove", start_screensaver);
document.addEventListener("click", start_screensaver);
start_screensaver();
.screensaver-layout { background: black; z-index: 1000; position: fixed; top: 0; left: 0; width: 100%; height: 100%; }
.hidden { display: none; }
Mouse move here and wait 2 seconds...
<div class="screensaver-layout hidden"></div>
Upvotes: 0
Views: 881
Reputation: 6505
Here is a simple solution with a debounce
function that takes two callbacks, one to run at the start and one that runs at the end.
const debounce = (start, end, ms = 500) => {
let timerId;
let started = false;
return (...args) => {
clearTimeout(timerId);
// Execute on startup
if (!started) {
start(...args);
started = true;
}
// Execute when debounced finished
timerId = setTimeout(() => {
end(...args);
started = false;
}, ms);
};
};
const debounceStarted = () => {
document.querySelector(".screensaver-layout").classList.add("hidden");
}
const debounceEnded = () => {
document.querySelector(".screensaver-layout").classList.remove("hidden");
}
document.addEventListener("mousemove", debounce(debounceStarted, debounceEnded, 2000));
.screensaver-layout {
background: black;
z-index: 1000;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.hidden {
display: none;
}
<div class="screensaver-layout hidden"></div>
Upvotes: 1
Reputation: 11347
Just record the last time you saw any interactive event, and have a screensaver "thread" that checks the last event time and acts accordingly.
<style>
#screensaver {
display: none
}
.has-screensaver #screensaver {
display: block
}
</style>
<div id="screensaver">screensaver</div>
<script>
const INTERACTIVE_EVENTS = [
'mousedown',
'mousemove',
'touchstart',
'scroll',
'wheel',
'keydown',
// @TODO anything else?
];
const SCREENSAVER_TIMEOUT = 3000
window.onload = () => {
let lastEventTime = new Date
function screenSaver() {
let elapsed = new Date - lastEventTime
document.body.classList.toggle(
'has-screensaver',
elapsed > SCREENSAVER_TIMEOUT)
setTimeout(screenSaver, 100)
}
INTERACTIVE_EVENTS.forEach(e =>
window.addEventListener(e,
() => lastEventTime = new Date()))
screenSaver()
}
</script>
Upvotes: 0
Reputation: 168967
I'd do as little work as possible in a mousemove
handler.
Since I expect you don't need the screen saver to start exactly 2 seconds (or whatever) after the last move, I'd just have a slow interval
running all the time that checks whether it's been enough time since the last mouse move, like so:
let lastMouseTime = 0;
let screensaverRunning = false;
function updateRunning(flag) {
screensaverRunning = flag;
document
.querySelector(".screensaver-layout")
.classList.toggle("hidden", !screensaverRunning);
}
setInterval(() => {
if (!lastMouseTime) return;
if (!screensaverRunning) {
if (+new Date() - lastMouseTime >= 2000) {
updateRunning(true);
}
}
}, 1000);
document.addEventListener("mousemove", () => {
lastMouseTime = +new Date();
if (screensaverRunning) {
updateRunning(false);
}
});
.screensaver-layout {
background: rgba(0, 255, 0, 0.5);
z-index: 1000;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.hidden {
display: none;
}
Mouse move here and wait approximately 2 seconds...
<div class="screensaver-layout hidden"></div>
Upvotes: 4