используя globalCompositeOperation со строками?

Я пытаюсь сделать часы с элементом холста HTML 5.

То, что я пытаюсь сделать, это сделать строку для каждой секунды, а затем стереть предыдущую строку.

Я хочу стереть предыдущую строку, нарисовав другую с помощью 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/ Кто-нибудь знает отвечать? он отлично работает с фигурами и текстами.


person aIKid    schedule 25.02.2013    source источник
comment
Готовы помочь... есть код?   -  person markE    schedule 25.02.2013


Ответы (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;
    }

Это Fiddle «стрелка часов», вращающаяся вокруг центральной точки: http://jsfiddle.net/m1erickson/hW2EY/

Однако, если мы попытаемся использовать «исключающее ИЛИ» на вращающейся линии, линии накладываются под углом, и поэтому операция «исключающее ИЛИ» будет неполной.

Вот скрипт, показывающий, что строка «xor» неэффективна при повороте: http://jsfiddle.net/m1erickson/f7hHx/

[Отредактировано: OP предоставила новый код, позволяющий дать расширенный ответ]

Я посмотрел на ваш новый код и предлагаю некоторые изменения и оптимизации.

Как я сказал в своем первоначальном ответе, вы не можете эффективно стереть линию, нарисованную под углом. Это происходит из-за сглаживания, которое браузер выполняет автоматически — сглаживание для холста нельзя отключить.

Вот скрипт результатов после изменений: http://jsfiddle.net/m1erickson/9QD65/

Изменения:

Хотите верьте, хотите нет: обычно полностью стирают и полностью перерисовывают холст во время каждого цикла анимации! Canvas действительно достаточно быстр, чтобы обрабатывать эти перерисовки, особенно теперь, когда Canvas ускоряется с помощью графического процессора. Если вам абсолютно нужно оптимизировать производительность, вы можете определить «грязные» области холста, которые необходимо стереть/перерисовать, а другие области оставить такими, как они были нарисованы ранее. На практике, если вам нужна такая производительность, ваш холст настолько сложен, что эффективнее полностью очистить/перерисовать, чем пытаться определить грязные области.

Оптимизации:

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>
person markE    schedule 25.02.2013
comment
Спасибо за ваш ответ. Но моя проблема в том, что globalCompositeOperation, кажется, не может "исключить" две строки. Я добавил свой упрощенный код, если вы не хотите взглянуть. - person aIKid; 26.02.2013
comment
Вау, большое спасибо! это действительно помогает. Благодарю вас! Раньше я никогда не думал о том, чтобы нарисовать полностью новый холст :) - person aIKid; 26.02.2013
comment
Больше отличных вещей! Кхм, а нельзя ли с помощью XOR перерисовать исходную стрелку часов, а затем увеличить позицию? То есть всегда рисуйте одну и ту же линию дважды: один раз, чтобы она появилась, и один раз, чтобы удалить ее. - person S Meaden; 14.09.2016