Нарисуйте плавные линии на холсте

Я хочу нарисовать линию на холсте с помощью

context.globalCompositeOperation = 'destination-out';
context.globalAlpha = 0.118;

Результат выглядит следующим образом:

введите здесь описание изображения

Фон, если изображение представляет собой желтый прямоугольник. Я использую круг, чтобы перетащить его на изображение, и он нарисует линию. Как видите, моя нарисованная линия не такая гладкая. Вы можете видеть круги, и у вас нет составной линии.

Я создал JSFiddle здесь:

console.clear();

var history = new Array();

var imageObj = new Image();
var img = null;

stage = new Konva.Stage({
    container: 'container',
    width: 600,
    height: 400
});
layer = new Konva.Layer();
stage.add(layer);

var rect = new Konva.Rect({
      x: 30,
      y: 30,
      width: 438,
      height: 300,
      fill: 'yellow'
    });
layer.add(rect);

var canvas = document.createElement('canvas');
var context = canvas.getContext('2d');



var circle = new Konva.Circle({
    x: 0,
    y: 0,
    radius: 40,
    opacity: 0.7,
    fill: '#ff5e0a',
    stroke: '#d95009',
    strokeWidth: 1,
    draggable: true
});
layer.add(circle);


circle.on('dragmove touchmove', function (e) {
    erase(e.evt.clientX, e.evt.clientY, circle.radius(), img.x(), img.y());
});


imageObj.onload = function () {
    canvas.width = imageObj.width;
    canvas.height = imageObj.height;
    context.drawImage(imageObj, 0, 0, imageObj.width, imageObj.height);

    img = new Konva.Image({
        x: 30,
        y: 30,
        image: canvas
    });

    layer.add(img);

    circle.moveToTop();
    layer.draw();
};

imageObj.crossOrigin = "anonymous";
imageObj.src = "https://dl.dropboxusercontent.com/u/47067729/darth-vader.jpg";


function erase(absX, absY, radius, imgX, imgY) {
    var x = 0;
    var y = 0;

    // set pointer
    circle.x(absX);
    circle.y(absY);

    x= absX - imgX;
    y= absY - imgY;

    context.globalCompositeOperation = 'destination-out';
    context.globalAlpha = 0.118;
    context.beginPath();
    context.arc(x, y, radius, 0, 2 * Math.PI);
    context.fill();

    layer.draw();
}

Как я могу нарисовать более плавные и сложные линии, когда вы не можете видеть каждую маленькую часть кругов?


person confile    schedule 20.02.2015    source источник
comment
Если вы рисуете полупрозрачный диск каждый раз, когда получаете событие касания экрана, результат действительно будет зависеть от скорости, с которой палец перемещается по экрану. Возможно, вы захотите интерполировать положение последовательных событий, чтобы плотность остаются постоянными при любом расстоянии между двумя точками.   -  person kuroi neko    schedule 21.02.2015
comment
@kuroineko Как я могу это сделать?   -  person confile    schedule 21.02.2015
comment
Я не знаю Konva, но, возможно, уже есть способ сделать это автоматически с помощью этой библиотеки. Если нет, вам придется запомнить предыдущее положение экрана события и переместить центр вашего круга из предыдущего в текущее положение с постоянным шагом расстояния, рисуя круг для каждого шага.   -  person kuroi neko    schedule 21.02.2015
comment
Не могли бы вы описать, как это делается в HTML5 Canvas?   -  person confile    schedule 21.02.2015
comment
(1) Сохраните все точки пути на полилинии (2) Перерисуйте изображение Вейдера (3) Перерисуйте полилинию (beginPath, moveTo, множество lineTo и штрих). Убедитесь, что вы не используете lineJoin='round', потому что это приведет к перекрывающимся отрисовкам, которые будут дважды стирать в местах перекрытий. Но вы можете использовать lineCap='round', если вам нужны закругленные конечные точки. Ваше здоровье!   -  person markE    schedule 21.02.2015
comment
@markE Тем не менее, я не смог воспроизвести это из вашего комментария. Не могли бы вы быть немного более конкретным в том, что делать?   -  person confile    schedule 04.03.2015


Ответы (1)


Предполагая, что ваша штуковина Konva не предоставляет готового решения, вам придется интерполировать положения событий сенсорного экрана с постоянным смещением.

Что-то вроде этого:

circle.on('dragmove touchmove', function (e) {

    if (circle.prev_pos) {
        var dx = e.evt.clientX - circle.prev_pos.x;
        var dy = e.evt.clientY - circle.prev_pos.y;
        var dist = Math.max (Math.abs(dx), Math.abs(dy));
        dx = dx / dist;
        dy = dy / dist;
        var x = circle.prev_pos.x;
        var y = circle.prev_pos.y;
        var d;
        for (d = 0 ; d < dist ; d++)
        {
            erase(x, y, circle.radius(), img.x(), img.y());
            x += dx;
            y += dy;
        } 
    }
    circle.prev_pos = { x:e.evt.clientX, y:e.evt.clientY};
});

См. эту скрипту.

person kuroi neko    schedule 20.02.2015
comment
Что вы имеете в виду, говоря, что вам придется интерполировать положения событий сенсорного экрана с постоянным смещением, не могли бы вы быть более конкретными, чтобы я мог принять ваш ответ? Как circle.prev_posпринадлежит prev.x? - person confile; 03.03.2015
comment
вы получите две последовательные позиции из вашего события сенсорного экрана, и вы должны вычислить все промежуточные точки между этими позициями, чтобы между каждой точкой было постоянное расстояние. Затем вы нарисуете круг с центром в каждой из этих точек, чтобы добиться постоянной плотности линий. - person kuroi neko; 03.03.2015
comment
Я реализовал ваш код здесь jsfiddle.net/confile/p9xvt76m, но нет никакой разницы, чем мой. - person confile; 04.03.2015
comment
попробуйте это. Была опечатка, и я понял, что параметр шага не нужен, так как положения касания сообщаются с такой скоростью, что любой шаг, кроме 1 пикселя, слишком медленный. - person kuroi neko; 04.03.2015
comment
Это прекрасно работает. Не могли бы вы обновить свой ответ, и тогда я приму его. - person confile; 04.03.2015
comment
Отличный ответ! Благодарю вас! - person confile; 04.03.2015
comment
Без проблем. Красить лорда Вейдера в желтый цвет было весело :). - person kuroi neko; 04.03.2015