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 thetiming
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(); }