Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implementing smooth sketching and drawing on the <canvas> element

I am trying to create a drawing area with canvas. I am having trouble with making the lines look smooth when drawing curves and I also have changing line thickness in my algorithm which looks bad as well because the size jumps to much as well and you can see where the size changed. I did find this link on stackoverflow but this was for a native iPhone app and I can't figure it out.

Here is my current JS code. and Here is it running on jsFiddle

var xStart, xEnd, yStart, yEnd, paint, ctx; $(document).ready(function (){     ctx = $('canvas')[0].getContext("2d");    ctx.strokeStyle = '#000';    ctx.lineJoin="round";    ctx.lineCap="round";    ctx.lineWidth = 1;      $('canvas').bind('mousedown mousemove mouseup mouseleave touchstart touchmove touchend', function(e){         var orig = e.originalEvent;          if(e.type == 'mousedown'){             e.preventDefault(); e.stopPropagation();              xStart = e.clientX - $(this).offset().left;             yStart = e.clientY - $(this).offset().top;             xEnd = xStart;             yEnd = yStart;              paint = true;             draw(e.type);          }else if(e.type == 'mousemove'){             if(paint==true){                 xEnd = e.clientX - $(this).offset().left;                 yEnd = e.clientY - $(this).offset().top;                  lineThickness = 1 + Math.sqrt((xStart - xEnd) *(xStart-xEnd) + (yStart - yEnd) * (yStart-yEnd))/5;                 if(lineThickness > 10){                     lineThickness = 10;                   }                  ctx.lineWidth = lineThickness;                 draw(e.type);             }         }else if(e.type == 'mouseup'){             paint = false;         }else if(e.type == 'mouseleave'){             paint = false;         }else if(e.type == 'touchstart'){             if(orig.touches.length == 1){                 e.preventDefault(); e.stopPropagation();                  xStart = orig.changedTouches[0].pageX - $(this).offset().left;                 yStart = orig.changedTouches[0].pageY - $(this).offset().top;                 xEnd = xStart;                 yEnd = yStart;                   paint = true;                 draw(e.type);             }         }else if(e.type == 'touchmove'){             if(orig.touches.length == 1){                 if(paint==true){                     xEnd = orig.changedTouches[0].pageX - $(this).offset().left;                     yEnd = orig.changedTouches[0].pageY - $(this).offset().top;                               lineThickness = 1 + Math.sqrt((xStart - xEnd) *(xStart-xEnd) + (yStart - yEnd) * (yStart-yEnd))/6;                        if(lineThickness > 10){                           lineThickness = 10;                           }                         ctx.lineWidth = lineThickness;                       draw(e.type);                 }             }         }else if(e.type == 'touchend'){             paint = false;         }        });     });       function draw(event){      if(event == 'mousedown'){         ctx.beginPath();         ctx.moveTo(xStart, yStart);         ctx.lineTo(xEnd, yEnd);         ctx.stroke();     }else if(event == 'mousemove'){         ctx.beginPath();         ctx.moveTo(xStart, yStart);         ctx.lineTo(xEnd, yEnd);         ctx.stroke();     }else if(event == 'touchstart'){         ctx.beginPath();         ctx.moveTo(xStart, yStart);         ctx.lineTo(xEnd, yEnd);         ctx.stroke();     }else if(event == 'touchmove'){         ctx.beginPath();         ctx.moveTo(xStart, yStart);         ctx.lineTo(xEnd, yEnd);         ctx.stroke();     }     xStart = xEnd;     yStart = yEnd;                   } 

Thank you all in advance.

This is what it looks like right now if you draw. current (jagged) implementation

... and this is what I would love to achieve:

smooth brushstrokes

like image 682
ryuutatsuo Avatar asked May 12 '12 20:05

ryuutatsuo


People also ask

What is the canvas element used for?

The HTML <canvas> element is used to draw graphics, on the fly, via scripting (usually JavaScript). The <canvas> element is only a container for graphics. You must use a script to actually draw the graphics. Canvas has several methods for drawing paths, boxes, circles, text, and adding images.

Which two methods are used to draw straight lines on a canvas?

There are the following methods to draw a straight line on the canvas. beginPath(): This method is used to begin the path that we are going to draw. It does not take any arguments. moveTo(): This method takes two arguments which will be the starting point of any path.


2 Answers

I made something like this a while ago and turned it into a jquery plugin. have a look over here, if it's what you're after I'll post a more detailed answer and dig out the simplified jquery version from my archives:

http://jsfiddle.net/95tft/

EDIT

OK, sorry I couldn't do this yesterday:

Originally the code above was forked from Mr Doob's 'harmony' sketcher over here: http://mrdoob.com/projects/harmony/#ribbon

(which I think is the best solution). But I kinda broke it down and remade it for my own purposes on another project. I've hacked my own plugin a bit to make it a bit easier still over here:

http://jsfiddle.net/dh3bj/

The only thing you might want to change is to change it to work on mousedown/mouseup which should be easy also have a look at the settings at the bottom of the plugin, you should be able to get the effect you want by playing with the brush size, colour, alpha (rgba) etc.

Hope that helps

like image 89
Alex Avatar answered Sep 29 '22 09:09

Alex


Have a look at this code:

http://jsfiddle.net/aMmVQ/

What I'm doing is starting a new list of points on mouseDown, then for each mousemove I add a point to the list. Once I get enough points (6 or so) I start drawing quadratic curves, with the control point of the curve being the average of the current point and the next point.

drawPoints is the bit that works this magic:

function drawPoints(ctx, points) {     // draw a basic circle instead     if (points.length < 6) {         var b = points[0];         ctx.beginPath(), ctx.arc(b.x, b.y, ctx.lineWidth / 2, 0, Math.PI * 2, !0), ctx.closePath(), ctx.fill();         return     }     ctx.beginPath(), ctx.moveTo(points[0].x, points[0].y);     // draw a bunch of quadratics, using the average of two points as the control point     for (i = 1; i < points.length - 2; i++) {         var c = (points[i].x + points[i + 1].x) / 2,             d = (points[i].y + points[i + 1].y) / 2;         ctx.quadraticCurveTo(points[i].x, points[i].y, c, d)     }     ctx.quadraticCurveTo(points[i].x, points[i].y, points[i + 1].x, points[i + 1].y), ctx.stroke() } 
like image 45
Simon Sarris Avatar answered Sep 29 '22 09:09

Simon Sarris