0

I want to draw a line on a canvas with

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

The result looks like this:

enter image description here

The background if the image is a yellow rectangle. I use a circle to drag and drop it over the image and it will paint the line. As you can see my drawn line is not so smooth. You can see the circles and you do not have a compound line.

I created a JSFiddle here:

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();
}

How can I draw more smooth and compound lines where you cannot see each small chunk of the circles?

Brijesh Bhatt
  • 3,810
  • 3
  • 18
  • 34
Michael
  • 32,527
  • 49
  • 210
  • 370
  • If you're drawing a semi-transparent disc each time you recieve a screen touch event, the result will indeed depend on the speed with which the finger moves on the screen.You might want to interpolate the position of successive events so that the density remain constant whatever the distance between two points. – kuroi neko Feb 20 '15 at 21:46
  • @kuroineko How can I do that? – Michael Feb 20 '15 at 21:46
  • I don't know `Konva`, but maybe there is already a method to do that automatically with this library. If not, you will have to memorize the previous event screen position and move the center of your circle from previous to current position with a constant distance step, drawing the circle for each step. – kuroi neko Feb 20 '15 at 22:03
  • Could you describe how it is done in HTML5 Canvas? – Michael Feb 20 '15 at 22:07
  • (1) Save all the way points on your polyline (2) Redraw the Vader image (3) Redraw a polyline (beginPath, moveTo, lots of lineTo and stroke). Be sure not to use lineJoin='round' because that will cause overlapping draws that will "double erase" at the overlaps. But you could use lineCap='round' if you desire rounded endpoints. Cheers! – markE Feb 21 '15 at 00:14
  • @markE Still I could not reproduce it from your comment. Could you please be a bit more specific on what to do? – Michael Mar 04 '15 at 14:15

1 Answers1

1

Assuming your Konva thingie does not provide a readymade solution, you will have to interpolate the touch screen event positions with a constant displacement.

Something like this:

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

See this fiddle.

kuroi neko
  • 8,479
  • 1
  • 19
  • 43
  • What do you mean by "you will have to interpolate the touch screen event positions with a constant displacement" could you please be more specific so I can accept your answer? How does ``circle.prev_pos``belongs to ``prev.x``? – Michael Mar 03 '15 at 01:28
  • 1
    you will get two consecutive positions from your touch screen event, and you must compute all intermediate points between these positions so that there is a constant distance between each point. You will then draw a circle centered on each of these points, to achieve a constant line density. – kuroi neko Mar 03 '15 at 07:39
  • I implemented your code here http://jsfiddle.net/confile/p9xvt76m/ but there is no difference than my one. – Michael Mar 04 '15 at 13:39
  • try [this](http://jsfiddle.net/r7fLugag/24/). There was a typo and I realized the step parameter was not needed, since the touch positions are reported with such a speed that anything but 1 pixel step is too slow. – kuroi neko Mar 04 '15 at 14:24
  • This works great. Can you please update your answer and then I will accept it. – Michael Mar 04 '15 at 14:39
  • 1
    No problem. Painting Lord Vader yellow was fun :). – kuroi neko Mar 04 '15 at 14:51