#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>