I am creating an app which allows user to draw a digit(using mouse) in Canvas. When they click on submit, the image should be saved as a JPEG and Clear should Erase the digit drawn in canvas. I am new to HTML5 Canvas. I saw some tutorials which created a canvas, but I am unable to save and clear canvas. Any help would really be appreciated. Thanks.
Here is my HTML:
<div style="padding-bottom: 20px;padding-left: 210px;">
<div class="btn-group" role="group" aria-label="...">
<button type="button" id="save" class="btn btn-default">Submit</button>
<button type="button" id="clear" class="btn btn-default">Clear</button>
</div></div>
</div>
Javascript within Script tag:
var clear = document.getElementById('clear');
clear.addEventListener('click', clearCanvas);
var clearCanvas = function(e) {
ctx.fillStyle = "white";
ctx.fillRect(0, 0, 500, 400);
ctx.clearRect(20, 20, 100, 50);
}
The tutorial you're using fails to mention a few key things:
The canvasInAPerfectWorldline isn't used: you can delete it.
You need jQuery: just add <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.js"></script/> in your <head> section.
You should wrap your javascript in the jQuery boilerplate, to ensure it gets executed after the rest to the document has loaded: $( document ).ready(function() { <your javascript here> });
The canvasWidth and canvasHeight variables are never set: add this line before the variables are used:var canvasWidth = 600; var cavasHeight = 400;
The variables mouseX and mouseY are never used: you can delete them (but take a look at the onClick() function two lines later to see what they would have been used for).
The finished result should look like this:
<!DOCTYPE html>
<html>
<head>
<title>HTML5 Create HTML5 Canvas JavaScript Drawing App Example</title>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.js"></script>
<style>canvas { border: 1px solid #ccc; }</style>
<script type="text/javascript">
$( document ).ready(function() {
var canvasWidth = 600; var cavasHeight = 400;
var canvasDiv = document.getElementById('canvasDiv');
canvas = document.createElement('canvas');
canvas.setAttribute('width', canvasWidth);
canvas.setAttribute('height', cavasHeight);
canvas.setAttribute('id', 'canvas');
canvasDiv.appendChild(canvas);
if(typeof G_vmlCanvasManager != 'undefined') {
canvas = G_vmlCanvasManager.initElement(canvas);
}
context = canvas.getContext("2d");
$('#canvas').mousedown(function(e){
paint = true;
addClick(e.pageX - this.offsetLeft, e.pageY - this.offsetTop);
redraw();
});
$('#canvas').mousemove(function(e){
if(paint){
addClick(e.pageX - this.offsetLeft, e.pageY - this.offsetTop, true);
redraw();
}
});
$('#canvas').mouseup(function(e){
paint = false;
});
$('#canvas').mouseleave(function(e){
paint = false;
});
$('#clear').click(function(){
clickX.length = 0;
clickY.length = 0;
clickDrag.length = 0;
redraw();
});
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);
}
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();
}
}
});
</script>
</head>
<body>
<div id="canvasDiv"></div>
</body>
</html>
As for saving your canvas as a JPEG, that's more complicated. As markE has pointed out, there's a related question here which shows one way to do it.
EDIT: Adding a 'Clear' button:
Your 'drawing' is really just a series of pen movements stored in the clickX, clickY and clickDrag arrays. Each time redraw() is called, the drawing gets recreated from the arrays. So just drawing a white rectangle will only clear the screen until the next redraw(). Instead, you can clear the arrays by adding this to your javascript:
$('#clear').click(function(){
clickX.length = 0;
clickY.length = 0;
clickDrag.length = 0;
redraw();
});
(BTW by deleting the details of your original question, you make it hard for the next person to understand the context of the answers. You should either (a) add to your original question, or (b) ask a new question. Also you can upvote and/or accept any answers you find helpful. Thanks)
I think that's a duplicated question. See here
Can I get image from canvas element and use it in img src tag?
var image = new Image();
image.id = "pic"
image.src = canvas.toDataURL();
document.getElementById('image_for_crop').appendChild(image);
Related
I have a HTML canvas in Ionic app.
<canvas id="canvas" color="{{ color }}" width="800" height="600" style="position:relative;"></canvas>
In this canvas, I am loading an image. Below is the code from controller
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
var img = new Image();
img.src = $stateParams.imageId;
img.onload = function() {
context.drawImage(img, 0, 0, canvas.width, canvas.height);
}
After the image is loaded, users need the ability to write on the image and circle/highlight certain areas of the image.
Doesn't HTML canvas provide this feature by default? Right now I am not able to annotate anything on the image. How can I achieve this?
You need to implement this yourself.
You can do it by hooking into the mouse click / move events. using your 2d context, draw small rectangle at the mouse's current position if the most moves and the left mouse button is down.
The effect is similar to a Windows paint pencil tool. Here's a simple example.
<html>
<head>
<style>
canvas{
border: 1px solid black;
}
</style>
</head>
<body>
<canvas id="myCanvas"></canvas>
<script>
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
var isMouseDown = false;
canvas.onmousedown = function(e){
isMouseDown = true;
}
canvas.onmouseup = function(e){
isMouseDown = false;
}
canvas.onmousemove = function(e){
if(isMouseDown === false){
return;
}
var canvasPosition = canvas.getBoundingClientRect();
var x = e.clientX - canvasPosition.left;
var y = e.clientY - canvasPosition.top;
ctx.fillRect(x, y, 2, 2);
};
</script>
</body>
</html>
I am trying to create a pannable image viewer which also allows magnification. If the zoom factor or the image size is such that the image no longer paints over the entire canvas then I wish to have the area of the canvas which does not contain the image painted with a specified background color.
My current implementation allows for zooming and panning but with the unwanted effect that the image leaves a tiled trail after it during a pan operation (much like the cards in windows Solitaire when you win a game). How do I clean up my canvas such that the image does not leave a trail and my background rectangle properly renders in my canvas?
To recreate the unwanted effect set magnification to some level at which you see the dark gray background show and then pan the image with the mouse (mouse down and drag).
Code snippet added below and Plnkr link for those who wish to muck about there.
http://plnkr.co/edit/Cl4T4d13AgPpaDFzhsq1
<!DOCTYPE html>
<html>
<head>
<style>
canvas{
border:solid 5px #333;
}
</style>
</head>
<body>
<button onclick="changeScale(0.10)">+</button>
<button onclick="changeScale(-0.10)">-</button>
<div id="container">
<canvas width="700" height="500" id ="canvas1"></canvas>
</div>
<script>
var canvas = document.getElementById('canvas1');
var context = canvas.getContext("2d");
var imageDimensions ={width:0,height:0};
var photo = new Image();
var isDown = false;
var startCoords = [];
var last = [0, 0];
var windowWidth = canvas.width;
var windowHeight = canvas.height;
var scale=1;
photo.addEventListener('load', eventPhotoLoaded , false);
photo.src = "http://www.html5rocks.com/static/images/cors_server_flowchart.png";
function eventPhotoLoaded(e) {
imageDimensions.width = photo.width;
imageDimensions.height = photo.height;
drawScreen();
}
function changeScale(delta){
scale += delta;
drawScreen();
}
function drawScreen(){
context.fillRect(0,0, windowWidth, windowHeight);
context.fillStyle="#333333";
context.drawImage(photo,0,0,imageDimensions.width*scale,imageDimensions.height*scale);
}
canvas.onmousedown = function(e) {
isDown = true;
startCoords = [
e.offsetX - last[0],
e.offsetY - last[1]
];
};
canvas.onmouseup = function(e) {
isDown = false;
last = [
e.offsetX - startCoords[0], // set last coordinates
e.offsetY - startCoords[1]
];
};
canvas.onmousemove = function(e)
{
if(!isDown) return;
var x = e.offsetX;
var y = e.offsetY;
context.setTransform(1, 0, 0, 1,
x - startCoords[0], y - startCoords[1]);
drawScreen();
}
</script>
</body>
</html>
You need to reset the transform.
Add context.setTransform(1,0,0,1,0,0); just before you clear the canvas and that will fix your problem. It sets the current transform to the default value. Then befor the image is draw set the transform for the image.
UPDATE:
When interacting with user input such as mouse or touch events it should be handled independently of rendering. The rendering will fire only once per frame and make visual changes for any mouse changes that happened during the previous refresh interval. No rendering is done if not needed.
Dont use save and restore if you don't need to.
var canvas = document.getElementById('canvas1');
var ctx = canvas.getContext("2d");
var photo = new Image();
var mouse = {}
mouse.lastY = mouse.lastX = mouse.y = mouse.x = 0;
mouse.down = false;
var changed = true;
var scale = 1;
var imageX = 0;
var imageY = 0;
photo.src = "http://www.html5rocks.com/static/images/cors_server_flowchart.png";
function changeScale(delta){
scale += delta;
changed = true;
}
// Turns mouse button of when moving out to prevent mouse button locking if you have other mouse event handlers.
function mouseEvents(event){ // do it all in one function
if(event.type === "mouseup" || event.type === "mouseout"){
mouse.down = false;
changed = true;
}else
if(event.type === "mousedown"){
mouse.down = true;
}
mouse.x = event.offsetX;
mouse.y = event.offsetY;
if(mouse.down) {
changed = true;
}
}
canvas.addEventListener("mousemove",mouseEvents);
canvas.addEventListener("mouseup",mouseEvents);
canvas.addEventListener("mouseout",mouseEvents);
canvas.addEventListener("mousedown",mouseEvents);
function update(){
requestAnimationFrame(update);
if(photo.complete && changed){
ctx.setTransform(1,0,0,1,0,0);
ctx.fillStyle="#333";
ctx.fillRect(0,0, canvas.width, canvas.height);
if(mouse.down){
imageX += mouse.x - mouse.lastX;
imageY += mouse.y - mouse.lastY;
}
ctx.setTransform(scale, 0, 0, scale, imageX,imageY);
ctx.drawImage(photo,0,0);
changed = false;
}
mouse.lastX = mouse.x
mouse.lastY = mouse.y
}
requestAnimationFrame(update);
canvas{
border:solid 5px #333;
}
<button onclick="changeScale(0.10)">+</button><button onclick="changeScale(-0.10)">-</button>
<canvas width="700" height="500" id ="canvas1"></canvas>
Nice Code ;)
You are seeing the 'tiled' effect in your demonstration because you are painting the scaled image to the canvas on top of itself each time the drawScreen() function is called while dragging. You can rectify this in two simple steps.
First, you need to clear the canvas between calls to drawScreen() and second, you need to use the canvas context.save() and context.restore() methods to cleanly reset the canvas transform matrix between calls to drawScreen().
Given your code as is stands:
Create a function to clear the canvas. e.g.
function clearCanvas() {
context.clearRect(0, 0, canvas.width, canvas.height);
}
In the canavs.onmousemove() function, call clearCanvas() and invoke context.save() before redefining the transform matrix...
canvas.onmousemove = function(e) {
if(!isDown) return;
var x = e.offsetX;
var y = e.offsetY;
/* !!! */
clearCanvas();
context.save();
context.setTransform(
1, 0, 0, 1,
x - startCoords[0], y - startCoords[1]
);
drawScreen();
}
... then conditionally invoke context.restore() at the end of drawScreen() ...
function drawScreen() {
context.fillRect(0,0, windowWidth, windowHeight);
context.fillStyle="#333333";
context.drawImage(photo,0,0,imageDimensions.width*scale,imageDimensions.height*scale);
/* !!! */
if (isDown) context.restore();
}
Additionally, you may want to call clearCanvas() before rescaling the image, and the canvas background could be styled with CSS rather than .fillRect() (in drawScreen()) - which could give a performance gain on low spec devices.
Edited in light of comments from Blindman67 below
See Also
Canvas.context.save : https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/save
Canvas.context.restore : https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/restore
requestAnimationFrame : https://developer.mozilla.org/en-US/docs/Web/API/Window/requestAnimationFrame
Paul Irish, requestAnimationFrame polyfill : http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/
Call context.save to save the transformation matrix before you call context.fillRect.
Then whenever you need to draw your image, call context.restore to restore the matrix.
For example:
function drawScreen(){
context.save();
context.fillStyle="#333333";
context.fillRect(0,0, windowWidth, windowHeight);
context.restore();
context.drawImage(photo,0,0,imageDimensions.width*scale,imageDimensions.height*scale);
}
Also, to further optimize, you only need to set fillStyle once until you change the size of canvas.
I want to make a loading bar for my web application and I want to use a html canvas for this. This is the script that I use to fill up the canvas:
<script>
var canvas = document.getElementById("bar");
var c = canvas.getContext("2d");
var xPos = 0;
draw = function() {
if(xPos < 300){
c.rect(0, 0, xPos, 30);
c.fill(255,0,0);
xPos += 0.5;
}
};
</script>
I tested this code on a online code converter (khan academy) and it worked (of course without the first 2 lines and c. in front of most things), and that is also my trouble I don't know where I have to put c. in front of?
I simplified the page a little bit:
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" type="text/css" href="test.css">
</head>
<body>
<canvas id="bar"></canvas>
<script>
var canvas = document.getElementById("bar");
var c = canvas.getContext("2d");
c.fillStyle = "#ff0000"
draw = function(){
if(xPos < 300){
c.fillRect(0, 0, xPos, 30);
xPos += 0.5;
}
};
</script>
</body>
</html>
Whatever you are trying to draw... this:
draw = function(){
if(xPos < 300) {
c.fillRect(0, 0, xPos, 30);
xPos += 0.5;
}
};
... it is a definition of variable in global context (context of window object), then assigning a function to it. That's all - it only defines the behavior.
What you need also needs to execute that (a sidenote: to execute it after the canvas is actually created - when you put code in a script tag after canvas tag - it's sufficient and you did it already).
To execute the function use:
draw();
Or don't wrap code in function at all (unless it's to be called multiple times).
Or use a syntax construct to execute the function created in place like this:
(draw = function(){
if(xPos < 300) {
c.fillRect(0, 0, xPos, 30);
xPos += 0.5;
setTimeout(draw,15); // use this to achieve animation effect
}
})();
var xPos = 0;
var canvas = document.getElementById("bar");
var c = canvas.getContext("2d");
c.fillStyle = "#FF0000";
var draw;
(draw = function(){
if(xPos < 300) {
c.fillRect(0, 0, xPos, 30);
xPos += 0.5;
setTimeout(draw,15);
}
})();
#bar {
width: 300px;
height: 50px;
}
<canvas id="bar"></canvas>
Edit: I've been thinking of what you might need, as it's not entirely abvious what you want. I have created this jsfiddle. Maybe it'll be of any help.
Hmmm...
You got some things mixed up. Try this:
<html>
<canvas id = "cvs1" width = "300" height = "30"></canvas>
</html>
And for the script:
var c = document.getElementById("cvs1").getContext("2d");
c.fillStyle = "#ff0000" //Set Fill Color(Set to red)
if(xPos < 300){
c.fillRect(xPos, 0, 30, 30);
xPos += 0.5;
}
If not:
What you did was use fill and rect seperately. You need to set the color, and then use the fillRect() function to draw the rectangle.
EDIT: You got the x,y,width,height as width,height,x,y. Fixed answer.
Good luck!
You need to call draw for every animation step. You could do this using setTimeout, setInterval or requestAnimationFrame :
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" type="text/css" href="test.css">
</head>
<body>
<canvas id="bar"></canvas>
<script>
var canvas = document.getElementById("bar");
var c = canvas.getContext("2d");
c.fillStyle = "#ff0000";
xPos=0;
draw = function(){
if(xPos < 300){
c.fillRect(0, 0, xPos, 30);
xPos += 0.5;
requestAnimationFrame(draw);
}
};
requestAnimationFrame(draw);
</script>
</body>
</html>
I found this helpful tutorial
http://www.rgraph.net/blog/2013/february/an-example-of-the-html5-canvas-ispointinpath-function.html
i copied it into my own text editor and nothing happens when I open it. I changed it by adding a declaration
<!DOCTYPE html>
<html lang="en">
<head>
<title>exampleMouseOver</title>
</head>
<script>
window.onload = function (e)
{
var canvas = document.getElementById('cvs');
var context = canvas.getContext('2d');
// Draw the rectangle
context.beginPath();
context.rect(50,50,100,100);
context.fill();
context.fillStyle = 'red';
// Draw the circle
context.beginPath();
context.arc(450,175, 50, 0,2 * Math.PI, false);
context.fill();
context.fillStyle = 'green';
// Draw the shape
context.beginPath();
context.moveTo(250,100);
context.lineTo(350,175);
context.lineTo(325,215);
context.lineTo(185,195);
context.fill();
canvas.onmousemove = function (e)
{
var canvas = e.target;
var context = canvas.getContext('2d');
// This gets the mouse coordinates (relative to the canvas)
var mouseXY = RGraph.getMouseXY(e);
var mouseX = mouseXY[0];
var mouseY = mouseXY[1];
// Replay the rectangle path (no need to fill() it) and test it
context.beginPath();
context.rect(50,50,100,100);
if (context.isPointInPath(mouseX, mouseY)) {
canvas.style.cursor = 'pointer';
return;
}
///////////////////////////////////////////////////////////////
// Replay the circle path (no need to fill() it) and test it
context.beginPath();
context.arc(450,175, 50, 0,2 * Math.PI, false);
if (context.isPointInPath(mouseX, mouseY)) {
canvas.style.cursor = 'pointer';
return;
}
///////////////////////////////////////////////////////////////
// Replay the irregular shape path (no need to fill() it) and test it
context.beginPath();
context.moveTo(250,100);
context.lineTo(350,175);
context.lineTo(325,215);
context.lineTo(185,195);
if (context.isPointInPath(mouseX, mouseY)) {
canvas.style.cursor = 'pointer';
return;
}
///////////////////////////////////////////////////////////////
// Return the cursor to the default style
canvas.style.cursor = 'default';
}
}
</script>
</html>
You'll need a body element and a canvas element. Also your script element needs to be inside either your head element or your body element.
The following is what the sample was using, but did not include in their sample code:
<body>
<canvas id="cvs" width="600" height="250" style="border: 1px solid gray; cursor: pointer;">[No canvas support]</canvas>
</body>
Edit: Additionally the code is calling "RGraph.getMouseXY(e)", which is in a library file that you are not referencing. You can either add a reference to that library or get the mouse position yourself.
If you want to use other parts of the RGraph library, for drawing charts, you should add the library. To add the library you should follow the instructions on the RGraph site related to downloading and starting with RGraph (http://www.rgraph.net/docs/starting-with-rgraph.html).
If this was just a sample that happened to do what you wanted to do, you should get the mouse position yourself. You can do this by changing these lines:
var mouseXY = RGraph.getMouseXY(e);
var mouseX = mouseXY[0];
var mouseY = mouseXY[1];
to these:
var mouseX = e.clientX - canvas.getBoundingClientRect().left;
var mouseY = e.clientY - canvas.getBoundingClientRect().top;
This may not be the most robust solution, but it should suffice for your purposes. Essentially you are getting the mouse position in the window, then subtracting the top-left of the canvas in the window, so that you are left with the mouse position in the canvas.
I have a HTML5 canvas of a certain size on the page
<canvas id="canvas" width="200" height="100" style="border:1px solid #000000;">
Right now, the canvas is painted when the mouse is dragged over it (i.e. you click first(not necessarily inside the canvas) then start dragging the mouse over it without releasing) Script below;
$(function () {
var c = document.getElementById("canvas");
var context = c.getContext("2d");
var clickX = new Array();
var clickY = new Array();
var clickDrag = new Array();
var paint;
var $canvas=$("#canvas");
$(this).mousedown(function (e) {
paint = true;
addClick(e.pageX - $canvas[0].offsetLeft, e.pageY - $canvas[0].offsetTop);
redraw();
});
$(this).mousemove(function (e) {
if (paint) {
addClick(e.pageX - $canvas[0].offsetLeft, e.pageY - $canvas[0].offsetTop, true);
redraw();
}
});
$(this).mouseup(function (e) {
paint = false;
});
$(this).mouseleave(function (e) {
paint = false;
});
function addClick(x, y, dragging) {
clickX.push(x);
clickY.push(y);
clickDrag.push(dragging);
}
function redraw() {
context.clearRect(0, 0, context.canvas.width, context.canvas.height); // Clears the canvas
context.strokeStyle = "#000000";
context.lineJoin = "round";
context.lineWidth = 2;
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();
}
}
});
What I need now is a way to scale all the mouse coordinates on document during the document's mouse events to the relative coordinates inside the canvas.
so that no matter wherever you drag the mouse on the document it is drawn inside the canvas (in relatively small size of course). Any Idea how to achieve this?
http://jsfiddle.net/umwc5/3/
Why I need this?
It is for a signature application, When a user scribbles the signature using a tablet on a page(without seeing the page!) the entire signature is to be registered in a small canvas.
Update
The final working fiddle
The most important thing you were missing here was to multiply by the canvas/screen ratio.
First calculate the ratio:
var docToCanv = Math.min($canvas[0].width / $('body').width(), $canvas[0].height/$('body').height());
Then use it like this:
addClick(e.pageX*docToCanv, e.pageY*docToCanv);
Depending on the additional behavior you want, you may need to adjust the location a bit, but this should get you past the current issue you are having.
Demo