We continue week canvas on Habré.
Graphic editors on flash / silverlaite on the Internet are not uncommon. There are a lot less of them on canvas, but there are quite interesting ones. Here are a couple of examples:
In this series of articles, I plan to talk about various points that can be encountered when creating a graphic editor on canvas. I will not touch on the trivial parts, I will try to describe only the most interesting. In this article I will describe an approximate algorithm for creating a brush for sketches.
So where do we start? Let's start by creating a simple file with canvas inside. I will try to write as compactly as possible without unnecessary frameworks and other things so that the algorithm itself is as transparent as possible. Here is a clean file in which we will create a brush for sketches:
<! DOCTYPE html >
< html >
< head >
< title ></ title >
< meta http-equiv ="Content-Type" content ="text/html; charset=UTF-8" >
< style type ="text/css" >
body {
margin: 0;
}
#cnvs {
border: #000000 1px solid;
}
</ style >
</ head >
< body >
< canvas id ="cnvs" width ="800" height ="500" ></ canvas >
</ body >
</ html >
* This source code was highlighted with Source Code Highlighter .
The outline is intentionally located in the upper left corner so that the coordinates of the cursor on the screen coincide with the coordinates of pixels on the outline (we neglect the error of 1 pixel of the border for the purity of the code, but we know and remember about it).
To create a drawing mechanism, we need several handlers of standard mouse events (onmousedown, onmouseup, onmousemove). Create a function for each.
function mDown(e){
};
function mUp(e){
};
function mMove(e){
};
* This source code was highlighted with Source Code Highlighter .
We also need several variables: in action we will store the current state of the left mouse button (pressed or not), in ctx - 2d context for drawing on the canvas, in points - an array for storing points, in pointer - a pointer to one of the elements of this array .
So what is the algorithm? Having observed how my buddy draws sketches, I have an idea of the mechanism of the work of the future brush. The main points that need to be taken into account for creating such a brush: sketches are drawn with strokes, the brush should be low-intensity (my friend weakly pressed the pencil, but painted one place several times, so the line turns out shaggy with different tails back and forth), the line becomes wider at the bends .
My version of the algorithm is:
- following the mouse we draw a regular line, as if it were a pencil
- besides we connect the current point with some long drawn (it will give the thickness on the bends)
- all this should be with a small element of chance (furry)
- the brush will be partially transparent (low intensity)
The first and second paragraph for clarity are shown in the figure:

So, in the points array we store the coordinates of the last 10 points (I chose the number 10, you can experiment with this). We reduce the intensity with the help of transparency (set to 0.1 - this means that our brush will be 90% transparent). Here is the code for declaring variables and accessing the canvas:
var action = "up" ;
var ctx,points,pointer;
function initcnvs(){
ctx = document .getElementById( 'cnvs' ).getContext( '2d' );
ctx.globalAlpha = 0.1;
points = new Array(10);
};
* This source code was highlighted with Source Code Highlighter .
The function call initcnvs () is hung on onload from body. The remaining functions on the relevant events:
< body onload ="initcnvs()" onmousedown ="mDown(event)" onmousemove ="mMove(event)" onmouseup ="mUp(event)" >
* This source code was highlighted with Source Code Highlighter .
Finally the algorithm itself. When the button is pressed, we change the action to down, throw the first point into our array and set a pointer to it.
function mDown(e){
action = "down" ;
points[0] = [e.pageX, e.pageY];
pointer = 0;
};
* This source code was highlighted with Source Code Highlighter .
When the button is released, we clear the array and change the action to up.
function mUp(e){
points = new Array(10);
action = "up" ;
};
* This source code was highlighted with Source Code Highlighter .
Finally, when moving (only if the mouse is clamped), we define a new point in the array (if we have reached the end, we start from the beginning). Next, we draw the usual segment connecting the previous point with the new coordinates of the cursor and, if the array is already sufficiently filled, connect the current position of the cursor with the oldest point in the array, while adding a random error of 5 pixels. At the end we save the new coordinates to the array.
function mMove(e){
if (action == "down" ) {
var nextpoint = pointer + 1;
if (nextpoint > 9) nextpoint = 0;
ctx.beginPath();
ctx.moveTo(points[pointer][0],points[pointer][1]);
ctx.lineTo(e.pageX, e.pageY);
if (points[nextpoint]) {
ctx.moveTo(points[nextpoint][0] + Math.round(Math.random()*10-5),points[nextpoint][1] + Math.round(Math.random()*10-5));
ctx.lineTo(e.pageX, e.pageY);
}
ctx.stroke();
pointer = nextpoint;
points[pointer] = [e.pageX, e.pageY];
}
};
* This source code was highlighted with Source Code Highlighter .
Here is what this brush looks like in motion:

You can experiment with parameters, randomness, intensity and see the results. Here is the source code of the whole example, it is compact enough:
<! DOCTYPE html >
< html >
< head >
< title ></ title >
< meta http-equiv ="Content-Type" content ="text/html; charset=UTF-8" >
< style type ="text/css" >
body {
margin: 0;
}
#cnvs {
border: #000000 1px solid;
}
</ style >
< script type ="text/javascript" >
var action = "up" ;
var ctx,points,pointer;
function initcnvs(){
ctx = document .getElementById( 'cnvs' ).getContext( '2d' );
ctx.globalAlpha = 0.1;
points = new Array(10);
};
function mDown(e){
action = "down" ;
points[0] = [e.pageX, e.pageY];
pointer = 0;
};
function mUp(e){
points = new Array(10);
action = "up" ;
};
function mMove(e){
if (action == "down" ) {
var nextpoint = pointer + 1;
if (nextpoint > 9) nextpoint = 0;
ctx.beginPath();
ctx.moveTo(points[pointer][0],points[pointer][1]);
ctx.lineTo(e.pageX, e.pageY);
if (points[nextpoint]) {
ctx.moveTo(points[nextpoint][0] + Math.round(Math.random()*10-5),points[nextpoint][1] + Math.round(Math.random()*10-5));
ctx.lineTo(e.pageX, e.pageY);
}
ctx.stroke();
pointer = nextpoint;
points[pointer] = [e.pageX, e.pageY];
}
};
</ script >
</ head >
< body onload ="initcnvs()" onmousedown ="mDown(event)" onmousemove ="mMove(event)" onmouseup ="mUp(event)" >
< canvas id ="cnvs" width ="800" height ="500" ></ canvas >
</ body >
</ html >
* This source code was highlighted with Source Code Highlighter .
That's all, I hope the article was useful for you. In the future, I plan to talk about smart shading, the “cancel” button for canvas, a trick with a blurred brush and much more. In addition, if you are interested in something on the topic - write in the comments, I will tell about it too. Errors in the text - in private, thanks in advance.
PS My crazy skills with this brush:
