Skip to content
Advertisement

Making a analog clock by using html canvas

Below is my preliminary Javascript code for making a analog clock. My main problem is I don’t know how to clear the “previous second lines” on the clock surface:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
    <script>
        setInterval(timing, 1000);
        
        var canvas1 = document.createElement("canvas");
        canvas1.id = "canvas-1";
        document.body.appendChild(canvas1);
        canvas1.width = 500;
        canvas1.height = 500;
        canvas1.style.backgroundColor = "#3d3d3b";
        var radius = (canvas1.height/2) * 0.9;

        var ctx = canvas1.getContext("2d");  
        
        ctx.beginPath();
        ctx.arc(250,250,radius,0,2*Math.PI);
        ctx.fillStyle = "white";
        ctx.fill();
       
        ctx.beginPath();
        ctx.arc(250, 250, radius * 0.1, 0, 2 * Math.PI);
        ctx.fillStyle = '#333';
        ctx.fill();

        ctx.beginPath();
        ctx.lineWidth = radius * 0.05;
        ctx.stroke();
        ctx.font = "40px Georgia"
        ctx.textBaseline="middle";
        ctx.textAlign="center";
        for (i=1;i<13;i++){
        ctx.fillText(i.toString(), 250+(Math.sin(i*Math.PI/6)*radius*0.8), 250-Math.cos(i*Math.PI/6)*radius*0.8);
        }
        
        function timing(){
        
        const d = new Date();
        
        ctx.beginPath();
        ctx.moveTo(250,250);
        ctx.lineWidth = radius*0.01;
        ctx.lineTo(250+(Math.sin(d.getSeconds()*Math.PI/30)*radius*0.85), 250-Math.cos(d.getSeconds()*Math.PI/30)*radius*0.85);        
        ctx.stroke(); 

        ctx.beginPath();
        ctx.moveTo(250,250);
        ctx.lineWidth = radius*0.03;
        ctx.lineTo(250+(Math.sin(d.getMinutes()*Math.PI/30)*radius*0.78), 250-Math.cos(d.getMinutes()*Math.PI/30)*radius*0.78);
        ctx.stroke();

        ctx.beginPath();
        ctx.moveTo(250,250);
        ctx.lineWidth = radius*0.05;
        ctx.lineTo(250+(Math.sin(d.getHours()*Math.PI/6)*radius*0.7), 250-Math.cos(d.getHours()*Math.PI/6)*radius*0.7);
        ctx.stroke();
        }
    </script>
</body>
</html>

I have tried to use “ctx.globalCompositeOperation = “destination-over”;”, however not successful:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
    <script>
        setInterval(timing, 1000);
        
        var canvas1 = document.createElement("canvas");
        canvas1.id = "canvas-1";
        document.body.appendChild(canvas1);
        canvas1.width = 500;
        canvas1.height = 500;
        canvas1.style.backgroundColor = "#3d3d3b";
        var radius = (canvas1.height/2) * 0.9;

        var ctx = canvas1.getContext("2d"); 
        
        ctx.beginPath();
        ctx.arc(250,250,radius,0,2*Math.PI);
        ctx.fillStyle = "white";
        ctx.fill();
        
        ctx.beginPath();
        ctx.arc(250, 250, radius * 0.1, 0, 2 * Math.PI);
        ctx.fillStyle = '#333';
        ctx.fill();

        ctx.beginPath();
        ctx.lineWidth = radius * 0.05;
        ctx.stroke();
        ctx.font = "40px Georgia"
        ctx.textBaseline="middle";
        ctx.textAlign="center";
        for (i=1;i<13;i++){
        ctx.fillText(i.toString(), 250+(Math.sin(i*Math.PI/6)*radius*0.8), 250-Math.cos(i*Math.PI/6)*radius*0.8);
        }
        
        function timing(){
        const d = new Date();

            ctx.beginPath();
            ctx.arc(250,250,radius,0,2*Math.PI);
            ctx.fillStyle = "white";
            ctx.fill();
            ctx.globalCompositeOperation = "destination-over";
            ctx.beginPath();
            ctx.moveTo(250,250);
            ctx.lineTo(250+(Math.sin((d.getSeconds()-1)*Math.PI/30)*radius*0.85), 250-Math.cos((d.getSeconds()-1)*Math.PI/30)*radius*0.85);
            ctx.stroke();

        ctx.beginPath();
        ctx.moveTo(250,250);
        ctx.lineWidth = radius*0.01;
        ctx.lineTo(250+(Math.sin(d.getSeconds()*Math.PI/30)*radius*0.85), 250-Math.cos(d.getSeconds()*Math.PI/30)*radius*0.85);        
        ctx.stroke(); 

        ctx.beginPath();
        ctx.moveTo(250,250);
        ctx.lineWidth = radius*0.03;
        ctx.lineTo(250+(Math.sin(d.getMinutes()*Math.PI/30)*radius*0.78), 250-Math.cos(d.getMinutes()*Math.PI/30)*radius*0.78);
        ctx.stroke();

        ctx.beginPath();
        ctx.moveTo(250,250);
        ctx.lineWidth = radius*0.05;
        ctx.lineTo(250+(Math.sin(d.getHours()*Math.PI/6)*radius*0.7), 250-Math.cos(d.getHours()*Math.PI/6)*radius*0.7);
        ctx.stroke();
        }
    </script>
</body>
</html>

Could you tell me how to clear these “previous second lines” by using globalCompositeOperation if such function can really do in my case? Thanks.

The reason i believe it is possible to do it through globalCompositeOperation, is because i had tried some test as below:

<html>
<body>

<canvas id="myCanvas" width="300" height="150" style="border:1px solid #d3d3d3;">
</canvas>
<button onclick="myFunction()">Click me</button>

<script>
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
ctx.beginPath();
ctx.arc(50, 50, 50, 0, 2*Math.PI);
ctx.fillStyle = 'red';
ctx.fill();
ctx.beginPath();
ctx.moveTo(50,50);
ctx.lineTo(90,90);
ctx.stroke();

function myFunction() {
ctx.beginPath();
ctx.arc(50, 50, 50, 0, 2*Math.PI);
ctx.fillStyle = 'red';
ctx.fill();
ctx.globalCompositeOperation = "destination-over";
ctx.beginPath();
ctx.moveTo(50,50);
ctx.lineTo(90,90);
ctx.stroke();}

</script>

</body>
</html>

Advertisement

Answer

The globalCompositeOperation property cannot really be used for this purpose.

You can however do this:

  • Create a second canvas element that overlays the first (using position: absolute). It is transparent, so the other canvas will be seen through it.
  • After drawing the background on the original canvas, switch the context (ctx) to the second canvas, so that the timing function will only deal with the overlayed canvas
  • In the timing function, start by clearing that overlay canvas

setInterval(timing, 1000);

// Create second canvas that will overlay the first
var canvas2 = document.createElement("canvas");
canvas2.width = 500;
canvas2.height = 500;
canvas2.style.position = "absolute";
document.body.appendChild(canvas2);

var canvas1 = document.createElement("canvas");
canvas1.id = "canvas-1";
document.body.appendChild(canvas1);
canvas1.width = 500;
canvas1.height = 500;
canvas1.style.backgroundColor = "#3d3d3b";
var radius = (canvas1.height/2) * 0.9;

var ctx = canvas1.getContext("2d");  

ctx.beginPath();
ctx.arc(250,250,radius,0,2*Math.PI);
ctx.fillStyle = "white";
ctx.fill();

ctx.beginPath();
ctx.arc(250, 250, radius * 0.1, 0, 2 * Math.PI);
ctx.fillStyle = '#333';
ctx.fill();

ctx.beginPath();
ctx.lineWidth = radius * 0.05;
ctx.stroke();
ctx.font = "40px Georgia"
ctx.textBaseline="middle";
ctx.textAlign="center";
for (i=1;i<13;i++){
    ctx.fillText(i.toString(), 250+(Math.sin(i*Math.PI/6)*radius*0.8), 250-Math.cos(i*Math.PI/6)*radius*0.8);
}

// Switch the context to the overlayed canvas
ctx = canvas2.getContext("2d");

function timing(){
    // Clear the second canvas (only)
    ctx.clearRect(0, 0, 500, 500);
    const d = new Date();

    ctx.beginPath();
    ctx.moveTo(250,250);
    ctx.lineWidth = radius*0.01;
    ctx.lineTo(250+(Math.sin(d.getSeconds()*Math.PI/30)*radius*0.85), 250-Math.cos(d.getSeconds()*Math.PI/30)*radius*0.85);        
    ctx.stroke(); 

    ctx.beginPath();
    ctx.moveTo(250,250);
    ctx.lineWidth = radius*0.03;
    ctx.lineTo(250+(Math.sin(d.getMinutes()*Math.PI/30)*radius*0.78), 250-Math.cos(d.getMinutes()*Math.PI/30)*radius*0.78);
    ctx.stroke();

    ctx.beginPath();
    ctx.moveTo(250,250);
    ctx.lineWidth = radius*0.05;
    ctx.lineTo(250+(Math.sin(d.getHours()*Math.PI/6)*radius*0.7), 250-Math.cos(d.getHours()*Math.PI/6)*radius*0.7);
    ctx.stroke();
}
User contributions licensed under: CC BY-SA
3 People found this is helpful
Advertisement