I have a problem with my canvas drawing.
case1:
My PC can be used as touch screen as well as having a mouse. However, I can only draw using the touch screen. The mouse doesn't work.
case2:
My friend's PC only has a mouse and the canvas works fine.
Please help. I can see where the problem, but I'm not good enough to make changes.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Desktops and Tablets</title>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<script type="text/javascript">
$(document).ready(function () {
initialize();
});
// works out the X, Y position of the click inside the canvas from the X, Y position on the page
function getPosition(mouseEvent, sigCanvas) {
var x, y;
if (mouseEvent.pageX != undefined && mouseEvent.pageY != undefined) {
x = mouseEvent.pageX;
y = mouseEvent.pageY;
} else {
x = mouseEvent.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
y = mouseEvent.clientY + document.body.scrollTop + document.documentElement.scrollTop;
}
return { X: x - sigCanvas.offsetLeft, Y: y - sigCanvas.offsetTop };
}
function initialize() {
// get references to the canvas element as well as the 2D drawing context
var sigCanvas = document.getElementById("canvas1");
var context = sigCanvas.getContext("2d");
context.strokeStyle = 'Black';
// This will be defined on a TOUCH device such as iPad or Android, etc.
var is_touch_device = 'ontouchstart' in document.documentElement;
if (is_touch_device) {
// create a drawer which tracks touch movements
var drawer = {
isDrawing: false,
touchstart: function (coors) {
context.beginPath();
context.moveTo(coors.x, coors.y);
this.isDrawing = true;
},
touchmove: function (coors) {
if (this.isDrawing) {
context.lineTo(coors.x, coors.y);
context.stroke();
}
},
touchend: function (coors) {
if (this.isDrawing) {
this.touchmove(coors);
this.isDrawing = false;
}
}
};
// create a function to pass touch events and coordinates to drawer
function draw(event) {
// get the touch coordinates. Using the first touch in case of multi-touch
var coors = {
x: event.targetTouches[0].pageX,
y: event.targetTouches[0].pageY
};
// Now we need to get the offset of the canvas location
var obj = sigCanvas;
if (obj.offsetParent) {
// Every time we find a new object, we add its offsetLeft and offsetTop to curleft and curtop.
do {
coors.x -= obj.offsetLeft;
coors.y -= obj.offsetTop;
}
// The while loop can be "while (obj = obj.offsetParent)" only, which does return null
// when null is passed back, but that creates a warning in some editors (i.e. VS2010).
while ((obj = obj.offsetParent) != null);
}
// pass the coordinates to the appropriate handler
drawer[event.type](coors);
}
// attach the touchstart, touchmove, touchend event listeners.
sigCanvas.addEventListener('touchstart', draw, false);
sigCanvas.addEventListener('touchmove', draw, false);
sigCanvas.addEventListener('touchend', draw, false);
// prevent elastic scrolling
sigCanvas.addEventListener('touchmove', function (event) {
event.preventDefault();
}, false);
}
else {
// start drawing when the mousedown event fires, and attach handlers to
// draw a line to wherever the mouse moves to
$("#canvas1").mousedown(function (mouseEvent) {
var position = getPosition(mouseEvent, sigCanvas);
context.moveTo(position.X, position.Y);
context.beginPath();
// attach event handlers
$(this).mousemove(function (mouseEvent) {
drawLine(mouseEvent, sigCanvas, context);
}).mouseup(function (mouseEvent) {
finishDrawing(mouseEvent, sigCanvas, context);
}).mouseout(function (mouseEvent) {
finishDrawing(mouseEvent, sigCanvas, context);
});
});
}
}
// draws a line to the x and y coordinates of the mouse event inside
// the specified element using the specified context
function drawLine(mouseEvent, sigCanvas, context) {
var position = getPosition(mouseEvent, sigCanvas);
context.lineTo(position.X, position.Y);
context.stroke();
}
// draws a line from the last coordiantes in the path to the finishing
// coordinates and unbind any event handlers which need to be preceded
// by the mouse down event
function finishDrawing(mouseEvent, sigCanvas, context) {
// draw the line to the finishing coordinates
drawLine(mouseEvent, sigCanvas, context);
context.closePath();
// unbind any events which could draw
$(sigCanvas).unbind("mousemove")
.unbind("mouseup")
.unbind("mouseout");
}
</script>
</head>
<body>
<h1>Canvas test</h1>
<div id="canvasDiv">
<!-- It's bad practice (to me) to put your CSS here. I'd recommend the use of a CSS file! -->
<canvas id="canvas1" width="500px" height="500px" style="border:2px solid #000000; margin-left: 400px;
margin-top: 100px; "></canvas>
</div>
</body>
</html>
The code below is causing your trouble -- it exclusively binds to only touch events or only mouse events.
if (is_touch_device) {
....
}
else {
....
}
Maybe you could use jQuery's vmouse? Or try binding to both touch and mouse events...
Related
I'm wanting to implement canvas as background for my website so users can use their cursors to paint on the webpage like this codepen: https://codepen.io/cocotx/pen/PoGRdxQ?editors=1010
(this is an example code from http://www.dgp.toronto.edu/~clwen/test/canvas-paint-tutorial/)
if(window.addEventListener) {
window.addEventListener('load', function () {
var canvas, context;
// Initialization sequence.
function init () {
// Find the canvas element.
canvas = document.getElementById('imageView');
if (!canvas) {
alert('Error: I cannot find the canvas element!');
return;
}
if (!canvas.getContext) {
alert('Error: no canvas.getContext!');
return;
}
// Get the 2D canvas context.
context = canvas.getContext('2d');
if (!context) {
alert('Error: failed to getContext!');
return;
}
// Attach the mousemove event handler.
canvas.addEventListener('mousemove', ev_mousemove, false);
}
// The mousemove event handler.
var started = false;
function ev_mousemove (ev) {
var x, y;
// Get the mouse position relative to the canvas element.
if (ev.layerX || ev.layerX == 0) { // Firefox
x = ev.layerX;
y = ev.layerY;
} else if (ev.offsetX || ev.offsetX == 0) { // Opera
x = ev.offsetX;
y = ev.offsetY;
}
// The event handler works like a drawing pencil which tracks the mouse
// movements. We start drawing a path made up of lines.
if (!started) {
context.beginPath();
context.moveTo(x, y);
started = true;
} else {
context.lineTo(x, y);
context.stroke();
}
}
init();
}, false); }
The problem is that the cursor stops painting when I scroll until I move my mouse again. Any idea on how to keep cursor painting even when I scroll?
Thanks in advance! Much appreciated!
You will have to store the last mouse event and fire a new fake one in the scroll event.
Luckily, the MouseEvent constructor accepts an mouseEventInit object on which we can set the clientX and clientY values of our new event, so we just need to store these values from the previous event and dispatch it in the scroll event.
Now, I couldn't help but rewrite almost everything from your code.
It had a lot of checks for old browsers (like very old ones which should never face the web again anyway), you may want to add it again if you wish.
It wasn't clearing the context, meaning that every time it drew a new line, it also did draw again the previous lines over themselves, leading in fatter lines, with a lot of noise at the beginning, and smoother lines at the end.
This could be fixed in many ways, the less intrusive one was to just clear the context at each frame.
To get the relative mouse position, it now uses the clientX and clientY properties of the event.
And the rest of the changes is commented in the snippet.
window.addEventListener('load', function () {
const canvas = document.getElementById('imageView');
context = canvas.getContext("2d");
let last_event; // we will store our mouseevents here
// we now listen to the mousemove event on the document,
// not only on the canvas
document.addEventListener('mousemove', ev_mousemove);
document.addEventListener('scroll', fireLastMouseEvent, { capture: true } );
// to get the initial position of the cursor
// even if the mouse never moves
// we listen to a single mouseenter event on the document's root element
// unfortunately this seems to not work in Chrome
document.documentElement.addEventListener( "mouseenter", ev_mousemove, { once: true } );
// called in scroll event
function fireLastMouseEvent() {
if( last_event ) {
// fire a new event on the document using the same clientX and clientY values
document.dispatchEvent( new MouseEvent( "mousemove", last_event ) );
}
}
// mousemove event handler.
function ev_mousemove (ev) {
const previous_evt = last_event || {};
const was_offscreen = previous_evt.offscreen;
// only for "true" mouse event
if( ev.isTrusted ) {
// store the clientX and clientY props in an object
const { clientX, clientY } = ev;
last_event = { clientX, clientY };
}
// get the relative x and y positions from the mouse event
const point = getRelativePointFromEvent( ev, canvas );
// check if we are out of the canvas viewPort
if( point.x < 0 || point.y < 0 || point.x > canvas.width || point.y > canvas.height ) {
// remember we were
last_event.offscreen = true;
// if we were already, don't draw
if( was_offscreen ) { return; }
}
// we come from out-of-screen to in-screen
else if( was_offscreen ) {
// move to the previous point recorded as out-of-screen
const previous_point = getRelativePointFromEvent( previous_evt, canvas );
context.moveTo( previous_point.x, previous_point.y );
}
// add the new point to the context's sub-path definition
context.lineTo( point.x, point.y );
// clear the previous drawings
context.clearRect( 0, 0, canvas.width, canvas.height );
// draw everything again
context.stroke();
}
function getRelativePointFromEvent( ev, elem ) {
// first find the bounding rect of the element
const bbox = elem.getBoundingClientRect();
// subtract the bounding rect from the client coords
const x = ev.clientX - bbox.left;
const y = ev.clientY - bbox.top;
return { x, y };
}
});
#container {
width: 400px;
height: 200px;
overflow: auto;
border: 1px solid;
}
#imageView { border: 1px solid #000; }
canvas {
margin: 100px;
}
<div id="container">
<canvas id="imageView" width="400" height="300"></canvas>
</div>
I would like to measure the length of a line drawn on a image uploaded in html code. I have found a script that use canvas element and enable me to draw a line, rectangle or free line.
The next step I would like to implement is to be able to draw a line, rectangle or free line on a image uploaded but in case of I choose to draw a line to have the possibility to measure the length of a line by setting a scale manually.
I know that in js there is the string string.length but it is not good for my purpose.
Here below there are the two code I have found, I am trying to modify and put together but without any success even because I am not very practical.
If anyone will help me to putting together the two code or give me some advice how to do it I will be very grateful.
This is the html code for draw in a rectangle
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Canvas Paint - Example 5</title>
<style type="text/css">
#container { position: relative; }
#imageView { border: 1px solid #000; }
#imageTemp { position: absolute; top: 1px; left: 1px; }
</style>
</head>
<body>
<p><label>Drawing tool: <select id="dtool">
<option value="line">Line</option>
<option value="rect">Rectangle</option>
<option value="pencil">Pencil</option>
</select></label></p>
<div id="container">
<canvas id="imageView" width="400" height="300">
<p>Unfortunately, your browser is currently unsupported by our web
application. We are sorry for the inconvenience.</p>
</canvas>
</div>
<script type="text/javascript" src="example-5.js"></script>
</body>
</html>
This the relating js code:
// Keep everything in anonymous function, called on window load.
if(window.addEventListener) {
window.addEventListener('load', function () {
var canvas, context, canvaso, contexto;
// The active tool instance.
var tool;
var tool_default = 'line';
function init () {
// Find the canvas element.
canvaso = document.getElementById('imageView');
if (!canvaso) {
alert('Error: I cannot find the canvas element!');
return;
}
if (!canvaso.getContext) {
alert('Error: no canvas.getContext!');
return;
}
// Get the 2D canvas context.
contexto = canvaso.getContext('2d');
if (!contexto) {
alert('Error: failed to getContext!');
return;
}
// Add the temporary canvas.
var container = canvaso.parentNode;
canvas = document.createElement('canvas');
if (!canvas) {
alert('Error: I cannot create a new canvas element!');
return;
}
canvas.id = 'imageTemp';
canvas.width = canvaso.width;
canvas.height = canvaso.height;
container.appendChild(canvas);
context = canvas.getContext('2d');
// Get the tool select input.
var tool_select = document.getElementById('dtool');
if (!tool_select) {
alert('Error: failed to get the dtool element!');
return;
}
tool_select.addEventListener('change', ev_tool_change, false);
// Activate the default tool.
if (tools[tool_default]) {
tool = new tools[tool_default]();
tool_select.value = tool_default;
}
// Attach the mousedown, mousemove and mouseup event listeners.
canvas.addEventListener('mousedown', ev_canvas, false);
canvas.addEventListener('mousemove', ev_canvas, false);
canvas.addEventListener('mouseup', ev_canvas, false);
}
// The general-purpose event handler. This function just determines the mouse
// position relative to the canvas element.
function ev_canvas (ev) {
if (ev.layerX || ev.layerX == 0) { // Firefox
ev._x = ev.layerX;
ev._y = ev.layerY;
} else if (ev.offsetX || ev.offsetX == 0) { // Opera
ev._x = ev.offsetX;
ev._y = ev.offsetY;
}
// Call the event handler of the tool.
var func = tool[ev.type];
if (func) {
func(ev);
}
}
// The event handler for any changes made to the tool selector.
function ev_tool_change (ev) {
if (tools[this.value]) {
tool = new tools[this.value]();
}
}
// This function draws the #imageTemp canvas on top of #imageView, after which
// #imageTemp is cleared. This function is called each time when the user
// completes a drawing operation.
function img_update () {
contexto.drawImage(canvas, 0, 0);
context.clearRect(0, 0, canvas.width, canvas.height);
}
// This object holds the implementation of each drawing tool.
var tools = {};
// The drawing pencil.
tools.pencil = function () {
var tool = this;
this.started = false;
// This is called when you start holding down the mouse button.
// This starts the pencil drawing.
this.mousedown = function (ev) {
context.beginPath();
context.moveTo(ev._x, ev._y);
tool.started = true;
};
// This function is called every time you move the mouse. Obviously, it only
// draws if the tool.started state is set to true (when you are holding down
// the mouse button).
this.mousemove = function (ev) {
if (tool.started) {
context.lineTo(ev._x, ev._y);
context.stroke();
}
};
// This is called when you release the mouse button.
this.mouseup = function (ev) {
if (tool.started) {
tool.mousemove(ev);
tool.started = false;
img_update();
}
};
};
// The rectangle tool.
tools.rect = function () {
var tool = this;
this.started = false;
this.mousedown = function (ev) {
tool.started = true;
tool.x0 = ev._x;
tool.y0 = ev._y;
};
this.mousemove = function (ev) {
if (!tool.started) {
return;
}
var x = Math.min(ev._x, tool.x0),
y = Math.min(ev._y, tool.y0),
w = Math.abs(ev._x - tool.x0),
h = Math.abs(ev._y - tool.y0);
context.clearRect(0, 0, canvas.width, canvas.height);
if (!w || !h) {
return;
}
context.strokeRect(x, y, w, h);
};
this.mouseup = function (ev) {
if (tool.started) {
tool.mousemove(ev);
tool.started = false;
img_update();
}
};
};
// The line tool.
tools.line = function () {
var tool = this;
this.started = false;
this.mousedown = function (ev) {
tool.started = true;
tool.x0 = ev._x;
tool.y0 = ev._y;
};
this.mousemove = function (ev) {
if (!tool.started) {
return;
}
context.clearRect(0, 0, canvas.width, canvas.height);
context.beginPath();
context.moveTo(tool.x0, tool.y0);
context.lineTo(ev._x, ev._y);
context.stroke();
context.closePath();
};
this.mouseup = function (ev) {
if (tool.started) {
tool.mousemove(ev);
tool.started = false;
img_update();
}
};
};
init();
}, false); }
// vim:set spell spl=en fo=wan1croql tw=80 ts=2 sw=2 sts=2 sta et ai cin fenc=utf-8 ff=unix:
While the code for drawing a line and measured the length is at this link:
https://openlayers.org/en/latest/examples/measure.html?q=measure+line
Let's say that canvas in which image is being rendered is of x pixels wide and y pixels height with aspect ratio of x/y, and original dimensions of image is w pixels and h pixels with aspect ratio of w/h.
Now take two points (x1,y1), (x2,y2)
x distance in image units = abs(x1 - x2) * w / x; // now units are in image dimension
y distance in image units = abs(y1- y2) * h / y; // now units are in image dimension
When i say the units are in image dimension
for example an image of 200 meters fitted in 100px width of canvas, now a length of 50 px is 50 * (200/100) meters = 100 meters
so coming for your original question
The length of line between two points is now s = Math.sqrt(Math.pow(x distance in image units, 2) + Math.pow(y distance in image units, 2))
Note: I did not put code as i felt it is better explained in conceptual way,although i feel this question goes better in math.stackexchange
I have 2 canvases:-
<canvas id="myCanvas" width="915" height="650" style="border: 2px double #000000;"></canvas>
<canvas id="pharmacy" width="915" height ="320" style ="border:2px double #000000;"></canvas>
They are separate canvases, on the same aspx page.
The idea is to have them both represent a timeline. i.e they are both scrollable left to right and vice-versa.
I had created the first canvas a while ago, and recently decided to implement the 2nd canvas.
When I copy pasted the original code in the 2nd canvas to emulate the scrolling part, I figured out that in practice, when I scrolled , only the original canvas would scroll and the new canvas doesn't.
If I comment out the code for the original canvas then the new canvas scrolls.
Which led me to think, that the event based scrolling I was trying to achieve has some form of flaw in its naming convention. (since a mouse click is represented as an event and then dragging is allowed; my code was only registering dragging event of the original canvas and not the new canvas).
http://jsfiddle.net/przBL/3/
I would greatly appreciate if someone could take a look at the code and let me know where I am going wrong??
The goal:- is to click on either of the canvases, drag the mouse, and both canvases scroll at the same time.
1st canvas:-
// when mouse is clicked on canvas
window.onmousedown = function (e) {
var evt = e || event;
// dragging is set to true.
dragging = true;
lastX = evt.offsetX;
}
// when mouse is clicked again and the canvas is deselected
window.onmouseup = function () {
// dragging is set to false.
dragging = false;
}
// when mouse is dragging the canvas sideways
can.onmousemove = function (e) {
var evt = e || event;
if (dragging) {
var delta = evt.offsetX - lastX;
translated += delta;
//console.log(translated);
ctx.restore();
ctx.clearRect(0, 0, 930, 900);
ctx.save();
ctx.translate(translated, 0);
lastX = evt.offsetX;
timeline();
}
}
2nd canvas:-
// when mouse is clicked on canvas
window.onmousedown = function (e) {
var evt = e || event;
// dragging is set to true.
dragging = true;
lastX = evt.offsetX;
}
// when mouse is clicked again and the canvas is deselected
window.onmouseup = function () {
// dragging is set to false.
dragging = false;
}
// when mouse is dragging the canvas sideways
can1.onmousemove = function (e) {
var evt = e || event;
if (dragging) {
var delta = evt.offsetX - lastX;
translated += delta;
//console.log(translated);
ctx1.restore();
ctx1.clearRect(0, 0, 915, 600);
ctx1.save();
ctx1.translate(translated, 0);
lastX = evt.offsetX;
pharm_line();
}
}
now I want both can and can1 to move synchronously on any mousedown and mousemove event and stop when mouse is up.
You can use common mouse-handlers to identically scroll both canvas's.
See window.onmousemove below which keeps both canvas's in translated sync.
// when mouse is clicked on canvas
window.onmousedown = function (e) {
var evt = e || event;
// dragging is set to true.
dragging = true;
lastX = evt.offsetX;
}
// when mouse is clicked again and the canvas is deselected
window.onmouseup = function (e) {
// dragging is set to false.
dragging = false;
}
window.onmousemove = function(e) {
var evt = e || event;
if (dragging) {
var delta = evt.offsetX - lastX;
translated += delta;
move(ctx,930,900);
move(ctx1,915,600);
lastX = evt.offsetX;
timeline();
pharm_line();
}
}
// common code used to service either canvas
function move(context,width,height){
context.restore();
context.clearRect(0, 0, width, height);
context.save();
context.translate(translated, 0);
}
So far, it's mobile touch pencil tool, which can draw perfectly, but I'm wondering... how would I make it so that it draws a completely straight line instead of a basic pencil tool which can draw outside the line?
Here's the code so far:
// "Draw Line" Button
$(document).ready(function () {
initialize();
});
// works out the X, Y position of the click inside the canvas from the X, Y position on the page
function getPosition(mouseEvent, sigCanvas) {
var x, y;
if (mouseEvent.pageX != undefined && mouseEvent.pageY != undefined) {
x = mouseEvent.pageX;
y = mouseEvent.pageY;
} else {
x = mouseEvent.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
y = mouseEvent.clientY + document.body.scrollTop + document.documentElement.scrollTop;
}
return { X: x - sigCanvas.offsetLeft, Y: y - sigCanvas.offsetTop };
}
function initialize() {
// get references to the canvas element as well as the 2D drawing context
var sigCanvas = document.getElementById("canvasSignature");
var context = sigCanvas.getContext("2d");
context.strokeStyle = 'Black';
// This will be defined on a TOUCH device such as iPad or Android, etc.
var is_touch_device = 'ontouchstart' in document.documentElement;
if (is_touch_device) {
// create a drawer which tracks touch movements
var drawer = {
isDrawing: false,
touchstart: function (coors) {
context.beginPath();
context.moveTo(coors.x, coors.y);
this.isDrawing = true;
},
touchmove: function (coors) {
if (this.isDrawing) {
context.lineTo(coors.x, coors.y);
context.stroke();
}
},
touchend: function (coors) {
if (this.isDrawing) {
this.touchmove(coors);
this.isDrawing = false;
}
}
};
// create a function to pass touch events and coordinates to drawer
function draw(event) {
// get the touch coordinates. Using the first touch in case of multi-touch
var coors = {
x: event.targetTouches[0].pageX,
y: event.targetTouches[0].pageY
};
// Now we need to get the offset of the canvas location
var obj = sigCanvas;
if (obj.offsetParent) {
// Every time we find a new object, we add its offsetLeft and offsetTop to curleft and curtop.
do {
coors.x -= obj.offsetLeft;
coors.y -= obj.offsetTop;
}
// The while loop can be "while (obj = obj.offsetParent)" only, which does return null
// when null is passed back, but that creates a warning in some editors (i.e. VS2010).
while ((obj = obj.offsetParent) != null);
}
// pass the coordinates to the appropriate handler
drawer[event.type](coors);
}
// attach the touchstart, touchmove, touchend event listeners.
sigCanvas.addEventListener('touchstart', draw, false);
sigCanvas.addEventListener('touchmove', draw, false);
sigCanvas.addEventListener('touchend', draw, false);
// prevent elastic scrolling
sigCanvas.addEventListener('touchmove', function (event) {
event.preventDefault();
}, false);
}
else {
// start drawing when the mousedown event fires, and attach handlers to
// draw a line to wherever the mouse moves to
$("#canvasSignature").mousedown(function (mouseEvent) {
var position = getPosition(mouseEvent, sigCanvas);
context.moveTo(position.X, position.Y);
context.beginPath();
// attach event handlers
$(this).mousemove(function (mouseEvent) {
drawLine(mouseEvent, sigCanvas, context);
}).mouseup(function (mouseEvent) {
finishDrawing(mouseEvent, sigCanvas, context);
}).mouseout(function (mouseEvent) {
finishDrawing(mouseEvent, sigCanvas, context);
});
});
}
}
// draws a line to the x and y coordinates of the mouse event inside
// the specified element using the specified context
function drawLine(mouseEvent, sigCanvas, context) {
var position = getPosition(mouseEvent, sigCanvas);
context.lineTo(position.X, position.Y);
context.stroke();
}
// draws a line from the last coordiantes in the path to the finishing
// coordinates and unbind any event handlers which need to be preceded
// by the mouse down event
function finishDrawing(mouseEvent, sigCanvas, context) {
// draw the line to the finishing coordinates
drawLine(mouseEvent, sigCanvas, context);
context.closePath();
// unbind any events which could draw
$(sigCanvas).unbind("mousemove")
.unbind("mouseup")
.unbind("mouseout");
}
Thanks,
Wardenclyffe
Anchor the pen down point at first touch and then wherever the next touch is draw a straight line from the first touch to the new touch. Have a floating check mark above the new touch to accept the new line. If the check mark isn't selected and there is another touch then remove the previous line, draw a new line, and show another check mark to save the new line. Provide an (x) above the original pen down point to cancel the line drawing tool.
The difference between a straight line tool and a pencil tool is that the pencil draws as many points as possible, and connects them with (possibly smoothed) line segments. A straight line tool connects a small number of lines with relatively large line segments.
One option would be for the pencil tool to lay down points as long as the finger touches the screen. And then the line tool draws from tap to tap. Alternately, if you tap and slide, the line tool could rubber-band the line from the touch point to the drag point, and then keep the last point before the touch lifts.
I have this html5 drawing app that draws just fine on the canvas element. My problem is, I have an img of an eraser and I want the user to be able to click it and it will erase the canvas. Extra karma points if you can tell me also how to change the stroke color to white.
This is my html:
<div id="draw_area">
<canvas id="myCanvas" />
<p>browser sucks, here's links blah blah blah</p>
</canvas>
</div>
This is the JS to complement it:
var points = new Array();
var outlineImage = new Image();
function clearCanvas(){
context.clearRect(0, 0, canvas.width, canvas.height);
}
if (window.addEventListener) {
window.addEventListener('load', function () {
var canvas, context, tool;
function init() {
// Find the canvas element.
canvas = document.getElementById('imageView');
if (!canvas) {
alert('Error: I cannot find the canvas element!');
return;
}
if (!canvas.getContext) {
alert('Error: no canvas.getContext!');
return;
}
// Size the canvas:
canvas.width = 367;
canvas.height= 249;
// Get the 2D canvas context.
context = canvas.getContext('2d');
if (!context) {
alert('Error: failed to getContext!');
return;
}
// Pencil tool instance.
tool = new tool_pencil();
// Attach the mousedown, mousemove and mouseup event listeners.
canvas.addEventListener('mousedown', ev_canvas, false);
canvas.addEventListener('mousemove', ev_canvas, false);
canvas.addEventListener('mouseup', ev_canvas, false);
}
// This painting tool works like a drawing pencil which tracks the mouse
// movements.
function tool_pencil() {
var tool = this;
this.started = false;
// This is called when you start holding down the mouse button.
// This starts the pencil drawing.
this.mousedown = function (ev) {
context.beginPath();
context.moveTo(ev._x, ev._y);
tool.started = true;
};
// This function is called every time you move the mouse. Obviously, it only
// draws if the tool.started state is set to true (when you are holding down
// the mouse button).
this.mousemove = function (ev) {
if (tool.started) {
context.lineTo(ev._x, ev._y);
context.stroke();
}
};
// This is called when you release the mouse button.
this.mouseup = function (ev) {
if (tool.started) {
tool.mousemove(ev);
tool.started = false;
}
};
}
// The general-purpose event handler. This function just determines the mouse
// position relative to the canvas element.
function ev_canvas(ev) {
if (navigator.appName == 'Microsoft Internet Explorer' || navigator.vendor == 'Google Inc.' || navigator.vendor == 'Apple Computer, Inc.') { // IE or Chrome
ev._x = ev.offsetX;
ev._y = ev.offsetY;
} else if (ev.layerX || ev.layerX == 0) { // Firefox
ev._x = ev.layerX - this.offsetLeft;
ev._y = ev.layerY - this.offsetTop;
} else if (ev.offsetX || ev.offsetX == 0) { // Opera
ev._x = ev.offsetX;
ev._y = ev.offsetY;
}
// Call the event handler of the tool.
var func = tool[ev.type];
if (func) {
func(ev);
}
points.push(ev);
}
init();
}, false);
}
I think I need a redraw function, but I don't really know what I'm talking about in regards to this issue. Any insight is much appreciated!
Assuming your background color is white, which is what clearRect will give you, then all you need to do is change the stroke color to white when the user selects the eraser. This can easily be done with
context.strokeStyle = 'white';
See http://www.w3.org/TR/2dcontext/#fill-and-stroke-styles for more information about changing colors.
If your eraser image is drawn on the canvas, then you will have to detect the clicking of it in your mousedown or mouseup event handlers. If it is outside the canvas, then just add a onclick function to set the strokeStyle when it is clicked. However, if you want the eraser to erase everything then do
function clearCanvas(){
context.clearRect(0, 0, context.canvas.width, context.canvas.height);
}
Your canvas variable was not available in the scope where clearCanvas is called. While there are other ways to fix this, simply do this:
function clearContext( ctx ){
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
}
and pass the context into your function when you call it.
However note that if you context is transformed in any way the above will not clear the entire visible region. To safeguard against this, you may want:
function clearContext( ctx ){
ctx.save();
ctx.setTransform(1,0,0,1,0,0);
ctx.clearRect(0,0,ctx.canvas.width,ctx.canvas.height);
ctx.restore();
}