skip to Main Content

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>

3

Answers


  1. 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>
    Login or Signup to reply.
  2. 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>
    Login or Signup to reply.
  3. 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>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search