javascript - 带有时钟图形的 Javascript 计时器
问题描述
我一直在研究一个计时器,它根据用户的选择显示 3 个预设时间。我决定包含一个时钟形状的倒计时图形,它只有一只手并且在分配给它的整个时间长度内旋转 360 度。
我遇到的问题是将原始计时器和新显示器结合起来。我有用画布绘制的“时钟”元素,我有一个工作计时器,但我需要知道如何利用这些时间并利用它来让手四处走动。就目前而言,当我让它运行时,指针会画出来,但主要是因为我没有为时钟设置间隔,但我确实为原始计时器设置了一个间隔。
完整的 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>
解决方案
您遇到的第一个问题是您只调用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);
};
您需要做的最后一件事是在画手时调用完成路径:
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>
推荐阅读
- javascript - 第 1 行的词汇错误:无法识别的文本。错误区域:1:--header-height ^....^ CompileError: Begins at CSS selector undefined
- python - 如何逐块读取大的 Azure blob 存储文件
- woocommerce - Woocommerce 产品折扣简码
- java - CSVPrinter 仅从标题中删除引号
- excel - 日期范围之间的两个独立的营业时间范围(如班次)
- linux - openLDAP 代理因 ldaps 而失败
- maven - Jaxb2 注释:未找到 lombok.builder
- javascript - React-native:渲染从 api 获取的平面列表中每个组件的信息时的问题
- amazon-web-services - 如何从 cloudwatch 日志查询中绘制 sum() 值的条形图结果
- javascript - 类绑定三元和非三元属性