Timer + JavaScript Semaphore

1

I'm making a timer that is a traffic light. At the moment of entering the values, for example 20 minutes are entered, and the green background should be. When 5 minutes remain, it should change to yellow, and when it is left in 0 min, it should be red.

<!DOCTYPE html>
<html>
 <head lang="es">
    <meta charset="UTF-8">
    <title>Temporizador</title>
 </head>
 <body>
  <h1 id="demo"></h1>
  <form onkeypress="return validar(event)">
     <input type="text" maxlength="2" name="h" id="h" onkeyup="contar(this,s)"/>
     <input type="text" maxlength="2" name="m" id="m" onkeyup="contar(this,h)">
     <input type="text" maxlength="2" name="s" id="s" onkeyup="contar(this,m)">
    <input type="button" onclick="contador_regresivo()" value="ingresar" />
    <script type="text/javascript">
       var h = document.getElementById("h");
       var m = document.getElementById("m");
       var s = document.getElementById("s");

       var interval;
       //contador_regresivo() // 0 Dias 0 Horas 1 Minuto 20 Segundos

       function contador_regresivo() {
           h = parseInt(h.value);
           m = parseInt(m.value);
           s = parseInt(s.value);

           interval = setInterval(function() {
               setInter();
               innerHTML();
               // document.body.innerHTML = h+" h "+m+" m "+s+" s";
           }, 1000);
       }

       function setInter() {
           if (s > 0 && s <= 60) {
               s--;
           } else {
               if (m > 0 && m <= 60) {
                   m--;
                   s = 59;
               } else {
                   if (h > 0) {
                       h--;
                       m = 59;
                       s = 59;
                   } else {
                       setInterval("cambiar()", 1000);
                       clearInterval(interval);
                       //alert("Tiempo Finalizado");
                   }
               }
           }
       }
       
       function innerHTML() {
           document.getElementById("demo").innerHTML = h + ":" + m + ":" +            s;
       }

       function cambiar() {
           if (m == 9) {
               document.body.style.backgroundColor = "green";
           } else if (m == 5) {
               document.body.style.backgroundColor = "yellow";
           } else if (h == 00 && m == 00 && s == 00) {
               document.body.style.backgroundColor = "red";
           }
       }
       //---------------VALIDAR---NUMEROS-------------------------------------------

       //Validar solo numeros input
       function validar(e) {
           tecla = (document.all) ? e.keyCode : e.which;
           if (tecla == 8) return true;
           patron = /\d/;
           te = String.fromCharCode(tecla);
           return patron.test(te);
       }
       
       //focus al ingresar un numero
       function contar(obj, destino) {
           if (obj.value.length == obj.maxLength) destino.focus();
       }
    </script>
  </form>
 </body>
</html>
    
asked by Rodrigo Villarroel Soto 06.10.2016 в 17:42
source

2 answers

1
  • The function change () must be executed in all conditions (if, else if) and not with a setInterval as it was. Since it was in your code, what I was doing was starting to run when the timer was finished.

  • It is not necessary to nest so much if, in this way they are more understandable

  • At the final else of the setInter () function, 0 must be assigned to h, m, and s.

  • Inside the function change () you have to make more validations so that the colors change at the exact moment (when the timer starts, at 5:00 and when the timer ends)

  • There are things that you should correct if you want to re-press the enter button to keep the timer running and not be damaged as it currently happens.

  • <!DOCTYPE html>
    <html>
    <head lang="es">
        <meta charset="UTF-8">
        <title>Temporizador</title>
    </head>
    <body>
    <h1 id="demo"></h1>
    <form onkeypress="return validar(event)">
    <input type="text" maxlength="2" name="h" id="h" onkeyup="contar(this,s)"/>
    <input type="text" maxlength="2" name="m" id="m" onkeyup="contar(this,h)">
    <input type="text" maxlength="2" name="s" id="s" onkeyup="contar(this,m)">
    <input type="button" onclick="contador_regresivo()" value="ingresar" />
      <script type="text/javascript">
        var h=document.getElementById("h");
        var m=document.getElementById("m");
        var s= document.getElementById("s");
    
        var interval;
        //contador_regresivo() // 0 Dias 0 Horas 1 Minuto 20 Segundos
    
        function contador_regresivo()
        {
    
            h=parseInt(h.value);
            m=parseInt(m.value);
            s=parseInt(s.value);
    
            interval = setInterval(function(){setInter()
                
                 innerHTML();
              // document.body.innerHTML = h+" h "+m+" m "+s+" s";
            },1000);
        }
    
        function setInter() {           
    
            if(s > 0 && s <= 60) { 
                s--;
                cambiar()
            }
            else if (m > 0 && m <= 60) {
                m--;
                s = 59;
                cambiar()
            }
            else if (h > 0) {
                h--;
                m = 59;
                s = 59;
                cambiar()
            }                            
            else {
                h = 0;
                m = 0;
                s = 0;                           
                clearInterval(interval);
                cambiar()
                //alert("Tiempo Finalizado");
            }                             
        }
    
        function innerHTML(){
            document.getElementById("demo").innerHTML = h+":"+m+":"+s;
        }
    
        function cambiar(){
    
          if(m>5 || (m==5 && s>0)){
             document.body.style.backgroundColor="green";
             
          }else if(m==5 && s==0){
             document.body.style.backgroundColor="yellow";
             
          }else if(h==00 && m ==00 && s==00){
             document.body.style.backgroundColor="red";             
          }
        }
    
    
        //---------------VALIDAR---NUMEROS-------------------------------------------
    
        //Validar solo numeros input
        function validar(e) {
            tecla = (document.all) ? e.keyCode : e.which;
            if (tecla==8) return true;
            patron = /\d/;
            te = String.fromCharCode(tecla);
            return patron.test(te);
        } 
    
        //focus al ingresar un numero
        function contar(obj,destino) {
          if (obj.value.length==obj.maxLength) destino.focus();
        }
    </script>
    </form>
    </body>
    </html>
        
    answered by 06.10.2016 в 18:05
    -1

    With window#setInterval you can do it, but if it requires more synchronization and fluidity, the best will be window#requestAnimationFrame .

    window # setTimeout

    var minutes = window.prompt('Ingresa el tiempo en minutos');
    var time = minutesToMilli(minutes);
    var times = timesByColor(time);
    
    highlight('green');
    window.setTimeout(function() {
      highlight('ambar');
      window.setTimeout(function() {
        highlight('red');
      }, times.ambar);
    }, times.green);
    
    
    function minutesToMilli() {
      return minutes * 60000;  
    }
    
    function timesByColor(t) {
      var time = t;
      var g = time * .66;
      time -= g;
      var a = time / 3;
      time -= a;
      var r = time;
      
      return {
        green: g,
        ambar: a,
        red: r
      }
    }
    
    function highlight(color) {
      var actives = document.querySelectorAll('.spotlight.highlight');
      [].forEach.call(actives, function(active) {
        active.classList.remove('highlight');
      });
      var spotlight = document.querySelector('.spotlight.${color}');
      spotlight.classList.add('highlight');
    }
    *,
    *:before,
    *:after {
      box-sizing: border-box;
    }
    
    .semaphore {
      background-color: #333;
      border: 1px solid transparent;
      height: 300px;
      margin: 20px auto;
      position: relative;
      width: 140px;
    }
    .semaphore .spotlight {
      border: 0;
      border-radius: 100%;
      box-shadow: 0 10px 3px 0 rgba(0, 0, 0, 0.6), inset 0 0 20px 5px rgba(0, 0, 0, 0.5);
      height: 70px;
      margin-left: 35px;
      opacity: .7;
      -webkit-transition: background-color .4s ease, box-shadow .4s ease, opacity .4s ease;
      transition: background-color .4s ease, box-shadow .4s ease, opacity .4s ease;
      width: 70px;
    }
    .semaphore .spotlight.green {
      background-color: #27ae60;
      margin-top: 15px;
    }
    .semaphore .spotlight.green.highlight {
      background-color: #00c231;
    }
    .semaphore .spotlight.ambar {
      background-color: #f39c12;
      margin-top: 30px;
    }
    .semaphore .spotlight.ambar.highlight {
      background-color: #ff9f06;
    }
    .semaphore .spotlight.red {
      background-color: #e74c3c;
      margin-top: 30px;
    }
    .semaphore .spotlight.red.highlight {
      background-color: #ff3824;
    }
    .semaphore .spotlight.highlight {
      box-shadow: 0 10px 3px 0 rgba(0, 0, 0, 0.6), inset 0 0 20px 5px rgba(0, 0, 0, 0.4);
      opacity: 1;
    }
    <div class="semaphore">
      <div class="spotlight green"></div>
      <div class="spotlight ambar"></div>
      <div class="spotlight red"></div>
    </div>

    window # requestAnimationFrame

    A much better and modern option is window#requestAnimationFrame . It is the natural replacement for window#setTimeout and window#setInterval .

    I put the same traffic light example for 30s : 20s for green, 3.33 for amber and the rest for red (simulating the time of a real traffic light).

    var time = Number(.5) * 60000; /* minutos a milisegundos */
    var duration = durations(time);
    var greenTurned = false;
    var ambarTurned = false;
    var redTurned = false;
    var animationId;
    
    (function() {
      var requestAnimationFrame = window.requestAnimationFrame 
                          || window.mozRequestAnimationFrame 
                          || window.webkitRequestAnimationFrame 
                          || window.msRequestAnimationFrame;
      var cancelAnimationFrame = window.cancelAnimationFrame 
                          || window.mozCancelAnimationFrame;
      window.requestAnimationFrame = requestAnimationFrame;
      window.cancelAnimationFrame = cancelAnimationFrame;
    })();
    
    function semaphore(timestamp) {
      if(timestamp <= duration.green && !greenTurned) {
        turnLight('green');
      }
      if(timestamp <= duration.ambar 
         && timestamp > duration.green && !ambarTurned) {
        turnLight('ambar');
      }
      if(timestamp > duration.ambar && !redTurned) {
        turnLight('red');
      }
      if(timestamp >= time) {
        alert('Tiemeout!');
        return cancelAnimationFrame(animationId);
      }
      animationId = requestAnimationFrame(semaphore);
    }
    
    animationId = requestAnimationFrame(semaphore);
    
    /**
     * Obtiene las duraciones de cada color.
     * Verde: 2/3 del tiempo total.
     * Ambar: 1/3 de (tiempo - verde)
     * Rojo: 1/3 de (tiempo - verde)
    */
    function durations(t) {
      var g = t / 1.5;
      var a = g + (t - g) / 3;
      var r = a + 1; // is irrelevante
      
      return { green: g, ambar: a, red: r };
    }
    
    /**
     * Enciende un color y apaga los focos prendidos.
     * @param color El color a encender
     */
    function turnLight(color) {
      // apaga los focos 
      var actives = document.querySelectorAll('.spotlight.turned');
      [].forEach.call(actives, function(active) {
        active.classList.remove('turned');
      });
      var spotlight = document.querySelector('.spotlight.${color}');
      spotlight.classList.add('turned');
      
      // resetea las variables de control
      greenTurned = false;
      ambarTurned = false;
      redTurned = false;
      
      switch(color) {
        case 'green': greenTurned = true; break;
        case 'ambar': ambarTurned = true; break;
        case 'red': redTurned = true; break;
      }
      console.info('${color} encendido');
    }
    @import "https://fonts.googleapis.com/css?family=Play";
    *,
    *:before,
    *:after {
      box-sizing: border-box;
    }
    
    h1 {
      color: #16a085;
      font-family: 'play';
      font-size: 42px;
      font-weight: 400;
      text-align: center;
      text-transform: uppercase;
    }
    
    .semaphore {
      background-color: #333;
      border: 1px solid transparent;
      box-shadow: 0 10px 20px 1px rgba(0, 0, 0, 0.2);
      height: 300px;
      margin: 20px auto;
      position: relative;
      width: 140px;
    }
    .semaphore .spotlight {
      border: 0;
      border-radius: 100%;
      box-shadow: 0 10px 3px 0 rgba(0, 0, 0, 0.6), inset 0 0 20px 5px rgba(0, 0, 0, 0.5);
      height: 70px;
      margin-left: 35px;
      opacity: .7;
      -webkit-transition: background-color .4s ease, box-shadow .4s ease, opacity .4s ease;
      transition: background-color .4s ease, box-shadow .4s ease, opacity .4s ease;
      width: 70px;
    }
    .semaphore .spotlight.green {
      background-color: #27ae60;
      margin-top: 15px;
    }
    .semaphore .spotlight.green.turned {
      background-color: #00c231;
    }
    .semaphore .spotlight.ambar {
      background-color: #f39c12;
      margin-top: 30px;
    }
    .semaphore .spotlight.ambar.turned {
      background-color: #ff9f06;
    }
    .semaphore .spotlight.red {
      background-color: #e74c3c;
      margin-top: 30px;
    }
    .semaphore .spotlight.red.turned {
      background-color: #ff3824;
    }
    .semaphore .spotlight.turned {
      box-shadow: 0 10px 3px 0 rgba(0, 0, 0, 0.6), inset 0 0 20px 5px rgba(0, 0, 0, 0.4);
      opacity: 1;
    }
    <h1>Semaphore</h1>
    <div class="semaphore">
      <div class="spotlight green"></div>
      <div class="spotlight ambar"></div>
      <div class="spotlight red"></div>
    </div>
        
    answered by 06.10.2016 в 23:23