Bài viết hướng dẫn bạn tạo một chiếc đồng hồ trên trang web với HTML canvas. Nội dung của bài được biên soạn từ w3schools.com

Phần I – Tạo Canvas

Đầu tiên chiếc đồng hồ cần có một nơi để “trưng bày” (HTML container). Chúng ta cần tạo một HTML canvas với kích thước là 300 x 300 px:

[sourcecode language=”javascript”]
<!DOCTYPE html>
<html>
<body>
<canvas id="canvas" width="300" height="300" style="backgroundcolor:#333"></canvas>
<script>
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var radius = canvas.height / 2;
ctx.translate(radius, radius);
radius = radius * 0.90
drawClock();
function drawClock() {
ctx.arc(0, 0, radius, 0 , 2*Math.PI);
ctx.fillStyle = "white";
ctx.fill();
}
</script>
</body>
</html>
[/sourcecode]

Code ở trên viết gì?

Thêm phần tử HTML <canvas> vào trang (chỗ để trưng bày đồng hồ):

[sourcecode language=”html”]
<canvas id="canvas" style="background-color: #333;" width="300" height="300"></canvas>
[/sourcecode]

Tạo một đối tượng canvas (var canvas) trong JavaScript từ phần tử HTML canvas vừa
thêm ở trên:

[sourcecode language=”javascript”]
var canvas = document.getElementById("canvas");
[/sourcecode]

Tạo đối tượng để vẽ 2d (var ctx) từ đối tượng canvas ở trên:

[sourcecode language=”javascript”]
var ctx = canvas.getContext("2d");
[/sourcecode]

Tính toán bán kính của chiếc đồng hồ (var radius), sử dụng chiều cao của canvas:

[sourcecode language=”javascript”]
var radius = canvas.height / 2;
[/sourcecode]

Lưu ý: Sử dụng chiều cao của canvas để tính bán kính của đồng hồ sẽ khiến đồng hồ có thể “trưng
bày” vừa vặn với bất kỳ kích cỡ nào của canvas.

Đặt lại vị trí (0,0) (của đối tượng vẽ – ctx) vào trung tâm của canvas:

[sourcecode language=”javascript”]
ctx.translate(radius, radius);
[/sourcecode]

Xây dựng một hàm để trưng bày (vẽ) đồng hồ:

[sourcecode language=”javascript”]
function drawClock() {
ctx.arc(0, 0, radius, 0 , 2*Math.PI);
ctx.fillStyle = "white";
ctx.fill();
}
[/sourcecode]

Phần II – Dựng mặt

Đồng hồ cần phải có mặt. Chúng ta sẽ viết một hàm JavaScript để xây dựng mặt cho nó (bổ sung các hàm này vào trong cặp thẻ <script> ở Phần I nhé, và nhớ bỏ hàm drawClock() đang viết dang dở đi :0):

[sourcecode language=”javascript”]
function drawClock() {
drawFace(ctx, radius);
}

function drawFace(ctx, radius) {
var grad;
ctx.beginPath();
ctx.arc(0, 0, radius, 0, 2*Math.PI);
ctx.fillStyle = ‘white’;
ctx.fill();
grad = ctx.createRadialGradient(0,0,radius*0.95, 0,0,radius*1.05);
grad.addColorStop(0, ‘#0f52a0’);
grad.addColorStop(0.5, ‘white’);
grad.addColorStop(1, ‘#0f52a0’);
ctx.strokeStyle = grad;
ctx.lineWidth = radius*0.1;
ctx.stroke();
ctx.beginPath();
ctx.arc(0, 0, radius*0.1, 0, 2*Math.PI);
ctx.fillStyle = ‘#0f52a0’;
ctx.fill();
}
[/sourcecode]

 

Code ở trên viết gì?

Chúng ta viết hàm drawFace() để vẽ mặt cho chiếc đồng hồ này:

[sourcecode language=”javascript”]
function drawClock() {
        drawFace(ctx, radius);
}

function drawFace(ctx, radius) {

}
[/sourcecode]

Vẽ một hình trong màu trắng:

[sourcecode language=”javascript”]
ctx.beginPath();
ctx.arc(0, 0, radius, 0, 2*Math.PI);
ctx.fillStyle = ‘white’;
ctx.fill();
[/sourcecode]

Tạo một radial gradient (sử dụng 95% và 105% bán kính của đồng hồ):

[sourcecode language=”javascript”]
grad = ctx.createRadialGradient(0,0,radius*0.95, 0,0,radius*1.05);
[/sourcecode]

Tạo ba mức màu (color stop), tương ứng với ba vùng trong, giữa và ngoài cùng của các
vòng tròn tạo ở trên (radial gradient):

[sourcecode language=”javascript”]
grad.addColorStop(0, ‘#0f52a0’);
grad.addColorStop(0.5, ‘white’);
grad.addColorStop(1, ‘#0f52a0’);
[/sourcecode]

Lưu ý: Các Color stop tạo ra một hiệu ứng 3D cho chiếc đồng hồ.

Định nghĩa gradient như một kiểu (style) stroke của đối tượng vẽ ctx:

[sourcecode language=”javascript”]
ctx.strokeStyle = grad;
[/sourcecode]

Định nghĩa độ rộng đường thẳng cho đối tượng vẽ ctx (bằng 10% bán kính):

[sourcecode language=”javascript”]
ctx.lineWidth = radius * 0.1;
[/sourcecode]

Vẽ đường tròn (hàm stroke thực thi việc vẽ và hiển thị các hình tròn được vẽ với kiểu là
grad):

[sourcecode language=”javascript”]
ctx.stroke();
[/sourcecode]

Vẽ tâm của đồng hồ (trục quay của các kim đồng hồ):

[sourcecode language=”javascript”]
ctx.beginPath();
ctx.arc(0, 0, radius*0.1, 0, 2*Math.PI);
ctx.fillStyle = ‘#0f52a0’;
ctx.fill();
[/sourcecode]

 

Phần III – Gắn số

Đồng hồ cần các con số chỉ giờ. Chúng ta sẽ viết một hàm JavaScript để gắn các con số cho nó (bổ sung hàm này vào trong cặp thẻ <script> ở Phần I nhé và thêm lời gọi hàm drawNumbers vào trong hàm drawClock ):

[sourcecode language=”javascript”]
function drawClock() {
    drawFace(ctx, radius);
    drawNumbers(ctx, radius);
}

function drawNumbers(ctx, radius) {
    var ang;
    var num;
    ctx.font = radius*0.15 + "px arial";
    ctx.textBaseline="middle";
    ctx.textAlign="center";

    for(num= 1; num < 13; num++){
        ang = num * Math.PI / 6;
        ctx.rotate(ang);
        ctx.translate(0, -radius*0.85);
        ctx.rotate(-ang);
        ctx.fillText(num.toString(), 0, 0);
        ctx.rotate(ang);
        ctx.translate(0, radius*0.85);
        ctx.rotate(-ang);
    }
}
[/sourcecode]

Code ở trên viết gì?

Thiết lập kích thước phông chữ (cho đối tượng vẽ) bằng 15% bán kính đồng hồ:

[sourcecode language=”javascript”]
ctx.font = radius*0.15 + "px arial";
[/sourcecode]

Thiết lập vị trí gắn số (nằm giữa – middle – theo chiều thẳng đứng và nằm ở trung tâm –
center – theo chiều ngang đối với vị trí gắn số):

[sourcecode language=”javascript”]
ctx.textBaseline="middle";
ctx.textAlign="center";
[/sourcecode]

Tính toán vị trí gắn số (cho 12 số) bằng 85% bán kính, quay một góc PI/6 (radian) với
mỗi số:

[sourcecode language=”javascript”]
for(num= 1; num > 13; num++) {
    ang = num * Math.PI / 6;
    ctx.rotate(ang);
    ctx.translate(0, -radius*0.85);
    ctx.rotate(-ang);
    ctx.fillText(num.toString(), 0, 0);
    ctx.rotate(ang);
    ctx.translate(0, radius*0.85);
    ctx.rotate(-ang);
}
[/sourcecode]

Phần IV – Lắp đặt kim

Đồng hồ còn cần có các kim (giờ, phút, giây). Chúng ta sẽ viết một hàm JavaScript để lắp đặt kim cho nó (bổ sung hàm này vào trong cặp thẻ <script> ở Phần I và thêm lệnh gọi hàm drawTime vào hàm drawClock nhé):

[sourcecode language=”javascript”]
function drawClock() {
    drawFace(ctx, radius);
    drawNumbers(ctx, radius);
    drawTime(ctx, radius);
}
 
function drawTime(ctx, radius){
    var now = new Date();
    var hour = now.getHours();
    var minute = now.getMinutes();
    var second = now.getSeconds();
    // Giờ
    hour=hour%12;
    hour=(hour*Math.PI/6)+(minute*Math.PI/(6*60))+(second*Math.PI/(360*60));
    drawHand(ctx, hour, radius*0.5, radius*0.07);
    // Phút
    minute=(minute*Math.PI/30)+(second*Math.PI/(30*60));
    drawHand(ctx, minute, radius*0.8, radius*0.07);
    // Giây
    second=(second*Math.PI/30);
    ctx.strokeStyle = ‘#f37126’;
    drawHand(ctx, second, radius*0.9, radius*0.02);
}
 
function drawHand(ctx, pos, length, width) {
    ctx.beginPath();
    ctx.lineWidth = width;
    ctx.lineCap = "round";
    ctx.moveTo(0,0);
    ctx.rotate(pos);
    ctx.lineTo(0, -length);
    ctx.stroke();
    ctx.rotate(-pos);
}
[/sourcecode]

Code ở trên viết gì?

Sử dụng Date có sẵn trong JavaScript để đặt giờ, phút và giây cho đồng hồ của chúng ta:

[sourcecode language=”javascript”]

var now = new Date();
var hour = now.getHours();
var minute = now.getMinutes();
var second = now.getSeconds();

[/sourcecode]

Tính toán góc của kim giờ, và vẽ nó với một chiều dài (50% bán kính), và một chiều rộng
(7% của bán kính). Nên nhớ kim quay được 1 vòng tương ứng với 360 o (tức 2xPI), như vậy
mỗi giờ tương ứng với một góc 360 o /12 = 2xPI/12 = PI/6, để tính góc của kim giờ còn
phải căn cứ vào phút và giây tại thời điểm đó:

[sourcecode language=”javascript”]

hour=hour%12;
hour=(hour*Math.PI/6)+(minute*Math.PI/(6*60))+(second*Math.PI/(360*60));
drawHand(ctx, hour, radius*0.5, radius*0.07);

[/sourcecode]

Sử dụng cùng cách thức trên với kim phút và kim giây.

Hàm drawHand() không cần phải giải thích. Nó chỉ vẽ các kim (đường thẳng) với độ dài, độ rộng đã được tính toán (sẽ quay một góc tương ứng vị trí của kim trước khi vẽ kim – ctx.rotate(pos) ) .

Phần V – Lắp pin và chạy

Để chạy chiếc đồng hồ này chúng ta cần gọi hàm drawClock() sau một khoảng thời gian:

[sourcecode language=”javascript”]

var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var radius = canvas.height / 2;
ctx.translate(radius, radius);
radius = radius * 0.90
//drawClock();
setInterval(drawClock, 1000);

[/sourcecode]

Code ở trên viết gì?

Việc cần làm bây giờ chỉ là bạn phải gọi hàm drawClock() sau một khoảng thời gian.
Để làm việc này chúng ta sẽ thay thế dòng code:

[sourcecode language=”javascript”]

drawClock();
Bằng dòng code:
setInterval(drawClock, 1000);

[/sourcecode]

Khoảng thời gian được tính bằng mili giây. Như vậy hàm drawClock() sẽ được gọi 1000 mili giây (tức 1 giây) một lần.

Tích tắc, tích tắc, … đây là chiếc đồng hồ của bạn:

canvas - clock

Phần VI – Thử thách thợ đồng hồ

1, Lắp đặt số La Mã thay cho số Thập phân
canvas - roman clock
2, Tháo bớt số chỉ để lại 4 số: 3, 6, 9, 12
canvas - numberless clock
3, Sơn lại đồng hồ
canvas - corlor clock
4, Sơn thêm vạch chỉ giờ
canvas - meta clock
5, Thay kim giây có phần đuôi dài hơn
canvas - long pointer clock
6, Thợ sáng tạo thêm nhé…

Nguyễn Việt Khoa