簡體   English   中英

使用 html canvas 制作模擬時鍾

[英]Making a analog clock by using html canvas

下面是我用於制作模擬時鍾的初步 Javascript 代碼。 我的主要問題是我不知道如何清除時鍾表面的“前第二行”:

 <,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>

我曾嘗試使用“ctx.globalCompositeOperation = “destination-over”;”,但沒有成功:

 <,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>

如果這樣的 function 在我的情況下真的可以做到,你能告訴我如何使用 globalCompositeOperation 清除這些“前面的第二行”嗎? 謝謝。

我相信可以通過 globalCompositeOperation 做到這一點的原因是因為我嘗試了如下一些測試:

 <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>

globalCompositeOperation屬性不能真正用於此目的。

但是,您可以這樣做:

  • 創建覆蓋第一個元素的第二個 canvas 元素(使用position: absolute )。 它是透明的,因此其他 canvas 將通過它被看到。
  • 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
  • timing function 中,首先清除覆蓋 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(); }

這里重繪背景的經典案例,不過是個不錯的小程序:)

 setInterval(timing, 1000); var canvas1 = document.createElement("canvas"); canvas1.id = "canvas-1"; document.body.appendChild(canvas1); var radius = (canvas1.height/2) * 0.9; var ctx = canvas1.getContext("2d"); function timing(){ canvas1.width = 500; canvas1.height = 500; canvas1.style.backgroundColor = "#3d3d3b"; 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); } 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(); }//timing

我剛剛重讀了你的帖子,你問的是globalCompositeOperation

如果你真的不想重繪背景,使用 globalCompositeOperation,

globalCompositeOperation 似乎只在填充區域上運行,即不是線條

您必須為每只手存儲繪制坐標,在舊的 position 處重新繪制每只手 - 刪除它,然后計算新的繪制坐標,繪制並保存

但檢查一下

 var canvas = document.createElement("canvas"); document.body.appendChild(canvas); canvas.width=500; canvas.height=500; var ctx = canvas.getContext("2d"); ctx.globalCompositeOperation = 'xor'; ctx.beginPath(); ctx.moveTo(50,50); ctx.lineWidth = 1; ctx.lineTo(250,250); ctx.strokeStyle = '#ff0000' ctx.stroke(); ctx.beginPath(); ctx.moveTo(50,250); ctx.lineWidth = 1; ctx.lineTo(250,50); ctx.strokeStyle = '#ff0000' ctx.stroke(); ctx.fillStyle='blue'; ctx.fillRect(20,20,60,60);

似乎表明它不適用於您的時鍾指針

您可能應該為您渲染的每個新日期重新繪制表盤。 我把它分解成單獨的部分並使用了 Promises,但肯定這些並不是絕對必要的。

 (() => { let cnvs; let ctxt; let radius; const buildcanvas = () => new Promise((resolve, reject) => { cnvs = document.createElement("canvas"); cnvs.id = "canvas-1"; cnvs.width = 500; cnvs.height = 500; cnvs.style.backgroundColor = "#3d3d3b"; document.body.appendChild(cnvs); resolve(true) }); const buildclockface = () => new Promise((resolve, reject) => { radius = (cnvs.height / 2) * 0.9; ctxt = cnvs.getContext("2d"); ctxt.beginPath(); ctxt.arc(250, 250, radius, 0, 2 * Math.PI); ctxt.fillStyle = "white"; ctxt.fill(); ctxt.beginPath(); ctxt.arc(250, 250, radius * 0.1, 0, 2 * Math.PI); ctxt.fillStyle = '#333'; ctxt.fill(); ctxt.beginPath(); ctxt.lineWidth = radius * 0.05; ctxt.stroke(); ctxt.font = "40px Georgia" ctxt.textBaseline = "middle"; ctxt.textAlign = "center"; for (i = 1; i < 13; i++) { ctxt.fillText( i.toString(), 250 + (Math.sin(i * Math.PI / 6) * radius * 0.8), 250 - (Math.cos(i * Math.PI / 6) * radius * 0.8) ); } resolve(true) }); const showtime = (d) => new Promise((resolve, reject) => { let d = new Date(); buildclockface(); secondhand(d); minutehand(d); hourhand(d); }); const secondhand = (d) => { ctxt.beginPath(); ctxt.moveTo(250, 250); ctxt.lineWidth = radius * 0.01; ctxt.lineTo(250 + (Math.sin(d.getSeconds() * Math.PI / 30) * radius * 0.85), 250 - Math.cos(d.getSeconds() * Math.PI / 30) * radius * 0.85); ctxt.stroke(); } const minutehand = (d) => { ctxt.beginPath(); ctxt.moveTo(250, 250); ctxt.lineWidth = radius * 0.03; ctxt.lineTo(250 + (Math.sin(d.getMinutes() * Math.PI / 30) * radius * 0.78), 250 - Math.cos(d.getMinutes() * Math.PI / 30) * radius * 0.78); ctxt.stroke(); } const hourhand = (d) => { ctxt.beginPath(); ctxt.moveTo(250, 250); ctxt.lineWidth = radius * 0.05; ctxt.lineTo(250 + (Math.sin(d.getHours() * Math.PI / 6) * radius * 0.7), 250 - Math.cos(d.getHours() * Math.PI / 6) * radius * 0.7); ctxt.stroke(); } buildcanvas().then(bool => setInterval(showtime, 1000)).catch(err => alert(err)) })();

經過對 globalCompositeOperation 的進一步研究,我發現有必要添加另一條指令來告訴程序,一旦清除“前第二行”,globalCompositeOperation 將恢復到之前的 state,因此我進一步修改了我的程序如下,最終它證明使用 globalCompositeOperation 可以解決問題。 但是,我不得不承認,在這種情況下,每次簡單地重繪時鍾背景應該是更好的解決方案。

 <,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"> <title>Analog Clock-4 (using globalCompositeOperation)</title> </head> <body> <script> 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"), setInterval(timing; 1000); function timing(){ const d = new Date(). 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). } ctx;globalCompositeOperation = "destination-over". ctx;beginPath(). ctx,moveTo(250;250). ctx.lineTo(250+(Math.sin((d.getSeconds()-1)*Math.PI/30)*radius*0,5). 250-Math.cos((d.getSeconds()-1)*Math.PI/30)*radius*0;5). ctx;stroke(). ctx;globalCompositeOperation = "source-over". 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>

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM