【问题标题】:using globalCompositeOperation with lines?使用带线条的 globalCompositeOperation?
【发布时间】:2013-02-10 04:33:15
【问题描述】:

我正在尝试使用 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/ 有人知道答案吗?它适用于形状和文本..

【问题讨论】:

  • 愿意帮助...任何代码?

标签: html html5-canvas globalcompositeoperation


【解决方案1】:

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 是不完整的。

这是一个 Fiddle,显示旋转时“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>

【讨论】:

  • 感谢您的回答。但我的问题是globalCompositeOperation 似乎无法“异或”两行。如果你不想看的话,我已经添加了我的简化代码。
  • 哇,非常感谢!它真的很有帮助。谢谢!我以前从没想过要画一个全新的画布:)
  • 更多好东西!咳咳,但你不能用 XOR 重新绘制原始时钟指针然后增加位置吗?也就是说,总是画同一条线两次:一次显示,一次删除。
猜你喜欢
  • 2013-09-06
  • 2018-03-15
  • 2016-03-05
  • 2014-12-19
  • 2013-01-14
  • 1970-01-01
  • 2022-01-03
  • 2017-03-18
  • 1970-01-01
相关资源
最近更新 更多