This code provides a simple way to draw on a canvas using Vanilla JavaScript. It allows users to draw various shapes such as lines, circles, squares, rectangles, ellipses, and polygons. The code utilizes mouse events to track the user’s interactions with the canvas, including mouse down, move, and up events.
Additionally, it provides functionality for changing line width, fill color, stroke color, and background color. The code is easy to understand and modify, making it helpful for beginners learning about canvas drawing or anyone looking to implement basic drawing features on a web page.
How to Create Simple Canvas Drawing with Vanilla JS
First of all, load the Pure CSS library by adding the following CDN link to the head tag of your webpage.
<link rel='stylesheet' href='https://yui.yahooapis.com/pure/0.6.0/pure-min.css'>
Create the HTML structure for drawing board as follows:
<div class="controls"> <h3>Shape:</h3> <div class='lightBorder' > <p><input type="radio" name="shape" value="line" checked="checked"> Line</p> <p><input type="radio" name="shape" value="rect"> Rectangle</p> <p><input type="radio" name="shape" value="circle"> Circle</p> <p><input type="radio" name="shape" value="square"> Square</p> <p><input type="radio" name="shape" value="ellipse"> Ellipse</p> <p><input type="radio" name="shape" value="polygon"> Polygon</p> </div> <h3>Line Type:</h3> <div class='lightBorder'> <p><label>Line Width: <input id='lineWidth' type='range' step='1' min="1" max="200" value="4"></label></p> Line End: <p><input name="lineCap" value="round" type='radio' checked="checked">round</p> <p><input name="lineCap" value="square" type='radio'>square</p> <p><input name="lineCap" value="butt" type='radio'>butt</p> </div> <h3>Colors:</h3> <div class='lightBorder'> <!-- color --> <p><label>Fill Color: <input id='fillColor' type='color' step='1' value="#24B0D5"></label></p> <p><label>Stroke Color: <input id='strokeColor' type='color' step='1'> </label></p> <p><label>Background Color:<input id='backgroundColor' type='color' step='1' value="#ffffff"></label></p> *changing background will delete image! </div> <div class='lightBorder'> <!-- polygon sides --> <p><label>Polygon Sides: <input id='polygonSides' type='range' step='1' min="3" max="12"></label></p> </div> <form> <h3>Input Text:</h3> <input type="text" id="textInput"/> </form> <!-- fill or no fill, you decide --> <p><label>Fill: <input id='fillBox' type='checkbox' checked='checked'></label></p> <p><label style="font-size:12px;">XOR: <input id='xor' type='checkbox'></label></p> <button type="button" class="button-error pure-button" id="clearCanvas" name="button">Clear Canvas</button> </div> <canvas id="canvas" width="650" height="800"></canvas>
Now, style the drawing board using the following CSS styles:
canvas { float: left; position: relative; border-radius: 10px; margin: 10px; border: 1px solid gray; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } #canvas1 { margin-left: 1150px; margin-top: 30px; } .controls { width: 200px; padding: 12px; border: 1px solid gray; border-radius: 10px; background: #D9D9D9; transition: all .5s ease; float: left; position: relative; margin: 10px; height: 800px; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; font-size: 13px; font-family: Arial; } .controls h3 { color: #3D3D3D; text-transform: uppercase; font-size: 11px; line-height: 10px; } .lightBorder { border: 1px solid #B6B6B6; margin-top: 10px; margin-bottom: 18px; padding: 5px; border-radius: 7px; background: #EEEEEE; box-shadow: inset 1px 1px 10px rgba(0,0,0,0.1); } } h1 { margin-left: 300px; } p { margin: 4px; } /*body { background: #FF4E50; background: linear-gradient(to left, #FF4E50 , #F9D423); transition: all .5s ease; }*/ .button-success, .button-error, .button-warning, .button-secondary { color: white; border-radius: 4px; text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2); } .button-success { background: rgb(28, 184, 65); /* this is a green */ } .button-error { background: rgb(202, 60, 60); /* this is a maroon */ } .button-warning { background: rgb(223, 117, 20); /* this is an orange */ } .button-secondary { background: rgb(66, 184, 221); /* this is a light blue */ }
Finally, add the following JavaScript function to activate the drawing board.
var canvas, context, dragging = false, //this will be dragging if mouse move is followed by mouse down dragStartLocation, snapshot; //getBoundingClientRect //The returned value is a DOMRect object, which contains read-only left, top, right and //bottom properties describing the border-box in pixels. top and left are relative to the top-left of the viewport. // rect is a DOMRect object with six properties: left, top, right, bottom, width, height //var rect = obj.getBoundingClientRect(); //define mouse coordinates function getCanvasCoordinates(event) { var x = event.clientX - canvas.getBoundingClientRect().left; var y = event.clientY - canvas.getBoundingClientRect().top; //return object where x is x and y is y return {x: x, y: y}; } //this avoids dragging the image //The getImageData() method returns an ImageData object that copies the pixel data for the specified rectangle on a canvas. function takeSnapShot() { snapshot = context.getImageData(0, 0, canvas.width, canvas.height); } //These must be added to dragStart() function restoreSnapShot() { context.putImageData(snapshot, 0, 0); } function drawLine(position) { context.beginPath(); context.moveTo(dragStartLocation.x, dragStartLocation.y);//this will be the first point and during mouse up the line will be drawn context.lineTo(position.x, position.y); //current position of x and y during mouseMove event context.stroke(); // The stroke() method actually draws the path you have defined with all those moveTo() and lineTo() methods. So Cool! } //The arc() method creates an arc/curve (used to create circles, or parts of circles). To create a circles set start angle to 0 and end angle to 2*Math.PI function drawCircle(position) { //takes position during mouse up var radius = Math.sqrt(Math.pow((dragStartLocation.x - position.x), 2) + Math.pow((dragStartLocation.y - position.y), 2)); context.beginPath(); context.arc(dragStartLocation.x, dragStartLocation.y, radius, 0, 2 * Math.PI); // context.fill(); } function drawEllipse(position) { var w = position.x - dragStartLocation.x ; var h = position.y - dragStartLocation.y ; var radius = Math.sqrt(Math.pow((dragStartLocation.x - position.x), 2) + Math.pow((dragStartLocation.y - position.y), 2)); context.beginPath(); //Used the .ellipse method instead of arc to give an extra radius, radius x and radius var cw = (dragStartLocation.x > position.x) ? true : false; console.log(cw); context.ellipse(dragStartLocation.x, dragStartLocation.y, Math.abs(w), Math.abs(h), 0, 2 * Math.PI, false); } function drawRect(position) { console.log(position.x, dragStartLocation.x); var w = position.x - dragStartLocation.x ; var h = position.y - dragStartLocation.y ; context.beginPath(); context.rect(dragStartLocation.x, dragStartLocation.y, w, h); } function drawPolygon(position, sides, angle) { var coordinates = [], radius = Math.sqrt(Math.pow((dragStartLocation.x - position.x), 2) + Math.pow((dragStartLocation.y - position.y), 2)), index = 0; for (index; index < sides; index++) { coordinates.push({ x: dragStartLocation.x + radius * Math.cos(angle), y: dragStartLocation.y - radius * Math.sin(angle) }) angle += (2 * Math.PI) / sides; } context.beginPath(); context.moveTo(coordinates[0].x, coordinates[0].y); for (index = 0; index < sides; index++) { context.lineTo(coordinates[index].x, coordinates[index].y); } context.closePath(); // context.fill(); } function draw(position) { var fillBox = document.getElementById("fillBox") , shape = document.querySelector('input[type="radio"][name="shape"]:checked').value , polygonSides = document.getElementById('polygonSides').value // , polygonAngle = document.getElementById('polygonAngle').value , polygonAngle = calculateAngle(dragStartLocation, position) , lineCap = document.querySelector('input[type="radio"][name="lineCap"]:checked').value , writeCanvas = document.getElementById('textInput').value , xor = document.getElementById('xor'); //global context context.lineCap = lineCap; //we don't need even't handlers because before drawing we are jsut taking a default value if (shape === "circle") { drawCircle(position); } if (shape === "square") { drawPolygon(position, 4, Math.PI / 4); } if (shape === "line") { drawLine(position); } if (shape === "ellipse") { drawEllipse(position); } if (shape === "rect") { drawRect(position); } if (shape === "polygon") { drawPolygon(position, polygonSides, polygonAngle * (Math.PI / 180)); } if (xor.checked){ context.globalCompositeOperation = "xor"; } else { context.globalCompositeOperation = "source-over"; } if (fillBox.checked) { context.fill(); } else { context.stroke(); } } //define dragstart, drag and dragStop function dragStart(event) { dragging = true; dragStartLocation = getCanvasCoordinates(event); takeSnapShot(); } function calculateAngle(start, current) { var angle = 360 - Math.atan2(current.y - start.y, current.x - start.x) * 180 / Math.PI; return angle; } function drag(event) { var position; if (dragging === true) { restoreSnapShot(); position = getCanvasCoordinates(event); //generic draw(position); } } //Drag Stop function dragStop(event) { dragging = false; //dragging stops here restoreSnapShot(); var position = getCanvasCoordinates(event); //generic draw(position); } //Line Width Here function changeLineWidth() { context.lineWidth = this.value; //**important** //event.stopPropagation() prevents the vent from bubblim up the DOM tree, preventing any parent handlers from being notified of the event. event.stopPropagation(); } //Fill Color function changeFillStyle() { context.fillStyle = this.value; event.stopPropagation(); } //Stroke Color function changeStrokeStyle() { context.strokeStyle = this.value; event.stopPropagation(); } //backgroundColor function changeBackgroundColor() { context.save(); context.fillStyle = document.getElementById('backgroundColor').value; context.fillRect(0, 0, canvas.width, canvas.height); context.restore(); //more info on save() and resote() www.html5.litten.com/understanding-save-and-restore-for-the-canvas-context/ } function eraseCanvas() { context.clearRect(0, 0, canvas.width, canvas.height); } //write on canvas function writeCanvas() { context.font = '55px Impact'; context.fillText(textInput.value, 25, 175); console.log(textInput.value); } //function invoked when document is fully loaded function init() { canvas = document.getElementById('canvas'); context = canvas.getContext('2d'); var lineWidth = document.getElementById('lineWidth'), fillColor = document.getElementById('fillColor'), strokeColor = document.getElementById('strokeColor'), canvasColor = document.getElementById('backgroundColor'), clearCanvas = document.getElementById('clearCanvas'), textInput = document.getElementById('textInput'); // context.strokeStyle = 'rebeccapurple'; // context.font = '25px Impact'; // context.fillText(writeCanvas, 50, 150); // var imgElement = document.getElementById("logo"); // context.fillStyle = context.createPattern(imgElement, 'repeat'); context.lineWidth = lineWidth.value; // no need for linecap will be defined directly by user // context.lineCap = 'square'; context.fillStyle = fillColor.value; //shapes made transparent by overlapping shapes // context.globalCompositeOperation = 'xor'; //event listeners below canvas.addEventListener('mousedown', dragStart, false); canvas.addEventListener('mousemove', drag, false); canvas.addEventListener('mouseup', dragStop, false); lineWidth.addEventListener('input', changeLineWidth, false); fillColor.addEventListener('input', changeFillStyle, false); strokeColor.addEventListener('input', changeStrokeStyle, false); canvasColor.addEventListener('input', changeBackgroundColor, false); clearCanvas.addEventListener('click', eraseCanvas, false); textInput.addEventListener('input', writeCanvas, false); } window.addEventListener('load', init, false);
That’s all! hopefully, you have successfully created a simple canvas drawing board using Vanilla JS. If you have any questions or suggestions, feel free to comment below.