文章目录
前言
Canvas API 提供了一个通过JavaScript 和 HTML的元素来绘制图形的方式。它可以用于动画、游戏画面、数据可视化、图片编辑以及实时视频处理等方面。
Canvas API主要聚焦于2D图形。而同样使用元素的 WebGL API 则用于绘制硬件加速的2D和3D图形。
一、前期准备
- HTML 页面添加一个Canvas容器,并设置好宽高。
<canvas id="clock" width="300" height="300">
你的浏览器不支持Canvas
</canvas>
- 设置canvas的CSS样式
canvas {
border: 1px solid red;
}
- 页面加载完成之后,获取canvas对象
window.addEventListener("load", function () {
canvas = document.getElementById('clock');
if (!canvas.getContext) {
return;
}
//去除透明度
ctx = canvas.getContext('2d', { alpha: false });
//移动中心
ctx.translate(canvas.width / 2, canvas.height / 2);
//设置钟表半径
clockRadius = Math.min(canvas.width, canvas.height) / 2;
//调整路径结束点样式
ctx.lineCap = "round";
//开始绘制
startDraw();
});
注意:
- canvas的属性设置的宽高是canvas的像素的宽高,也就是说设置的是canvas容器的像素,比如这里的宽高个设置的为300,即canvas被设置为一个300*300的容器。
- 通过CSS设置的宽高,为canvas元素放置于页面时所占的宽高,会缩放canvas容器,其对画布内部的像素没有影响。
- 因为我们这个Demo没有透明度的要求,所以这里获取canvas对象时取消了透明度。
- 钟表是以中心为原点,所以我们移动canvas的中心,并设置钟表半径为最小边一半的大小。
二、绘制刻度
1.流程
function drawCalibration() {
ctx.strokeStyle = "black";
for (var i = 0; i < 60; i++) {
ctx.beginPath();
var theta = i * (Math.PI * 2) / 60;
var x = clockRadius * Math.cos(theta);
var y = clockRadius * Math.sin(theta);
ctx.moveTo(x, y);
if (i % 5 == 0) {
ctx.lineWidth = hoursCalibrationWidth;
ctx.lineTo(0.85 * x, 0.85 * y);
} else {
ctx.lineWidth = minutesCalibrationWidth;
ctx.lineTo(0.95 * x, 0.95 * y);
}
ctx.stroke();
}
}
- 首先我们把路径颜色设置为黑色。
- 一般我们钟表会有60个刻度,其中12个时针刻度,48个分针刻度,故我们把钟表圆盘等分为60等份,5的倍数的刻度即是我们的时针刻度,其余为分针刻度。
- 此时,我们求出每个刻度的弧度值,并根据该刻度求出每个刻度在圆边的坐标,移动路径起点到该坐标。
- 当为5的倍数时,我们求得原点到圆边坐标0.85部分的坐标,并连接两点。
- 当不为5的倍数时,我们求得原点到圆边坐标0.95部分的坐标,并连接两点。
- 绘制所有路径。
2.效果图
三、绘制文字
1.流程
function drawHourText() {
ctx.fillStyle = "black";
ctx.font = "20px Arial";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
for (var i = 1; i <= 12; i++) {
var theta = (i - 3) * (Math.PI * 2) / 12;
var x = clockRadius * 0.75 * Math.cos(theta);
var y = clockRadius * 0.75 * Math.sin(theta);
ctx.fillText(i, x, y);
}
}
- 首先我们设置绘制文字的基本设置。
- 一般钟表只会绘制12个小时的时间,故我们只取12个时间点,所以应该表盘分为12份。
- canvas坐标体系中,角度是一般我们现实理解的钟表当中的3点,而此处我们循环以1开头的,画的应该是30度的坐标,也就是钟表当中的4点,而我们把1这个数字画到了钟表当中的4点的位置,所以我们需要把它移动到正常钟表中的1点的位置,故把i减去3即可 ,即逆向旋转90度,然后整个圆是分为12份,所以除以12。
- 现在我们需要的是求我们绘制文字的坐标位置,原理和上面绘制刻度一样,只不过我们绘制的文字的坐标是以原点为圆心,到圆边0.75的位置。
- 最后绘制我们的文字。
2.效果图
四、绘制指针
1.取得当前时间
function drawDateTime() {
ctx.strokeStyle = "black";
var date = new Date();
drawHoursLine(date.getHours());
drawMinutesLine(date.getMinutes());
drawSecondsLine(date.getSeconds());
}
2.绘制秒针
function drawSecondsLine(seconds) {
ctx.beginPath();
ctx.lineWidth = secondsLineWidth;
var theta = (seconds - 15) * (Math.PI * 2) / 60;
var x = clockRadius * Math.cos(theta);
var y = clockRadius * Math.sin(theta);
ctx.moveTo(-0.20 * x, -0.20 * y);
ctx.lineTo(0.65 * x, 0.65 * y);
ctx.stroke();
}
- 我们要绘制秒针,首先需要一个时间的秒数,此处有外部传进来。
- 由绘制文字部分我们应该了解到了canvas的坐标体系相关的问题,但是秒针是60个刻度的,所以我们要逆向旋转90度,需要减去15,然后整个圆是分为60份,所以除以60。
- 求得当前角度的在圆边上的点,然后找到距线圆心到圆边的向量中为-0.20的位置的坐标,以这个点为路径的起始点,然后找到该向量中0.65位置的坐标的点。
- 连接该两点,绘制秒针。
效果图:
3.绘制分针
function drawMinutesLine(minutes) {
ctx.beginPath();
ctx.lineWidth = minutesLineWidth;
var theta = (minutes - 15) * (Math.PI * 2) / 60;
var x = clockRadius * Math.cos(theta);
var y = clockRadius * Math.sin(theta);
ctx.moveTo(-0.15 * x, -0.15 * y);
ctx.lineTo(0.50 * x, 0.50 * y);
ctx.stroke();
}
基本绘制过程和绘制秒针一样,故不多做讲解。
4.绘制时针
function drawHoursLine(hour) {
ctx.beginPath();
ctx.lineWidth = hoursLineWidth;
var theta = ((hour % 12) - 2) * (Math.PI * 2) / 12;
var x = clockRadius * Math.cos(theta);
var y = clockRadius * Math.sin(theta);
ctx.moveTo(-0.10 * x, -0.10 * y);
ctx.lineTo(0.35 * x, 0.35 * y);
ctx.stroke();
}
基本绘制过程和绘制秒针一样,故不多做讲解。
5.效果图
五、绘制圆心
1.流程
function drawCenterCicle() {
ctx.beginPath();
ctx.fillStyle = "#999999";
ctx.arc(0, 0, 6, 0, 2 * Math.PI);
ctx.fill();
ctx.beginPath();
ctx.fillStyle = "#666666";
ctx.arc(0, 0, 3, 0, 2 * Math.PI);
ctx.fill();
}
这里只是个效果,没必要
2.效果图
到此,整个表盘已经绘制完成了,但是这仅仅是个静态的钟表,下面我们需要让他动起来
六、让钟表动起来
1.流程
function startDraw() {
drawClock();
interval = setInterval(function () {
drawClock();
}, 1000);
}
function drawClock() {
ctx.clearRect(-clockRadius, -clockRadius, 2 * clockRadius, 2 * clockRadius);
drawCalibration();
drawHourText();
drawDateTime();
drawCenterCicle();
}
window.addEventListener("visibilitychange", function (event) {
if (document.hidden) {
window.clearInterval(interval);
interval = null;
} else {
startDraw();
}
})
- 首先我们创建个函数drawClock,把前面的代码整合起来,并在绘制钟表之前,清楚一下画布当中的内容。
- 然后创建startDraw函数,内部维护了个计时时间,间隔1秒(应该是至少一秒,JavaScript事件循环的问题)调用我们的绘制函数。
- 添加一个visibilitychange事件监听,当当前页面可见时,开启我们的计时事件,不可见时清楚计时器。
此时你会发现,我们的钟表跟随时间动起来了,证明我们的Demo成功了。
2.效果图
七、项目地址
总结
本文从向量的角度绘制了钟表,并没有使用旋转画布之类的方式,其思路大家可供借鉴。
Canvas功能强大,丰富了我们的视觉体验,其不仅仅可用来绘制2D图像,还可以应用于3D图像的绘制。该文仅仅展示了它的冰山一角,需要我们仔细专研,增强我们的用户体验。