{"id":523,"date":"2024-01-23T20:23:11","date_gmt":"2024-01-23T14:53:11","guid":{"rendered":"http:\/\/codetheory.in\/?p=523---2405b1bb-1bf5-4c8e-93ee-49548741cfab"},"modified":"2024-01-23T20:23:11","modified_gmt":"2024-01-23T14:53:11","slug":"html5-canvas-drawing-lines-with-smooth-edges","status":"publish","type":"post","link":"https:\/\/codetheory.in\/html5-canvas-drawing-lines-with-smooth-edges\/","title":{"rendered":"HTML5 Canvas Drawing Lines with Smooth Edges"},"content":{"rendered":"
In the previous post<\/a> we kicked off with our painting application using HTML5 canvas. If you remember well, we ended up with a basic app where one can easily draw lines or something else with a “pencil” tool. But there was a problem. When drawing different shapes like circles, the lines\/curves had jagged edges. They were not smooth or anti-aliased, whatever you want to call them. This article will aim towards solving those issues.<\/p>\n <\/p>\n If we save the This is how you would store the “last” mouse positions:<\/p>\n Quite straightforward.<\/p>\n Take a look at the demo first (drag to sketch):<\/p>\n Although improvements in the edges can be noticed, it is still not entirely smooth. Plus, if you look closely, you’ll find some undesirable dots that are probably the joints. Even worse, I realized that it started to lag chrome when I kept on sketching for sometime.<\/p>\n I am not claiming this to be the best or the most accurate way. On experimenting I just found this to be much more better than any other approach that I tried.<\/p>\n When using Now one solution to solve that is to create a curve till the mid point<\/em> of the next 2 subsequent sample points<\/em>. When these curves are joined (using the new interpolated points as start\/end points) and the sample points as the control points, then the entire transition becomes smooth. No jagged edges anymore! Consider this piece of code that’ll do the trick:<\/p>\n Thanks to this SO answer<\/a> that helped out. It might be a bit hard to grasp but you can try plotting sample points on a graph, based on the formula above. Might make things a bit clearer.<\/p>\n Drag and Draw!<\/p>\n Not only did we replace As soon as one clicks on the canvas (mousedown) and then drags (mousemove) all the mouse co-ordinates are stored inside an array called All this happens on a temporary canvas, which is kinda really interesting. When the mouse click is released (onmouseup), we do 3 things:<\/p>\n You might be wondering why did we use a temporary canvas<\/em> ? On experimenting quite a bit and with the help of that SO answer, I figured out that for a smooth drawing, it is important to store all sample points inside an array and then draw it. This process of storing subsequent points, looping over it and drawing must be repeated as the mouse is clicked and dragged\/moved<\/em>, which means it’ll require clearing up that part of canvas else we’ll end up drawing curves over curves.<\/p>\n Now, we cannot<\/strong> clear our main canvas. Why not ? Because we might have drawn something earlier ? Hence a temporary canvas is essential. Later on you’ll also realize how much useful this temp. canvas will be when implementing other tools like the rectangle, circle and line ones.<\/p>\n Precisely, whenever you are drawing something at one go (mousedown + mousemove, without mouseup), it’s best to do it on a temporary canvas, because we arn’t finalized yet. As soon as the mouse click is released, i.e., we’re finalized, move everything from the temp. canvas to the main canvas and clear up the temp. canvas.<\/p>\n It is important to note that this temporary canvas takes the width and height of the main canvas and is positioned absolutely. Also note, now we use Phew! \ud83d\ude42<\/p>\n It’s been quite a bit of read. Go and experiment a bit if you would like to. Hopefully, I didn’t miss anything. In the next tutorial<\/a> we’ll cover how to implement more tools like rectangle, circle, and lines.<\/p>\n","protected":false},"excerpt":{"rendered":" In the previous post we kicked off with our painting application using HTML5 canvas. If you remember well, we ended up with a basic app where one can easily draw lines or something else with a “pencil” tool. But there was a problem. When drawing different shapes like circles, the lines\/curves had jagged edges. They … Continue reading “HTML5 Canvas Drawing Lines with Smooth Edges”<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[12],"tags":[14,15,8],"_links":{"self":[{"href":"https:\/\/codetheory.in\/wp-json\/wp\/v2\/posts\/523"}],"collection":[{"href":"https:\/\/codetheory.in\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/codetheory.in\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/codetheory.in\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/codetheory.in\/wp-json\/wp\/v2\/comments?post=523"}],"version-history":[{"count":24,"href":"https:\/\/codetheory.in\/wp-json\/wp\/v2\/posts\/523\/revisions"}],"predecessor-version":[{"id":184668,"href":"https:\/\/codetheory.in\/wp-json\/wp\/v2\/posts\/523\/revisions\/184668"}],"wp:attachment":[{"href":"https:\/\/codetheory.in\/wp-json\/wp\/v2\/media?parent=523"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/codetheory.in\/wp-json\/wp\/v2\/categories?post=523"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/codetheory.in\/wp-json\/wp\/v2\/tags?post=523"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}The Simple Way – Using moveTo() more<\/h2>\n
mouse.x<\/code> and
mouse.y<\/code> in a temporary variable and use that to “move to” everytime
onmousemove<\/code> (inside
onPaint<\/code> function) – then that can improve the result substantially.<\/p>\n
\r\nvar mouse = {x: 0, y: 0};\r\nvar last_mouse = {x: 0, y: 0};\r\n\r\n\/* Mouse Capturing Work *\/\r\ncanvas.addEventListener('mousemove', function(e) {\r\n\tlast_mouse.x = mouse.x;\r\n\tlast_mouse.y = mouse.y;\r\n\r\n\tmouse.x = e.pageX - this.offsetLeft;\r\n\tmouse.y = e.pageY - this.offsetTop;\r\n}, false);\r\n<\/pre>\n
onPaint()<\/code> will also need some changes:<\/p>\n
\r\nvar onPaint = function() {\r\n\tctx.beginPath();\r\n\tctx.moveTo(last_mouse.x, last_mouse.y);\r\n\tctx.lineTo(mouse.x, mouse.y);\r\n\tctx.closePath();\r\n\tctx.stroke();\r\n};\r\n<\/pre>\n
Problems<\/h3>\n
<\/pre>\n
The More Accurate Way – Using quadraticCurveTo()<\/h2>\n
lineTo<\/code>, we basically join all the sample points that we have (mouse positions). In this case, the points where the lines meet do not have a smooth curve, which is why our final sketch does not looks smooth enough.<\/p>\n
\r\ntmp_ctx.beginPath();\r\ntmp_ctx.moveTo(ppts[0].x, ppts[0].y);\r\n\r\nfor (var i = 1; i < ppts.length - 2; i++) {\r\n\tvar c = (ppts[i].x + ppts[i + 1].x) \/ 2;\r\n\tvar d = (ppts[i].y + ppts[i + 1].y) \/ 2;\r\n\r\n\ttmp_ctx.quadraticCurveTo(ppts[i].x, ppts[i].y, c, d);\r\n}\r\n\r\n\/\/ For the last 2 points\r\ntmp_ctx.quadraticCurveTo(\r\n\tppts[i].x,\r\n\tppts[i].y,\r\n\tppts[i + 1].x,\r\n\tppts[i + 1].y\r\n);\r\ntmp_ctx.stroke();\r\n<\/pre>\n
tmp_ctx<\/code> is the context of a temporary canvas (we’ll discuss this in more details in a bit).
ppts<\/code> is an array that contains objects with x\/y mouse co-ordinates, i.e., the sample points.<\/p>\n
Demo<\/h2>\n
<\/pre>\n
lineTo<\/code> with
quadraticCurveTo<\/code>, but there has been a complete change in the logic now.<\/p>\n
ppts<\/code> (pencil points). At the same time, the array is looped over, using a for loop and using the formula shown above, we draw curves. Ofcourse, clearing canvas using
clearRect<\/code> is also required before drawing the curves.<\/p>\n
\n
drawImage<\/code>.<\/li>\n
clearRect<\/code>.<\/li>\n
<\/p>\n
e.offsetX<\/code> (chrome, opera) or
e.layerX<\/code> (firefox) to capture the mouse co-ordinates relative to the canvas. I haven’t tested Internet Explorer 9 or above, but it must be working fine with those too.<\/p>\n
Next Up<\/h2>\n