0

我正在尝试使用 HTML 5 的 canvas 元素制作时钟。

我想要做的是每秒划一条线,然后擦除前一条线。

我想用绘制另一条线来擦除前一条线,globalCompositeOperation='xor';但它不起作用!

这是代码:

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>Clock</title>
    </head>

    <body onload="spin()">
    <canvas id="myCanvas" width="578" height="400"></canvas>
    <script>
    var firstTime = 0;
    var prevX=null;
    var prevY=null;

    function spin() {
        //get the canvas element
        var canvas = document.getElementById('myCanvas');
        var context = canvas.getContext('2d');

        //get the right angle for the clock hand
        var date = new Date;
        var seconds = date.getSeconds();
        var a = seconds*6;
        var angleRadian = a*Math.PI/180;
        var angle = 1/2*Math.PI - angleRadian;

        if(a > 360)
            a = 0; 
        //Erase the previous line, if it has been drawn.
        if(prevX!=null)
            erasePrevLine(angle, canvas, context);
        //draw line
        drawLine(angle, 100, canvas, context);
        //repeat for the next second
        setTimeout(spin, 500);
    }
    function drawLine(angle, radius, canvas, context)   {
        var centerX = canvas.width/2;
        var centerY = canvas.height/2;

        var xTarget = centerX + Math.cos(angle)*radius;
        var yTarget = centerY - Math.sin(angle)*radius;

        //save this state to be erased
        prevX = xTarget;
        prevY = yTarget;

        //draw
        context.beginPath();
        context.moveTo(centerX,centerY);
        context.lineTo(xTarget, yTarget);
        context.stroke();
    }
    function erasePrevLine(angle, canvas, context)  {
        context.globalCompositeOperation = 'xor';
        var centerX = canvas.width/2;
        var centerY = canvas.height/2;
        prevAngle = angle + (Math.PI/180*6);
        var xTarget = prevX;
        var yTarget = prevY;

        //draw on the previous line
        context.beginPath();
        context.moveTo(centerX,centerY);
        context.lineTo(xTarget, yTarget );
        context.stroke();
    }
    </script>
    </body>
    </html>

这是现场示例: http: //jsfiddle.net/pyerT/1/ 有人知道答案吗?它适用于形状和文本..

4

1 回答 1

2

globalCompositeOperation (xor) 不能像“时钟指针”一样在旋转线上工作……原因如下:

假设你画一条垂直线。然后在第一条的右侧绘制第二条垂直线。假设第二条垂直线与第一条线重叠一半。

Canvas.globalCompositeOperation=”xor” 导致重叠区域被移除,因此第二行移除了第一行的一半以及自身的一半。

这是代码和小提琴:http: //jsfiddle.net/m1erickson/e24KU/

    function drawLine(){
        ctx.globalCompositeOperation="xor";
        ctx.strokeStyle="red";
        ctx.lineWidth=10;
        ctx.beginPath();
        ctx.moveTo(posX,10);
        ctx.lineTo(posX,100);
        ctx.stroke();
        posX+=5;
    }

这是围绕中心点扫过的“时钟指针”的小提琴:http: //jsfiddle.net/m1erickson/hW2EY/

但是,如果我们尝试在旋转线上使用“xor”,则这些线会以一定角度重叠,因此 xor 是不完整的。

这是一个小提琴,显示旋转时“xor”行无效:http: //jsfiddle.net/m1erickson/f7hHx/

[已编辑:OP 提供了新代码,允许扩展答案]

我查看了您的新代码,并建议进行一些更改和优化。

正如我在原始答案中所说,您无法有效地擦除以一定角度绘制的线。这是由于浏览器自动执行的抗锯齿功能 - 无法关闭画布的抗锯齿功能。

这是更改后结果的小提琴:http: //jsfiddle.net/m1erickson/9QD65/

变化:

信不信由你:通常在每个动画循环期间完全擦除和完全重绘画布!Canvas 确实足够快来处理这些重绘——尤其是现在 Canvas 是 GPU 加速的。如果您绝对需要优化您的性能,您可以定义画布上必须擦除/重绘的“脏”区域,并将其他区域保留为先前绘制的。在实践中,一旦您需要这种类型的性能,您的画布就会非常复杂,以至于完全清除/重绘比尝试定义脏区更有效。

优化:

将 canvas,context,centerX,centerY 移出动画循环,因为这些值可以计算一次并重复使用。

这是我建议的代码供您查看:

 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>Clock</title>
    </head>

    <body onload="spin()">
    <canvas id="myCanvas" width="578" height="400"></canvas>
    <script>
    var firstTime = 0;
    var canvas = document.getElementById('myCanvas');
    var context = canvas.getContext('2d');
    var centerX=canvas.width/2;
    var centerY=canvas.height/2;
    var canvasWidth=canvas.width;
    var canvasHeight=canvas.height;

    function spin() {
        //get the right angle for the clock hand
        var date = new Date;
        var seconds = date.getSeconds();
        var a = seconds*6;
        var angleRadian = a*Math.PI/180;
        var angle = 1/2*Math.PI - angleRadian;

        if(a > 360)
            a = 0; 
        //draw line
        drawLine(angle, 100, canvas, context, "black",1);
        //repeat for the next second
        setTimeout(spin, 500);
    }

    function drawLine(angle, radius, canvas, context)   {
        var xTarget = centerX + Math.cos(angle)*radius;
        var yTarget = centerY - Math.sin(angle)*radius;

        //clear the canvas
        context.clearRect(0,0,canvasWidth,canvasHeight);

        //draw
        context.save();
        context.beginPath();
        context.moveTo(centerX,centerY);
        context.lineTo(xTarget, yTarget);
        context.stroke();
        context.restore()
    }

    </script>
    </body>
    </html>
于 2013-02-25T06:56:08.993 回答