Draw on HTML5 Canvas using a mouse
Asked Answered
S

13

178

I want to draw on a HTML Canvas using a mouse (for example, draw a signature, draw a name, ...)

How would I go about implementing this?

Staysail answered 3/3, 2010 at 4:29 Comment(4)
check this! #3814942Niall
Related to javascript library for free form drawingIrs
See also: How do I hand draw on canvas?Irs
A related question: After you draw on a canvas, how to store and post the mouse positions to a server to record (re-trace) the drawing?Cornucopia
H
314

Here is a working sample.

 <html>
    <script type="text/javascript">
    var canvas, ctx, flag = false,
        prevX = 0,
        currX = 0,
        prevY = 0,
        currY = 0,
        dot_flag = false;

    var x = "black",
        y = 2;
    
    function init() {
        canvas = document.getElementById('can');
        ctx = canvas.getContext("2d");
        w = canvas.width;
        h = canvas.height;
    
        canvas.addEventListener("mousemove", function (e) {
            findxy('move', e)
        }, false);
        canvas.addEventListener("mousedown", function (e) {
            findxy('down', e)
        }, false);
        canvas.addEventListener("mouseup", function (e) {
            findxy('up', e)
        }, false);
        canvas.addEventListener("mouseout", function (e) {
            findxy('out', e)
        }, false);
    }
    
    function color(obj) {
        switch (obj.id) {
            case "green":
                x = "green";
                break;
            case "blue":
                x = "blue";
                break;
            case "red":
                x = "red";
                break;
            case "yellow":
                x = "yellow";
                break;
            case "orange":
                x = "orange";
                break;
            case "black":
                x = "black";
                break;
            case "white":
                x = "white";
                break;
        }
        if (x == "white") y = 14;
        else y = 2;
    
    }
    
    function draw() {
        ctx.beginPath();
        ctx.moveTo(prevX, prevY);
        ctx.lineTo(currX, currY);
        ctx.strokeStyle = x;
        ctx.lineWidth = y;
        ctx.stroke();
        ctx.closePath();
    }
    
    function erase() {
        var m = confirm("Want to clear");
        if (m) {
            ctx.clearRect(0, 0, w, h);
            document.getElementById("canvasimg").style.display = "none";
        }
    }
    
    function save() {
        document.getElementById("canvasimg").style.border = "2px solid";
        var dataURL = canvas.toDataURL();
        document.getElementById("canvasimg").src = dataURL;
        document.getElementById("canvasimg").style.display = "inline";
    }
    
    function findxy(res, e) {
        if (res == 'down') {
            prevX = currX;
            prevY = currY;
            currX = e.clientX - canvas.offsetLeft;
            currY = e.clientY - canvas.offsetTop;
    
            flag = true;
            dot_flag = true;
            if (dot_flag) {
                ctx.beginPath();
                ctx.fillStyle = x;
                ctx.fillRect(currX, currY, 2, 2);
                ctx.closePath();
                dot_flag = false;
            }
        }
        if (res == 'up' || res == "out") {
            flag = false;
        }
        if (res == 'move') {
            if (flag) {
                prevX = currX;
                prevY = currY;
                currX = e.clientX - canvas.offsetLeft;
                currY = e.clientY - canvas.offsetTop;
                draw();
            }
        }
    }
    </script>
    <body onload="init()">
        <canvas id="can" width="400" height="400" style="position:absolute;top:10%;left:10%;border:2px solid;"></canvas>
        <div style="position:absolute;top:12%;left:43%;">Choose Color</div>
        <div style="position:absolute;top:15%;left:45%;width:10px;height:10px;background:green;" id="green" onclick="color(this)"></div>
        <div style="position:absolute;top:15%;left:46%;width:10px;height:10px;background:blue;" id="blue" onclick="color(this)"></div>
        <div style="position:absolute;top:15%;left:47%;width:10px;height:10px;background:red;" id="red" onclick="color(this)"></div>
        <div style="position:absolute;top:17%;left:45%;width:10px;height:10px;background:yellow;" id="yellow" onclick="color(this)"></div>
        <div style="position:absolute;top:17%;left:46%;width:10px;height:10px;background:orange;" id="orange" onclick="color(this)"></div>
        <div style="position:absolute;top:17%;left:47%;width:10px;height:10px;background:black;" id="black" onclick="color(this)"></div>
        <div style="position:absolute;top:20%;left:43%;">Eraser</div>
        <div style="position:absolute;top:22%;left:45%;width:15px;height:15px;background:white;border:2px solid;" id="white" onclick="color(this)"></div>
        <img id="canvasimg" style="position:absolute;top:10%;left:52%;" style="display:none;">
        <input type="button" value="save" id="btn" size="30" onclick="save()" style="position:absolute;top:55%;left:10%;">
        <input type="button" value="clear" id="clr" size="23" onclick="erase()" style="position:absolute;top:55%;left:15%;">
    </body>
    </html>
Hoo answered 6/12, 2011 at 10:12 Comment(14)
Why do you do switch? You can also do x = obj.id. Done.Zygote
I'm trying to use this code, but have issues with the drawing being off vertically when I'm not scrolled all the way down on the page. What should I change in this code?Redeemable
I know its a bit late, but I've tried this in Safari for the iPad and it does not work. Any fix for this?Radiocarbon
@Radiocarbon I believe it is because it uses mouse events, which do not work well with touch devices.Disrespectable
seems variable dot_flag is not needed? Or am I missing something?Clothespress
@CameronDarlington See this question for a few quality ways of grabbing the mouse position: #17130895 I had the same problem.Gertiegertrud
@Clothespress the dot_flag is used to create the initial drawing. It's not necessary, but your drawing will always be off from where you click if you don't use it, so it's a bit inaccurateGertiegertrud
great resource but it doesn't consider the scroll effect for mouse x and yJitters
I changed canvas.offsetLeft; and canvas.offsetTop; with canvas.getBoundingClientRect().left; and canvas.getBoundingClientRect().top; respectively to fix the scrolling problem.Multipurpose
This code got me started on the right path. But i have to say that this code is pretty rough and ugly. For anyone finding this and struggling with mouse position, take a look at this answer: https://mcmap.net/q/100581/-real-mouse-position-in-canvasConjoint
Does not work on a touch screen. Not referring to tablets. I mean a Windows 10 laptop with both a mouse and a touch screen.Loquacity
to get this to work with a drawing pen(like a graphics tablet) the mouse events have to be replaced with touch events eg. touchmove, touchstart, touchend and then the clientX comes from e.touches["0"].clientX in the findxy() code, haven't thought of an easy way to detect what's being used though, as you can't listen to both events at the same time from what I've tested. I left mouseout as is. It's not perfect, but it does workPtisan
working on this again, wanted to point out if your position is offset the places to edit are in findxy specifically the currX, currY for down/move. add or subtract there till it's rightPtisan
For odd context.lineWidth (e.g. 1, 3) you should add 0.5 to both prevX/Y and currX/Y, otherwise the line will not be clear - https://mcmap.net/q/144124/-html5-canvas-and-line-width/…Bookcraft
C
89

I think, other examples here are too complicated. This one is simpler and JS only...

// create canvas element and append it to document body
var canvas = document.createElement('canvas');
document.body.appendChild(canvas);

// some hotfixes... ( ≖_≖)
document.body.style.margin = 0;
canvas.style.position = 'fixed';

// get canvas 2D context and set him correct size
var ctx = canvas.getContext('2d');
resize();

// last known position
var pos = { x: 0, y: 0 };

window.addEventListener('resize', resize);
document.addEventListener('mousemove', draw);
document.addEventListener('mousedown', setPosition);
document.addEventListener('mouseenter', setPosition);

// new position from mouse event
function setPosition(e) {
  pos.x = e.clientX;
  pos.y = e.clientY;
}

// resize canvas
function resize() {
  ctx.canvas.width = window.innerWidth;
  ctx.canvas.height = window.innerHeight;
}

function draw(e) {
  // mouse left button must be pressed
  if (e.buttons !== 1) return;

  ctx.beginPath(); // begin

  ctx.lineWidth = 5;
  ctx.lineCap = 'round';
  ctx.strokeStyle = '#c0392b';

  ctx.moveTo(pos.x, pos.y); // from
  setPosition(e);
  ctx.lineTo(pos.x, pos.y); // to

  ctx.stroke(); // draw it!
}
Celestine answered 6/6, 2015 at 15:43 Comment(13)
How do I get it to paint on mouse move, not by clicking?Saltcellar
@Saltcellar just comment line if (e.buttons !== 1) return; ;-).Vacillating
I got it working on my footer but now the line is about 10px away from my cursorSaltcellar
If my cursor is in the middle of the canvas it lines up perfectly with the line but if I move the cursor up or down, the line moves further away from the cursor. Any ideas?Saltcellar
@Saltcellar Just tried newest Chrome, Firefox and Edge and everything working... What browser do you use?Vacillating
I think it's because I made the canvas relative. Please scroll to the footer on this page to see what I am talking about: weight.ryancameron.meSaltcellar
@Saltcellar It's more likely because my resize function. I'm setting width and height of canvas based on window size. You should set these based on your <div class="container-fluid">.Vacillating
that helped with also subtracting about 250 from the y coordinate on the set position function. Thanks so much for your help!Saltcellar
this will only work if the canvas is the full width and height of the windowJoyner
@TaylorA.Leach yep, if canvas is not placed in top right corner, you will need to add some offset in setPosition function...Vacillating
What is resize() for? Why do you need to resize the canvas? It can be fixed in width and height.Ambulatory
@Ambulatory Yep, you can have canvas with fixed width and height, but I choose version where canvas cover entire page. It did not add much complexity, but looks better for this demo :-).Vacillating
This is great but makes assumptions about canvas positioning. More generic: var rect = canvas.getBoundingClientRect(); pos.x = e.clientX - rect.left; pos.y = e.clientY - rect.top;Mahmoud
C
48

Here's the most straightforward way to create a drawing application with canvas:

  1. Attach a mousedown, mousemove, and mouseup event listener to the canvas DOM
  2. on mousedown, get the mouse coordinates, and use the moveTo() method to position your drawing cursor and the beginPath() method to begin a new drawing path.
  3. on mousemove, continuously add a new point to the path with lineTo(), and color the last segment with stroke().
  4. on mouseup, set a flag to disable the drawing.

From there, you can add all kinds of other features like giving the user the ability to choose a line thickness, color, brush strokes, and even layers.

Conspicuous answered 16/9, 2011 at 0:9 Comment(2)
If there is beginPath, should there be closePath after mouseup?Ambulatory
@Ambulatory When you stroke(), it autmatically closes the path.Addition
D
13

I was looking to use this method for signatures as well, i found a sample on http://codetheory.in/.

I've added the below code to a jsfiddle

Html:

<div id="sketch">
    <canvas id="paint"></canvas>
</div>

Javascript:

 (function() {
    var canvas = document.querySelector('#paint');
    var ctx = canvas.getContext('2d');

    var sketch = document.querySelector('#sketch');
    var sketch_style = getComputedStyle(sketch);
    canvas.width = parseInt(sketch_style.getPropertyValue('width'));
    canvas.height = parseInt(sketch_style.getPropertyValue('height'));

    var mouse = {x: 0, y: 0};
    var last_mouse = {x: 0, y: 0};

    /* Mouse Capturing Work */
    canvas.addEventListener('mousemove', function(e) {
        last_mouse.x = mouse.x;
        last_mouse.y = mouse.y;

        mouse.x = e.pageX - this.offsetLeft;
        mouse.y = e.pageY - this.offsetTop;
    }, false);


    /* Drawing on Paint App */
    ctx.lineWidth = 5;
    ctx.lineJoin = 'round';
    ctx.lineCap = 'round';
    ctx.strokeStyle = 'blue';

    canvas.addEventListener('mousedown', function(e) {
        canvas.addEventListener('mousemove', onPaint, false);
    }, false);

    canvas.addEventListener('mouseup', function() {
        canvas.removeEventListener('mousemove', onPaint, false);
    }, false);

    var onPaint = function() {
        ctx.beginPath();
        ctx.moveTo(last_mouse.x, last_mouse.y);
        ctx.lineTo(mouse.x, mouse.y);
        ctx.closePath();
        ctx.stroke();
    };

}());
Disused answered 22/8, 2013 at 15:9 Comment(3)
The best part is you can draw straight connected lines by right clicking at different locations. :)Cockney
Does not work on a touch screen. Not referring to tablets. I mean a Windows 10 laptop with both a mouse and a touch screen.Loquacity
I get: Uncaught TypeError: Failed to execute 'getComputedStyle' on 'Window': parameter 1 is not of type 'Element'.Ambulatory
T
7

I had to provide a simple example for this subject so I'll share here:

http://jsfiddle.net/Haelle/v6tfp2e1

class SignTool {
  constructor() {
    this.initVars()
    this.initEvents()
  }

  initVars() {
    this.canvas = $('#canvas')[0]
    this.ctx = this.canvas.getContext("2d")
    this.isMouseClicked = false
    this.isMouseInCanvas = false
    this.prevX = 0
    this.currX = 0
    this.prevY = 0
    this.currY = 0
  }

  initEvents() {
    $('#canvas').on("mousemove", (e) => this.onMouseMove(e))
    $('#canvas').on("mousedown", (e) => this.onMouseDown(e))
    $('#canvas').on("mouseup", () => this.onMouseUp())
    $('#canvas').on("mouseout", () => this.onMouseOut())
    $('#canvas').on("mouseenter", (e) => this.onMouseEnter(e))
  }
  
  onMouseDown(e) {
  	this.isMouseClicked = true
    this.updateCurrentPosition(e)
  }
  
  onMouseUp() {
  	this.isMouseClicked = false
  }
  
  onMouseEnter(e) {
  	this.isMouseInCanvas = true
    this.updateCurrentPosition(e)
  }
  
  onMouseOut() {
  	this.isMouseInCanvas = false
  }

  onMouseMove(e) {
    if (this.isMouseClicked && this.isMouseInCanvas) {
    	this.updateCurrentPosition(e)
      this.draw()
    }
  }
  
  updateCurrentPosition(e) {
      this.prevX = this.currX
      this.prevY = this.currY
      this.currX = e.clientX - this.canvas.offsetLeft
      this.currY = e.clientY - this.canvas.offsetTop
  }
  
  draw() {
    this.ctx.beginPath()
    this.ctx.moveTo(this.prevX, this.prevY)
    this.ctx.lineTo(this.currX, this.currY)
    this.ctx.strokeStyle = "black"
    this.ctx.lineWidth = 2
    this.ctx.stroke()
    this.ctx.closePath()
  }
}

var canvas = new SignTool()
canvas {
  position: absolute;
  border: 2px solid;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<canvas id="canvas" width="500" height="300"></canvas>
Trunnel answered 10/10, 2018 at 15:56 Comment(1)
This is a wonderful example and simple to understand in a class !!! I converted it to vanilla JavaScript to avoid loading extra crappy jquery ! Thank you !!!!!Allograph
A
6

Here is my very simple working canvas draw 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>
Afterbirth answered 13/11, 2015 at 1:39 Comment(0)
C
6

A super short version, here, without position:absolute in vanilla JavaScript. The main idea is to move the canvas' context to the right coordinates and draw a line. Uncomment click handler and comment mousedown & mousemove handlers below to get a feel for how it is working.

<!DOCTYPE html>
<html>
<body>

<p style="margin: 50px">Just some padding in y direction</p>

<canvas id="myCanvas" width="300" height="300" style="background: #000; margin-left: 100px;">Your browser does not support the HTML5 canvas tag.</canvas>

<script>
    const c = document.getElementById("myCanvas");
    // c.addEventListener("click", penTool); // fires after mouse left btn is released
    c.addEventListener("mousedown", setLastCoords); // fires before mouse left btn is released
    c.addEventListener("mousemove", freeForm);


    const ctx = c.getContext("2d");

    function setLastCoords(e) {
        const {x, y} = c.getBoundingClientRect();
        lastX = e.clientX - x;
        lastY = e.clientY - y;
    }

    function freeForm(e) {
        if (e.buttons !== 1) return; // left button is not pushed yet
        penTool(e);
    }

    function penTool(e) {
        const {x, y} = c.getBoundingClientRect();
        const newX = e.clientX - x;
        const newY = e.clientY - y;

        ctx.beginPath();
        ctx.lineWidth = 5;
        ctx.moveTo(lastX, lastY);
        ctx.lineTo(newX, newY);
        ctx.strokeStyle = 'white';
        ctx.stroke();
        ctx.closePath();

        lastX = newX;
        lastY = newY;
    }

    let lastX = 0;
    let lastY = 0;  
</script>

</body>
</html>
Cinchona answered 25/12, 2019 at 18:2 Comment(1)
It is important, not to set the width and height in CSS as it only stretches the canvas instead of resizing it - https://mcmap.net/q/144125/-offset-when-drawing-on-canvasCinchona
D
3

Also check this one here:
Example:
https://github.com/williammalone/Simple-HTML5-Drawing-App

Documentation:
http://www.williammalone.com/articles/create-html5-canvas-javascript-drawing-app/

This document includes following codes:

HTML:

<canvas id="canvas" width="490" height="220"></canvas>

JS:

context = document.getElementById('canvas').getContext("2d");

$('#canvas').mousedown(function(e){
  var mouseX = e.pageX - this.offsetLeft;
  var mouseY = e.pageY - this.offsetTop;
        
  paint = true;
  addClick(e.pageX - this.offsetLeft, e.pageY - this.offsetTop);
  redraw();
});

$('#canvas').mouseup(function(e){
  paint = false;
});

$('#canvas').mouseleave(function(e){
  paint = false;
});

var clickX = new Array();
var clickY = new Array();
var clickDrag = new Array();
var paint;

function addClick(x, y, dragging)
{
  clickX.push(x);
  clickY.push(y);
  clickDrag.push(dragging);
}

//Also redraw
function redraw(){
  context.clearRect(0, 0, context.canvas.width, context.canvas.height); // Clears the canvas
  
  context.strokeStyle = "#df4b26";
  context.lineJoin = "round";
  context.lineWidth = 5;
            
  for(var i=0; i < clickX.length; i++) {        
    context.beginPath();
    if(clickDrag[i] && i){
      context.moveTo(clickX[i-1], clickY[i-1]);
     }else{
       context.moveTo(clickX[i]-1, clickY[i]);
     }
     context.lineTo(clickX[i], clickY[i]);
     context.closePath();
     context.stroke();
  }
}

And another awesome example
http://perfectionkills.com/exploring-canvas-drawing-techniques/

Dovelike answered 11/2, 2014 at 5:3 Comment(0)
V
3

It's been years since the question was asked and was answered.

For anyone who looks for a simple drawing canvas (eg, for taking the signature from the user/customer), here I am posting a more simplified jquery version of the currently accepted answer

$(document).ready(function() {
  var flag, dot_flag = false,
	prevX, prevY, currX, currY = 0,
	color = 'black', thickness = 2;
  var $canvas = $('#canvas');
  var ctx = $canvas[0].getContext('2d');

  $canvas.on('mousemove mousedown mouseup mouseout', function(e) {
    prevX = currX;
    prevY = currY;
    currX = e.clientX - $canvas.offset().left;
    currY = e.clientY - $canvas.offset().top;
    if (e.type == 'mousedown') {
      flag = true;
    }
    if (e.type == 'mouseup' || e.type == 'mouseout') {
      flag = false;
    }
    if (e.type == 'mousemove') {
      if (flag) {
        ctx.beginPath();
        ctx.moveTo(prevX, prevY);
        ctx.lineTo(currX, currY);
        ctx.strokeStyle = color;
        ctx.lineWidth = thickness;
        ctx.stroke();
        ctx.closePath();
      }
    }
  });

  $('.canvas-clear').on('click', function(e) {
    c_width = $canvas.width();
    c_height = $canvas.height();
    ctx.clearRect(0, 0, c_width, c_height);
    $('#canvasimg').hide();
  });
});
<html>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.0/jquery.min.js"></script>
  <body>
    <canvas id="canvas" width="400" height="400" style="position:absolute;top:10%;left:10%;border:2px solid;"></canvas>
    <input type="button" value="Clear" class="canvas-clear" />
  </body>
</html>
Vain answered 19/6, 2019 at 7:59 Comment(0)
L
3

I took the first answer and Changed as per my need. Now

  1. Works on mobile device also.
  2. more responsive,
  3. no issue with scroll

 <html>
  <head>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" integrity="sha384-xOolHFLEh07PJGoPkLv1IbcEPTNtaed2xpHsD9ESMhqIYd0nLMwNLD69Npy4HI+N" crossorigin="anonymous">
    <style>
        .selected::before {

           content: 'x';
           height: 30px;
           width: 30px;
           left: 18px;
           top: -8px;
           font-size: 3rem;
           position: absolute;
        }

        .select-colour{
          height: 30px;
          width: 30px;
        }

    </style>
 </head>
    <script type="text/javascript">
    var canvas, ctx, flag = false,
        prevX = 0,
        currX = 0,
        prevY = 0,
        currY = 0,
        dot_flag = false;

    var x = "black",
        y = 2;
    
    function init() {
        canvas = document.getElementById('can');
        ctx = canvas.getContext("2d");
        w = canvas.width;
        h = canvas.height;
    
        canvas.addEventListener("mousemove", function (e) {
            findxy('move', e, '')
        }, false);
        canvas.addEventListener("mousedown", function (e) {
            findxy('down', e, '')
        }, false);
        canvas.addEventListener("mouseup", function (e) {
            findxy('up', e, '')
        }, false);
        canvas.addEventListener("mouseout", function (e) {
            findxy('out', e, '')
        }, false);

        canvas.addEventListener("touchmove", function (e) {
            findxy('move', e, 'touch')
        }, false);
        canvas.addEventListener("touchstart", function (e) {
            findxy('down', e, 'touch')
        }, false);
        canvas.addEventListener("touchend", function (e) {
            findxy('up', e, 'touch')
        }, false);    

    }
    
    function color(obj) {        
        x = obj.id; 
        if (x == "white") y = 14;
        else y = 2;
        var prevSelected = document.getElementsByClassName("selected");
        // If it exists, remove it.
        if(prevSelected.length > 0) { 
          prevSelected[0].classList.remove("selected");
        }
                 
       document.getElementById(obj.id).classList.add("selected");
    }
    
    function draw() {
        ctx.beginPath();
        ctx.moveTo(prevX, prevY);
        ctx.lineTo(currX, currY);
        ctx.strokeStyle = x;
        ctx.lineWidth = y;
        ctx.stroke();
        ctx.closePath();
    }
    
    function erase() {
        var m = confirm("Want to clear");
        if (m) {
            ctx.clearRect(0, 0, w, h);
            document.getElementById("canvasimg").style.display = "none";
        }
    }
    
    function save() {
        document.getElementById("canvasimg").style.border = "2px solid";
        var dataURL = canvas.toDataURL();
        document.getElementById("canvasimg").src = dataURL;
        document.getElementById("canvasimg").style.display = "inline";
    }

    function download() {
        var link = document.createElement('a');
        var dataURL = canvas.toDataURL();
        link.href = dataURL;
        link.download = 'mydrawing.png';
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
    }
    
    function findxy(res, e, source) {
        if(source){
            var clientX = e.changedTouches[0].clientX;
            var clientY = e.changedTouches[0].clientY;
        }else{
            var clientX = e.clientX;
            var clientY = e.clientY;
        }

        if (res == 'down') {
            prevX = currX;
            prevY = currY;
            currX = clientX - canvas.getBoundingClientRect().left;
            currY = clientY - canvas.getBoundingClientRect().top;
    
            flag = true;
            dot_flag = true;
            if (dot_flag) {
                ctx.beginPath();
                ctx.fillStyle = x;
                ctx.fillRect(currX, currY, 2, 2);
                ctx.closePath();
                dot_flag = false;
            }
        }

        if (res == 'up' || res == "out") {
            flag = false;
        }

        if (res == 'move') {
            if (flag) {
                prevX = currX;
                prevY = currY;
                currX = clientX - canvas.getBoundingClientRect().left;
                currY = clientY - canvas.getBoundingClientRect().top;
                draw();
            }
        }
    }
    </script>
  <body onload="init()">
        <div style="">
                <div style="float: left;">
                   <canvas id="can" width="600" height="500" style=" border:5px solid;"></canvas>
                </div>
                <div style="margin-left:20px; float:left;">
                    <div class="row">
                        <div class="col-12">Choose Color</div>
                        <div class="col-2 pt-3"><div class="select-colour" style="background:green;" id="green" onclick="color(this)"></div></div>
                       <div class="col-2 pt-3"><div class="select-colour" style="background:blue;" id="blue" onclick="color(this)"></div></div>
                       <div class="col-2 pt-3"><div class="select-colour" style="background:red;" id="red" onclick="color(this)"></div></div>
                       <div class="col-2 pt-3"><div class="select-colour" style="background:yellow;" id="yellow" onclick="color(this)"></div></div>
                       <div class="col-2 pt-3"><div class="select-colour" style="background:orange;" id="orange" onclick="color(this)"></div></div>
                       <div class="col-2 pt-3"><div class="select-colour" style="background:black;" id="black" onclick="color(this)"></div></div>
                       <div class="col-12 pt-3 pb-3" style="">Eraser</div>
                       <div class="col-2 pt-3"><div class="select-colour" style="background:white;border:2px solid;" id="white" onclick="color(this)"></div></div>
                </div>

                <div class="row">
                    <div class="col-6 col-lg-12 mt-3">
                        <input type="button" value="Download" id="download" size="23" onclick="download()" class="btn btn-primary">
                    </div>
                    <div class="col-6 col-lg-12 mt-3">
                            <input type="button" value="clear" id="clr" size="23" onclick="erase()" class="btn btn-light">
                    </div>          
                </div>
           </div>

        </div>
    </body>
    </html>
Lashundalasker answered 8/9, 2022 at 13:26 Comment(0)
O
2

I used what 1083202 did but removed all the controlles, and implemented the change that KWILLIAMS suggested, that made it non sensitive to scrolling. I also made the canvas big, basically over my whole page 2000x1600 px, excepts for the margins. I did remove all the drawing tools and buttons, and used "blue" as the only color.

I put the JS-code in a separate file named myJS.js, and put that in a folder named "JS" localy:

I then used a Stylus on my Laptop to write on the tuchpad, witch works out a bit better then with the finger or mouse.

The document I use it for is kind of informal, for internal use, but it is nice to be able to put some pen strokes on it before making a pdf-file.

    var canvas, ctx, flag = false,
        prevX = 0,
        currX = 0,
        prevY = 0,
        currY = 0,
        dot_flag = false;

    var x = "blue",
        y = 3;
    
    function init() {
        canvas = document.getElementById('can');
        ctx = canvas.getContext("2d");
        w = canvas.width;
        h = canvas.height;
    
        canvas.addEventListener("mousemove", function (e) {
            findxy('move', e)
        }, false);
        canvas.addEventListener("mousedown", function (e) {
            findxy('down', e)
        }, false);
        canvas.addEventListener("mouseup", function (e) {
            findxy('up', e)
        }, false);
        canvas.addEventListener("mouseout", function (e) {
            findxy('out', e)
        }, false);
    }
 
    function draw() {
        ctx.beginPath();
        ctx.moveTo(prevX, prevY);
        ctx.lineTo(currX, currY);
        ctx.strokeStyle = x;
        ctx.lineWidth = y;
        ctx.stroke();
        ctx.closePath();
    }
   
    function findxy(res, e) {
        if (res == 'down') {
            prevX = currX;
            prevY = currY;
            currX = e.clientX - canvas.getBoundingClientRect().left;
            currY = e.clientY - canvas.getBoundingClientRect().top;
    
            flag = true;
            dot_flag = true;
            if (dot_flag) {
                ctx.beginPath();
                ctx.fillStyle = x;
                ctx.fillRect(currX, currY, 2, 2);
                ctx.closePath();
                dot_flag = false;
            }
        }
        if (res == 'up' || res == "out") {
            flag = false;
        }
        if (res == 'move') {
            if (flag) {
                prevX = currX;
                prevY = currY;
                currX = e.clientX - canvas.getBoundingClientRect().left;
                currY = e.clientY - canvas.getBoundingClientRect().top;
                draw();
            }
        }
    }
<html> 



<body onload="init()">
<p>Below you can draw:</p>
<canvas id="can" width="750" height="1050" style="position:absolute;">Below you can write:</canvas>
</body>
Ophicleide answered 27/5, 2021 at 14:15 Comment(0)
M
1

Let me know if you have trouble implementing this. It uses processing.js and has features for changing colors and making the draw point larger and smaller.

<html>
<head>
<!--script librarires-->
<script type="text/javascript" src="processing.js"></script>
<script type="text/javascript" src="init.js"></script>

<!--styles -->
<style type="text/css" src="stylesheet.css">
</style> 
</head>
<body>
<!--toolbox -->
<div id="draggable toolbox"></div>
<script type="application/processing">
// new script
int prevx, prevy;
int newx, newy;
boolean cliked;
color c1 = #000000;
int largeur=2;
int ps = 20;
int px = 50;
int py = 50;

void setup() {
size(500,500);
frameRate(25);
background(50);

 prevx = mouseX;
 prevy = mouseY;
 cliked = false;
 }

void draw() {
 //couleur
 noStroke(0);
 fill(#FFFFFF);//blanc
rect(px, py, ps, ps);
 fill(#000000);
 rect(px, py+(ps), ps, ps);
 fill(#FF0000);
 rect(px, py+(ps*2), ps, ps);
 fill(#00FF00);
 rect(px, py+(ps*3), ps, ps);
 fill(#FFFF00);
 rect(px, py+(ps*4), ps, ps);
 fill(#0000FF);
 rect(px, py+(ps*5), ps, ps);
 //largeur
 fill(#FFFFFF);
 rect(px, py+(ps*7), ps, ps);
  fill(#FFFFFF);
 rect(px, py+(ps*8), ps, ps);
 stroke(#000000);
 line(px+2, py+(ps*7)+(ps/2), px+(ps-2), py+(ps*7)+(ps/2));
 line(px+(ps/2), py+(ps*7)+1, px+(ps/2), py+(ps*8)-1);
 line(px+2, py+(ps*8)+(ps/2), px+(ps-2), py+(ps*8)+(ps/2));

 if(cliked==false){
  prevx = mouseX;
 prevy = mouseY;  
 }
 if(mousePressed) { 
  cliked = true;
 newx = mouseX;
  newy = mouseY;
  strokeWeight(largeur);
  stroke(c1);
  line(prevx, prevy, newx, newy);
  prevx = newx;
 prevy = newy;
 }else{
  cliked= false;
  }
}
void mouseClicked() {
 if (mouseX>=px && mouseX<=(px+ps)){
  //couleur
  if (mouseY>=py && mouseY<=py+(ps*6)){ 
   c1 = get(mouseX, mouseY);
  }
   //largeur
  if (mouseY>=py+(ps*7) && mouseY<=py+(ps*8)){ 
   largeur += 2;
  }
 if (mouseY>=py+(ps*8) && mouseY<=py+(ps*9)){ 
   if (largeur>2){
    largeur -= 2;
   }
  }
 }
}
</script><canvas></canvas>
</body>
</html>
Madsen answered 11/12, 2010 at 21:54 Comment(1)
i don't understand it, but it starts processing.Madsen
C
1

if you have background image for your canvas, you will have to make some tweaks to have it work properly because white erasing trick will hide the background.

here is a gist with the code.

<html>
    <script type="text/javascript">
    var canvas, canvasimg, backgroundImage, finalImg;
    var mouseClicked = false;
    var prevX = 0;
    var currX = 0;
    var prevY = 0;
    var currY = 0;
    var fillStyle = "black";
    var globalCompositeOperation = "source-over";
    var lineWidth = 2;

    function init() {
      var imageSrc = '/abstract-geometric-pattern_23-2147508597.jpg'
      backgroundImage = new Image();
      backgroundImage.src = imageSrc;
      canvas = document.getElementById('can');
      finalImg = document.getElementById('finalImg');
      canvasimg = document.getElementById('canvasimg');
      canvas.style.backgroundImage = "url('" + imageSrc + "')";
      canvas.addEventListener("mousemove", handleMouseEvent);
      canvas.addEventListener("mousedown", handleMouseEvent);
      canvas.addEventListener("mouseup", handleMouseEvent);
      canvas.addEventListener("mouseout", handleMouseEvent);
    }

    function getColor(btn) {
      globalCompositeOperation = 'source-over';
      lineWidth = 2;
      switch (btn.getAttribute('data-color')) {
        case "green":
        fillStyle = "green";
        break;
        case "blue":
        fillStyle = "blue";
        break;
        case "red":
        fillStyle = "red";
        break;
        case "yellow":
        fillStyle = "yellow";
        break;
        case "orange":
        fillStyle = "orange";
        break;
        case "black":
        fillStyle = "black";
        break;
        case "eraser":
        globalCompositeOperation = 'destination-out';
        fillStyle = "rgba(0,0,0,1)";
        lineWidth = 14;
        break;
      }

    }

    function draw(dot) {
      var ctx = canvas.getContext("2d");
      ctx.beginPath();
      ctx.globalCompositeOperation = globalCompositeOperation;
      if(dot){
        ctx.fillStyle = fillStyle;
        ctx.fillRect(currX, currY, 2, 2);
      } else {
        ctx.beginPath();
        ctx.moveTo(prevX, prevY);
        ctx.lineTo(currX, currY);
        ctx.strokeStyle = fillStyle;
        ctx.lineWidth = lineWidth;
        ctx.stroke();
      }
      ctx.closePath();
    }

    function erase() {
      if (confirm("Want to clear")) {
        var ctx = canvas.getContext("2d");
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        document.getElementById("canvasimg").style.display = "none";
      }
    }

    function save() {
      canvas.style.border = "2px solid";
      canvasimg.width = canvas.width;
      canvasimg.height = canvas.height;
      var ctx2 = canvasimg.getContext("2d");
      // comment next line to save the draw only
      ctx2.drawImage(backgroundImage, 0, 0);
      ctx2.drawImage(canvas, 0, 0);
      finalImg.src = canvasimg.toDataURL();
      finalImg.style.display = "inline";
    }

    function handleMouseEvent(e) {
      if (e.type === 'mousedown') {
        prevX = currX;
        prevY = currY;
        currX = e.offsetX;
        currY = e.offsetY;
        mouseClicked = true;
        draw(true);
      }
      if (e.type === 'mouseup' || e.type === "mouseout") {
        mouseClicked = false;
      }
      if (e.type === 'mousemove') {
        if (mouseClicked) {
          prevX = currX;
          prevY = currY;
          currX = e.offsetX;
          currY = e.offsetY;
          draw();
        }
      }
    }
    </script>
    <body onload="init()">
      <canvas id="can" width="400" height="400" style="position:absolute;top:10%;left:10%;border:2px solid;">
      </canvas>
      <div style="position:absolute;top:12%;left:43%;">Choose Color</div>
      <div style="position:absolute;top:15%;left:45%;width:10px;height:10px;background:green;" data-color="green" onclick="getColor(this)"></div>
      <div style="position:absolute;top:15%;left:46%;width:10px;height:10px;background:blue;" data-color="blue" onclick="getColor(this)"></div>
      <div style="position:absolute;top:15%;left:47%;width:10px;height:10px;background:red;" data-color="red" onclick="getColor(this)"></div>
      <div style="position:absolute;top:17%;left:45%;width:10px;height:10px;background:yellow;" data-color="yellow" onclick="getColor(this)"></div>
      <div style="position:absolute;top:17%;left:46%;width:10px;height:10px;background:orange;" data-color="orange" onclick="getColor(this)"></div>
      <div style="position:absolute;top:17%;left:47%;width:10px;height:10px;background:black;" data-color="black" onclick="getColor(this)"></div>
      <div style="position:absolute;top:20%;left:43%;">Eraser</div>
      <div style="position:absolute;top:22%;left:45%;width:15px;height:15px;background:white;border:2px solid;" data-color="eraser" onclick="getColor(this)"></div>
      <canvas id="canvasimg" style="display:none;" ></canvas>
      <img id="finalImg" style="position:absolute;top:10%;left:52%;display:none;" >
      <input type="button" value="save" id="btn" size="30" onclick="save()" style="position:absolute;top:55%;left:10%;">
      <input type="button" value="clear" id="clr" size="23" onclick="erase()" style="position:absolute;top:55%;left:15%;">
    </body>
    </html>
Codding answered 28/7, 2018 at 16:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.