Two weeks One month (I decided to give one month as requested by many people) have passed since I put the first challenge online, so it’s now the time to show you my solution. On one side, for my own pleasure, I wanted to give a more than complete solution with many options, or different ways to do it. On the other side, too often I saw examples about a specific element of a programming language that were too complex. If you were a new developer, you can get lost easily with all the code just to find what you were looking for, so I decided to take the simpler path.
Because I know how the Internet is working, let me also specify that this is not considered as the ultimate, and perfect solution: this is the solution I made with my knowledges, and I will be more than happy to discuss it in the comment section. The last thing to take into consideration before we start: I suck with design, and it’s OK as the goal is not to have something pretty, but something functional. To make things clear, people who just want to play with the code can make it yours by cloning, forking, or downloading a copy of it from the repository on GitHub. For others, I’ll explain each part, and if you have any questions, please let me know in the comment section.
Let’s start with the HTML code:
The first thing is to prepare our application to react to different events. Right before, we’ll check if the browser supports canvas by using a subset of Modernizr, and if the browser support also the File API. If it’s the case, let’s add a simple change event on the file input so we’ll be able to load the file when the user will select one. We’ll add mouseup, and mousedown events to the canvas so we know when the user will draw on it. Last but not least, we’ll add a click event to the generate button to fire up the function that will save the image from the canvas. If the browser use by the user don’t support canvas or File API will disable elements on the page, and let him know about the fact he can’t use our application. The second part of the code is the function that will load the file loaded by the user into the canvas element.
The first thing to do is to make sure that the canvas element is visible, and the img is not. It’s not critical, but I added these lines of code to make sure the users can use the tool more than once without having to refresh the page. The magic happens at line 9, where I started to access the file with the FileReader, and I’ll start to read it with the readAsDataURL. After this, I’m setting an event handler once the operation will be successfully completed. In that case, I want to load the image that I accessed with the input field of type file. Once the image is loaded, I’ll resize the canvas element with the size of the image the user selected, so it will work with any image size as little or as large they will be. I’ll do the same with the hidden img element that I’ll use later to create the modified image. Last, but not least, because the canvas itself have no drawing abilities, we need to get one of his context, in our case, the 2d one, and use some script to do it. By using getContext on the canvas I’m able to draw on it, like drawing the image I built a couple lines before.
There is nothing complicated here: I’m setting a listener on mousemove on the canvas element, so if the user moves his mouse over the canvas when the mouse was down (remember the listener I added at the beginning), the function drawOnCanvas will be fired up. I did the same thing to stop the drawing by removing the same listener when the mouse is up.
Now, let’s check one of the biggest function, or I should say, one of the most important one for this challenge, the drawing function.
Actually, it seems worse than it is. Firstly, let’s get some data to draw on the canvas. We need to access the left, and top from the canvas, since the position of the mouse will be relative to the left top corner of the browser window. Once we have those positions, we can start to draw by, of course, getting the 2d context again. In my code, I used the fillStyle function to set the colour to black, but it’s just for this challenge purpose, as the default colour is already black.
The fun begins with the beginPath function. I would have been able to use something simpler like fillRect, but I figured out that if we are going to have only one tool to draw, a circle would be better. To create a circle, we need to create a path by starting it with the begin function, and closing it with the close one. Everything between those two functions will make our path, in that case, an arc. The function takes 5 parameters: the x coordinate where we will start the drawing, the y coordinate where we will start the drawing, the radius of the circle (5 is totally arbitrary, I thought the size was good enough for drawing), the starting angle (in radians – we’ll start at 0), and the end angle (also in radians – Math. PI *2 will make a full circle). In theory, you can also add a last optional parameter to tell the function to draw counter-clockwise, but it changed nothing in our case. At the end, we are calling the fill function to render the path we just created.
In that case, the only important lines are 6, and 7. We are setting the src of our hidden img by creating an image from the canvas element using the toDataUrl function. After this, the user will be able to right-click where he was drawing, and save the file. Another solution would have been to use a trick with window.location.href (see the code below), but it’s not implemented in all browsers yet.
For this not so beautiful HTML page, I used very little CSS.
Let’s start with the #image one. I’m telling the browser to hide the element when it’s loaded, and set to an absolute position at 0 pixels from the left. It will give me the opportunity to have the image the user will save, and the canvas element one over the other, so when the user will ask to save the image, he won’t have to deal with an element elsewhere in the page. Not critical, but I thought it would give a better experience. As for the rest, nothing complicated here. Oh, maybe you didn’t see a lot of attribute selector, like the [disabled] I used with #imgSave. It just means that if this element have this attribute, this CSS will be rendered.
That’s it, you now have a simple HTML page that lets the users load an image, draw on it, and save the new masterpiece to his computer. So did you do the challenge? If you did, please share your result with us in the comment section. Did you had any problem? Do you find anything that I can improve? Do you have any challenge you would like to see? Did you find this one useful? Share your thoughts, and keep looking at my site for the next one.
P.-S.: It’s a pain in the ass to find a good code plugin for WordPress, so hope you’ll like the one I choose. I replaced the one I had, as this one is better, and it’s the less worse I found!
Creative Commons: https://www.w3.org/html/logo/