Таймер Javascript с тактовой графикой

#javascript

#javascript

Вопрос:

Я работал над таймером, который отображает 3 предустановленных времени на основе выбора пользователя. Я решил включить графику обратного отсчета в форме часов, которые имеют только одну стрелку и вращаются на 360 градусов в течение всего отведенного им времени.

Проблема, с которой я сталкиваюсь, заключается в объединении исходного таймера и нового дисплея. У меня есть элемент «clock», нарисованный с помощью canvas, и у меня есть рабочий таймер, но мне нужно знать, как использовать это время и использовать его, чтобы заставить руку двигаться. В текущем виде стрелки будут отображаться, когда я включу его, но не отображаются в основном потому, что я не установил интервал для часов, но у меня есть один для исходного таймера.

Полный JS

 ((d) => {
  let btn = d.getElementById("btn");
  let reset = d.getElementById("reset");
  let countdown = d.getElementById("countdown");
  let btnLength = d.getElementById("btnLength");

  // 25 Minutes
  let short = 1500;
  // 45 Minutes
  let med = 2700;
  // 90 Minutes
  let long = 5400;

  let counter;
  let startTime = short;

  let timerFormat = (s) => {
    return (s - (s %= 60)) / 60   (9 < s ? ":" : ":0")   s;
  };

  countdown.textContent = timerFormat(startTime);

  let timer = () => {
    startTime--;
    countdown.textContent = timerFormat(startTime);
    if (startTime === 0) clearInterval(counter);
  };

  let start = () => {
    counter = counter || setInterval(timer, 950);
  };

  let stop = () => {
    clearInterval(counter);
    counter = undefined;
  };

  // Changes between Start and Stop button labelling.

  btn.addEventListener("click", () => {
    if (counter) {
      stop();
      btn.textContent = "Start";
    } else {
      start();
      btn.textContent = "Stop";
    }
  });

  // Stops counter, resets to original start time and
  // resets button label to Start.

  reset.onclick = () => {
    stop();
    startTime = short;
    if (btn.textContent === "Stop") btn.textContent = "Start";
    btnLength.textContent = "Short";
    countdown.textContent = timerFormat(startTime);
  };

  // Changes timer legnth and button labelling

  btnLength.addEventListener("click", () => {
    if (startTime === short) {
      startTime = med;
      btnLength.textContent = "Medium";
    } else if (startTime === med) {
      startTime = long;
      btnLength.textContent = "Long";
    } else if (startTime === long) {
      startTime = short;
      btnLength.textContent = "Short";
    }
    countdown.textContent = timerFormat(startTime);
  });

  //Clock

  let c = d.getElementById("canvas");
  let ctx = c.getContext("2d");
  let radius = c.height / 2;
  ctx.translate(radius, radius);
  radius = radius * 0.9;

  showTime = (ctx, radius) => {
    let startTime =
      (startTime * Math.PI) / 30   (startTime * Math.PI) / (30 * 60);
    hand(ctx, startTime, radius * 0.8, radius * 0.07);
  };

  hand = (ctx, position, length, width) => {
    ctx.beginPath();
    ctx.lineWidth = width;
    ctx.lineCap = "round";
    ctx.moveTo(0, 0);
    ctx.rotate(position);
    ctx.lineTo(0, -length);
    ctx.stroke();
    ctx.rotate(-position);
  };

  clock = () => {
    ctx.arc(0, 0, radius, 0, 2 * Math.PI, true);
    ctx.fillStyle = "#f5afaf";
    ctx.fill();
    showTime();
  };

  clock();
})(document);

  

HTML

 <!DOCTYPE html>
<html lang="en">
  <head>
    <title>Document</title>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link rel="stylesheet" href="../styles/styles.css" />
    <script defer src="../JS/timer.js"></script>
  </head>
  <body>
    <div class="btn__container">
      <div id="countdown"></div>
      <button class="btn" id="btn">Start</button>
      <button class="btn" id="btnLength">Short</button>
      <button class="btn" id="reset">Reset</button>
<div class="clock">
<canvas  id="canvas" width="300" height="300" style="background-color:none"></canvas>
</div>
  </body>
</html>


  

Ответ №1:

Первая проблема, с которой вы сталкиваетесь, заключается в том, что вы вызываете только clock() один раз — в конце вашего скрипта для его инициализации. Добавьте его в timer() функцию:

 let timer = () => {
  startTime--;
  countdown.textContent = timerFormat(startTime);
  clock(); // <-- here
  if (startTime === 0) clearInterval(counter);
};
  

Вторая проблема заключается в том, что вы не передаете ctx и radius при вызове showTime() :

 clock = () => {
  ctx.arc(0, 0, radius, 0, 2 * Math.PI, true);
  ctx.fillStyle = "#f5afaf";
  ctx.fill();
  showTime(ctx, radius); // <-- here
};
  

Третья проблема, с которой вы столкнетесь сейчас, заключается Cannot access 'startTime' before initialization в следующем. Это потому, что вы повторно объявляете startTime в showTime() функции. Обновите эту функцию до:

 showTime = (ctx, radius) => {
  let time = // <-- here
    (startTime * Math.PI) / 30   (startTime * Math.PI) / (30 * 60);
  hand(ctx, time, radius * 0.8, radius * 0.07);
};
  

Последнее, что вам нужно сделать, это вызвать finish the path при рисовании руки:

 hand = (ctx, position, length, width) => {
  ctx.beginPath();
  ctx.lineWidth = width;
  ctx.lineCap = "round";
  ctx.moveTo(0, 0);
  ctx.rotate(position);
  ctx.lineTo(0, -length);
  ctx.stroke();
  ctx.rotate(-position);
  ctx.closePath(); // <-- here
};
  

Теперь стрелка движется вместе с таймером. Однако, если позволите, я хотел бы предложить несколько улучшений. В настоящее время вы перемещаете холст, чтобы получить центр ваших часов в координатах (0,0) , а затем перемещаете холст еще два раза каждый раз, когда рисуете руку. Я предлагаю вам оставить холст в покое и вместо этого указать offset , который сообщает вам, где находится центр часов.

Теперь вам нужно будет указать точные координаты кончика стрелки, что можно сделать с помощью быстрой тригонометрии (см. Полный пример ниже).

И последнее, начальная позиция вашей руки находится где-то в правом нижнем углу. Возможно, хорошая позиция для времени 0 была бы на вершине. Если вы хотите изменить его, вы можете добавить некоторое отношение PI к angle вычисленному showTime() (например, добавить Math.PI , чтобы иметь время 0 внизу.

Смотрите полный пример ниже:

 ((d) => {
    let btn = d.getElementById("btn");
    let reset = d.getElementById("reset");
    let countdown = d.getElementById("countdown");
    let btnLength = d.getElementById("btnLength");
  
    // 25 Minutes
    let short = 1500;
    // 45 Minutes
    let med = 2700;
    // 90 Minutes
    let long = 5400;
  
    let counter;
    let startTime = short;
  
    let timerFormat = (s) => {
      return (s - (s %= 60)) / 60   (9 < s ? ":" : ":0")   s;
    };
  
    countdown.textContent = timerFormat(startTime);
  
    let timer = () => {
      startTime--;
      countdown.textContent = timerFormat(startTime);
      clock(); // <-- here
      if (startTime === 0) clearInterval(counter);
    };
  
    let start = () => {
      counter = counter || setInterval(timer, 950);
    };
  
    let stop = () => {
      clearInterval(counter);
      counter = undefined;
    };
  
    // Changes between Start and Stop button labelling.
  
    btn.addEventListener("click", () => {
      if (counter) {
        stop();
        btn.textContent = "Start";
      } else {
        start();
        btn.textContent = "Stop";
      }
    });
  
    // Stops counter, resets to original start time and
    // resets button label to Start.
  
    reset.onclick = () => {
      stop();
      startTime = short;
      if (btn.textContent === "Stop") btn.textContent = "Start";
      btnLength.textContent = "Short";
      countdown.textContent = timerFormat(startTime);
    };
  
    // Changes timer length and button labelling
  
    btnLength.addEventListener("click", () => {
      if (startTime === short) {
        startTime = med;
        btnLength.textContent = "Medium";
      } else if (startTime === med) {
        startTime = long;
        btnLength.textContent = "Long";
      } else if (startTime === long) {
        startTime = short;
        btnLength.textContent = "Short";
      }
      countdown.textContent = timerFormat(startTime);
    });
  
    //Clock
  
    let c = d.getElementById("canvas");
    let ctx = c.getContext("2d");
    let offset = c.height / 2;
    let radius = offset * 0.9;
  
    showTime = (ctx, radius) => {
      let second = startTime % 60;
      let angle = (2*Math.PI) - (2 * Math.PI * second) / 60; // Add   Math.PI; to put time 0 in the bottom

      hand(ctx, angle, radius * 0.8, radius * 0.07); 
    };
  
    hand = (ctx, angle, length, width) => {
      // Calculate coordinates of the tip of the hand.
      x = offset   Math.round(- length * Math.sin(angle));
      y = offset   Math.round(- length * Math.cos(angle));

      ctx.beginPath();
      ctx.lineWidth = width;
      ctx.lineCap = "round";
      ctx.moveTo(offset, offset);
      ctx.lineTo(x, y);
      ctx.stroke();
      ctx.closePath();
    };
  
    clock = () => {
      ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
      ctx.arc(offset, offset, radius, 0, 2 * Math.PI, true);
      ctx.fillStyle = "#f5afaf";
      ctx.fill();
      showTime(ctx, radius); 
    };
  
    clock();
  })(document);  
 <!DOCTYPE html>
<html lang="en">
  <head>
    <title>Document</title>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    
    <script defer src="../Desktop/timer.js"></script>
  </head>
  <body>
    <div class="btn__container">
      <div id="countdown"></div>
      <button class="btn" id="btn">Start</button>
      <button class="btn" id="btnLength">Short</button>
      <button class="btn" id="reset">Reset</button>
      </div>
<div class="clock">
<canvas  id="canvas" width="300" height="300" style="background-color:none"></canvas>
</div>
  </body>
</html>