首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >使用html画布制作模拟时钟

使用html画布制作模拟时钟
EN

Stack Overflow用户
提问于 2022-08-25 06:45:37
回答 4查看 169关注 0票数 1

下面是我制作模拟时钟的初步Javascript代码。我的主要问题是我不知道如何清除时钟表面上的“先前的第二行”:

代码语言: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 =“目的-over”;“但是没有成功:

代码语言: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.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>

你能告诉我如何使用globalCompositeOperation清除这些“先前的第二行”,如果这个函数在我的情况下真的可以做到的话?谢谢。

我之所以相信可以通过globalCompositeOperation进行测试,是因为我已经尝试了如下一些测试:

代码语言:javascript
运行
复制
<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>

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2022-08-25 12:15:09

globalCompositeOperation属性不能真正用于此目的。

但是,您可以这样做:

  • 创建第二个覆盖第一个画布元素的画布元素(使用position: absolute)。它是透明的,所以另一个画布将通过它看到。
  • 在原始画布上绘制背景后,将上下文(ctx)切换到第二个画布,以便timing函数只处理覆盖的画布。
  • timing函数中,从清除覆盖画布开始

代码语言:javascript
运行
复制
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();
}

票数 1
EN

Stack Overflow用户

发布于 2022-08-25 07:16:22

在这里重绘背景的典型例子,不错的小程序:)

代码语言:javascript
运行
复制
        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似乎只在充满的区域运行(不包括线路)

你必须为每一只手存储你的画坐标,在旧位置重画每一只手--移除它,然后计算出新的绘制坐标,绘制和保存。

但是看看这个

代码语言:javascript
运行
复制
        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);

似乎表明它不适合你的时钟手

票数 1
EN

Stack Overflow用户

发布于 2022-08-25 07:26:37

你可能应该重新绘制每一个新的日期,你的时钟面。我把它分解成不同的部分,并使用承诺,但肯定这些都不是绝对必要的。

代码语言:javascript
运行
复制
(() => {

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

})();

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/73483028

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档