How to draw on canvas using JavaScript?

Question

How to draw for free (using the mouse / fingers) on the canvas element, how can you do this in paint with a pencil?

About this question

There are many questions that want to achieve free drawing on canvas:

  • draw with mouse HTML5 canvas
  • KineticJS - draw for free with the mouse
  • Free canvas art using fabric.js
  • Outline with JS
  • Paint does not work properly
  • Mouse position on canvas
  • Implement smooth sketching and drawing on an element

So, I thought it would be nice to make a reference question, where each answer will be a wiki community and contains an explanation of only one JavaScript / pure JavaScript library, how to draw on canvas.

Response structure

Answers must be a community wiki and use the following template:

## [Name of library](Link to project page) ### Simple example A basic, complete example. That means it has to contain HTML and JavaScript. You can start with this: <!DOCTYPE html> <html> <head> <title>Simple example</title> <script type='text/javascript' src='http://cdnjs.com/[your library]'></script> <style type='text/css'> #sheet { border:1px solid black; } </style> <script type='text/javascript'> window.onload=function(){ // TODO: Adjust } </script> </head> <body> <canvas id="sheet" width="400" height="400"></canvas> </body> </html> If possible, this example should work with both, mouse and touch events. [JSFiddle](Link to code on jsfiddle.net) This solution works with: <!-- Please test it the following way: Write "Hello World" Problems that you test this way are: * Does it work at all? * Are lines separated? * Does it get slow when you write too much? --> * Desktop computers: * [Browser + Version list] * Touch devices: * [Browser + Version list] on [Device name] ### Import / Export Some explanations how to import / export user drawn images. ### Line smoothing Explanations about how to manipulate the line the user draws. This can include: * BΓ©zier curves * Controlling thickness of lines 
+19
javascript canvas
Apr 6
source share
6 answers

Fabric.js

 <!DOCTYPE html> <html> <head> <title>Simple example</title> <script type='text/javascript' src='http://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.4.0/fabric.min.js'></script> <style type='text/css'> #sheet { border:1px solid black; } </style> <script type='text/javascript'> window.onload=function(){ var canvas = new fabric.Canvas('sheet'); canvas.isDrawingMode = true; canvas.freeDrawingBrush.width = 5; canvas.freeDrawingBrush.color = "#ff0000"; } </script> </head> <body> <canvas id="sheet" width="400" height="400"></canvas> </body> </html> 

JSFiddle - Demo

  • The width of the lines can be controlled using canvas.freeDrawingBrush.width .
  • The color of the lines can be controlled using canvas.freeDrawingBrush.color .

This solution works with:

  • Desktop computers:
    • Chrome 33
    • Firefox 28
  • Touch devices:
    • Chrome 34 on Nexus 4
    • Opera 20 on Nexus 4
    • Firefox 28 on Nexus 4

Import Export

Only possible by serializing a full canvas, see the Tutorial

Line smoothing

It runs automatically, and it seems impossible to disable it.

+8
Apr 6
source share

Plain javascript

Simple example

 <!DOCTYPE html> <html> <head> <title>Simple example</title> <style type='text/css'> #sheet { border:1px solid black; } </style> </head> <body> <canvas id="sheet" width="400" height="400"></canvas> <script type='text/javascript'> /*jslint browser:true */ "use strict"; var context = document.getElementById('sheet').getContext("2d"); var canvas = document.getElementById('sheet'); context = canvas.getContext("2d"); context.strokeStyle = "#ff0000"; context.lineJoin = "round"; context.lineWidth = 5; var clickX = []; var clickY = []; var clickDrag = []; var paint; /** * Add information where the user clicked at. * @param {number} x * @param {number} y * @return {boolean} dragging */ function addClick(x, y, dragging) { clickX.push(x); clickY.push(y); clickDrag.push(dragging); } /** * Redraw the complete canvas. */ function redraw() { // Clears the canvas context.clearRect(0, 0, context.canvas.width, context.canvas.height); for (var i = 0; i < clickX.length; i += 1) { if (!clickDrag[i] && i == 0) { context.beginPath(); context.moveTo(clickX[i], clickY[i]); context.stroke(); } else if (!clickDrag[i] && i > 0) { context.closePath(); context.beginPath(); context.moveTo(clickX[i], clickY[i]); context.stroke(); } else { context.lineTo(clickX[i], clickY[i]); context.stroke(); } } } /** * Draw the newly added point. * @return {void} */ function drawNew() { var i = clickX.length - 1 if (!clickDrag[i]) { if (clickX.length == 0) { context.beginPath(); context.moveTo(clickX[i], clickY[i]); context.stroke(); } else { context.closePath(); context.beginPath(); context.moveTo(clickX[i], clickY[i]); context.stroke(); } } else { context.lineTo(clickX[i], clickY[i]); context.stroke(); } } function mouseDownEventHandler(e) { paint = true; var x = e.pageX - canvas.offsetLeft; var y = e.pageY - canvas.offsetTop; if (paint) { addClick(x, y, false); drawNew(); } } function touchstartEventHandler(e) { paint = true; if (paint) { addClick(e.touches[0].pageX - canvas.offsetLeft, e.touches[0].pageY - canvas.offsetTop, false); drawNew(); } } function mouseUpEventHandler(e) { context.closePath(); paint = false; } function mouseMoveEventHandler(e) { var x = e.pageX - canvas.offsetLeft; var y = e.pageY - canvas.offsetTop; if (paint) { addClick(x, y, true); drawNew(); } } function touchMoveEventHandler(e) { if (paint) { addClick(e.touches[0].pageX - canvas.offsetLeft, e.touches[0].pageY - canvas.offsetTop, true); drawNew(); } } function setUpHandler(isMouseandNotTouch, detectEvent) { removeRaceHandlers(); if (isMouseandNotTouch) { canvas.addEventListener('mouseup', mouseUpEventHandler); canvas.addEventListener('mousemove', mouseMoveEventHandler); canvas.addEventListener('mousedown', mouseDownEventHandler); mouseDownEventHandler(detectEvent); } else { canvas.addEventListener('touchstart', touchstartEventHandler); canvas.addEventListener('touchmove', touchMoveEventHandler); canvas.addEventListener('touchend', mouseUpEventHandler); touchstartEventHandler(detectEvent); } } function mouseWins(e) { setUpHandler(true, e); } function touchWins(e) { setUpHandler(false, e); } function removeRaceHandlers() { canvas.removeEventListener('mousedown', mouseWins); canvas.removeEventListener('touchstart', touchWins); } canvas.addEventListener('mousedown', mouseWins); canvas.addEventListener('touchstart', touchWins); </script> </body> </html> 

Jsfiddle

  • The width of the lines can be controlled using context.lineWidth .
  • The color of the lines can be controlled using strokeStyle .

This solution works with:

  • Desktop computers:
    • Chrome 33
    • Firefox 28
  • Touch devices:
    • Firefox 28 on Nexus 4

He does not work with

  • Touch devices:
    • Chrome 34 / Opera 20 on Nexus 4 (see issue )

Import Export

Image import and export can be done by import / export of clickX , clickY and clickDrag .

Line smoothing

Ultimately, it will be possible to replace lineTo() with bezierCurveTo()

+6
Apr 6
source share

Easeljs

Simple example

 A basic, complete example. That means it has to contain HTML and JavaScript. You can start with this: <!DOCTYPE html> <html> <head> <title>EaselJS example</title> <script type="text/javascript" src="http://cdnjs.cloudflare.com/ajax/libs/EaselJS/0.7.1/easeljs.min.js"></script> <script> var canvas, stage; var drawingCanvas; var oldPt; var oldMidPt; var color; var stroke; var index; function init() { if (window.top != window) { document.getElementById("header").style.display = "none"; } canvas = document.getElementById("sheet"); index = 0; //check to see if we are running in a browser with touch support stage = new createjs.Stage(canvas); stage.autoClear = false; stage.enableDOMEvents(true); createjs.Touch.enable(stage); createjs.Ticker.setFPS(24); drawingCanvas = new createjs.Shape(); stage.addEventListener("stagemousedown", handleMouseDown); stage.addEventListener("stagemouseup", handleMouseUp); stage.addChild(drawingCanvas); stage.update(); } function stop() {} function handleMouseDown(event) { color = "#ff0000"; stroke = 5; oldPt = new createjs.Point(stage.mouseX, stage.mouseY); oldMidPt = oldPt; stage.addEventListener("stagemousemove" , handleMouseMove); } function handleMouseMove(event) { var midPt = new createjs.Point(oldPt.x + stage.mouseX>>1, oldPt.y+stage.mouseY>>1); drawingCanvas.graphics.clear().setStrokeStyle(stroke, 'round', 'round').beginStroke(color).moveTo(midPt.x, midPt.y).curveTo(oldPt.x, oldPt.y, oldMidPt.x, oldMidPt.y); oldPt.x = stage.mouseX; oldPt.y = stage.mouseY; oldMidPt.x = midPt.x; oldMidPt.y = midPt.y; stage.update(); } function handleMouseUp(event) { stage.removeEventListener("stagemousemove" , handleMouseMove); } </script> </head> <body onload="init();"> <canvas id="sheet" width="400" height="400"></canvas> </body> </html> 

Demo

The interesting parts in the documentation are:

This solution works with:

  • Desktop computers:
    • Chrome 33
    • Firefox 28
  • Touch devices:
    • Chrome 34 / Firefox 28 / Opera 20 on Nexus 4

Import Export

?

Line smoothing

?

+2
Apr 6 '14 at 15:38
source share

Paper.js

Simple example

 <!DOCTYPE html> <html> <head> <title>Paper.js example</title> <script type='text/javascript' src='http://paperjs.org/assets/js/paper.js'></script> <style type='text/css'> #sheet { border:1px solid black; } </style> </head> <body> <script type="text/paperscript" canvas="sheet"> var path; function onMouseDown(event) { // If we produced a path before, deselect it: if (path) { path.selected = false; } // Create a new path and set its stroke color to black: path = new Path({ segments: [event.point], strokeColor: 'black', strokeWidth: 3 }); } // While the user drags the mouse, points are added to the path // at the position of the mouse: function onMouseDrag(event) { path.add(event.point); } // When the mouse is released, we simplify the path: function onMouseUp(event) { path.simplify(); } </script> <canvas id="sheet" width="400" height="400"></canvas> </body> </html> 

Jsfiddle

  • The width of the lines can be controlled using strokeWidth .
  • The color of the lines can be controlled using strokeColor .

This solution works with:

  • Desktop computers:
    • Chrome 33

Import Export

?

Line smoothing

Line smoothing can be done by adjusting path.simplify(); .

+1
Apr 6 '14 at 12:25
source share

(Disclaimer: I wrote this library)

Scrawl.js

Simple example

 <!DOCTYPE html> <html> <head> <title>Simple example</title> <style type='text/css'> #sheet {border:1px solid black;} </style> </head> <body> <canvas id="sheet" width="400" height="400"></canvas> <script src="http://scrawl.rikweb.org.uk/js/scrawlCore-min.js"></script> <script> var mycode = function(){ //define variables var myPad = scrawl.pad.sheet, myCanvas = scrawl.canvas.sheet, sX, sY, here, drawing = false, currentSprite = false, startDrawing, endDrawing; //event listeners startDrawing = function(e){ drawing = true; currentSprite = scrawl.newShape({ start: here, lineCap: 'round', lineJoin: 'round', method: 'draw', lineWidth: 4, strokeStyle: 'red', data: 'l0,0 ', }); sX = here.x; sY = here.y; if(e){ e.stopPropagation(); e.preventDefault(); } }; myCanvas.addEventListener('mousedown', startDrawing, false); endDrawing = function(e){ if(currentSprite){ currentSprite = false; } drawing = false; if(e){ e.stopPropagation(); e.preventDefault(); } }; myCanvas.addEventListener('mouseup', endDrawing, false); //animation object scrawl.newAnimation({ fn: function(){ //get current mouse position here = myPad.getMouse(); if(here.active){ if(drawing){ if(here.x !== sX || here.y !== sY){ //extend the line currentSprite.set({ data: currentSprite.data+' '+(here.x - sX)+','+(here.y - sY), }); sX = here.x; sY = here.y; } } } else{ //stop drawing if mouse leaves canvas area if(currentSprite){ endDrawing(); } } //update display scrawl.render(); }, }); }; //Scrawl is modular - load additional modules scrawl.loadModules({ path: 'js/', modules: ['animation', 'shape'], callback: function(){ window.addEventListener('load', function(){ scrawl.init(); //start Scrawl mycode(); //run code }, false); }, }); </script> </body> </html> 

Jsfiddle

This solution works with:

  • latest versions of IE, Chrome, Firefox, Opera (desktop)
  • (not tested on mobile / touch devices)

Adding Touch Support

  • (try adding a custom touch library like Hammer.js?)

Import Export

Line smoothing and other sprites

  • line data is stored internally as the value of SVGTiny Path.d - any algorithm that can receive line data in this format and smooth it should work
  • line attributes - thickness, color, positioning, rotation, etc. - can be set and animated.
0
Apr 6 '14 at 14:00
source share

Here try my free canvas and erase.

https://jsfiddle.net/richardcwc/d2gxjdva/

 //Canvas var canvas = document.getElementById('canvas'); var ctx = canvas.getContext('2d'); //Variables var canvasx = $(canvas).offset().left; var canvasy = $(canvas).offset().top; var last_mousex = last_mousey = 0; var mousex = mousey = 0; var mousedown = false; var tooltype = 'draw'; //Mousedown $(canvas).on('mousedown', function(e) { last_mousex = mousex = parseInt(e.clientX-canvasx); last_mousey = mousey = parseInt(e.clientY-canvasy); mousedown = true; }); //Mouseup $(canvas).on('mouseup', function(e) { mousedown = false; }); //Mousemove $(canvas).on('mousemove', function(e) { mousex = parseInt(e.clientX-canvasx); mousey = parseInt(e.clientY-canvasy); if(mousedown) { ctx.beginPath(); if(tooltype=='draw') { ctx.globalCompositeOperation = 'source-over'; ctx.strokeStyle = 'black'; ctx.lineWidth = 3; } else { ctx.globalCompositeOperation = 'destination-out'; ctx.lineWidth = 10; } ctx.moveTo(last_mousex,last_mousey); ctx.lineTo(mousex,mousey); ctx.lineJoin = ctx.lineCap = 'round'; ctx.stroke(); } last_mousex = mousex; last_mousey = mousey; //Output $('#output').html('current: '+mousex+', '+mousey+'<br/>last: '+last_mousex+', '+last_mousey+'<br/>mousedown: '+mousedown); }); //Use draw|erase use_tool = function(tool) { tooltype = tool; //update } 
 canvas { cursor: crosshair; border: 1px solid #000000; } 
 <canvas id="canvas" width="800" height="500"></canvas> <input type="button" value="draw" onclick="use_tool('draw');" /> <input type="button" value="erase" onclick="use_tool('erase');" /> <div id="output"></div> 
0
Nov 13 '15 at 1:35
source share



All Articles