HTML5 Canvas stroke not following mouse Y point - javascript

I'm writing a drawing app in HTML5 Canvas, which includes a freehand draw with the mouse.
I have a problem whereby the mouse move to draw the stroke is not under the crosshair cursor. The X co-ordinate is fine, but the Y coordinate is offset by a varying amount as the mouse pointer is moved (closer at the top of the page, drifts further away as we closer to the bottom).
It has something to do with the 'header bar' div at the top.
Here is the code.
<style>
#divContainer {
width: 100%;
height: 100%;
}
#divHeader {
position: absolute;
left: 0px;
top: 0px;
right: 0px;
height: 28px;
background-color: #333;
}
#divContentArea {
position: absolute;
left: 0px;
top: 29px;
right: 0px;
bottom: 5px;
}
#divContentCenter {
position: absolute;
top: 0px;
left: 0px;
bottom: 0px;
right:0px;
}
.canvascontainer {
position: relative;
overflow: auto;
width:100%;
height:100%;
}
.canvas {
cursor: crosshair;
width: 100%;
height: 100%;
position:absolute;
left:0px;
top:0px;
z-index: 2;
}
.maincanvas {
cursor: crosshair;
width: 100%;
height: 100%;
position:absolute;
left:0px;
top:0px;
z-index: 1;
}
</style>
<div id="divContainer">
<div id="divHeader">
The Header
</div>
<div id="divContentArea">
<div id="divContentCenter">
<div id='canvascontainer' class='canvascontainer' >
<canvas id="canvas" class='canvas'>
Sorry, your browser does not support a canvas object.
</canvas>
<canvas id="maincanvas" class='maincanvas'>
Sorry, your browser does not support a canvas object.
</canvas>
</div>
</div>
</div>
</div>
<script>
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
const canrect = canvas.getBoundingClientRect();
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var maincanvas = document.getElementById('maincanvas');
var maincontext = maincanvas.getContext('2d');
maincanvas.width = window.innerWidth;
maincanvas.height = window.innerHeight;
var lastPoint;
var startPoint;
var isDrawing = false;
context.lineWidth = 3;
context.lineJoin = context.lineCap = 'round';
context.setLineDash([0, 0]);
context.globalAlpha = 1.0;
function drawGuideLines() {
for ( i = 0; i < canvas.height; i += 20 ) {
context.beginPath();
context.setLineDash([2, 2]);
context.lineWidth = 1;
if ( i % 60 == 0 ) {
context.lineWidth = 2;
}
context.strokeStyle = '#ccc';
context.moveTo(0,i);
context.lineTo(canvas.width,i);
context.stroke();
}
for ( i = 0; i < canvas.width; i += 20 ) {
context.beginPath();
context.setLineDash([2, 2]);
context.lineWidth = 1;
if ( i % 60 == 0 ) {
context.lineWidth = 2;
}
context.strokeStyle = '#ccc';
context.moveTo(i,0);
context.lineTo(i, canvas.height);
context.stroke();
}
}
function getMousePos(e) {
return {
x: e.offsetX - canrect.left,
y: e.offsetY + canrect.top
};
}
function clearPage() {
context.clearRect(0, 0, canvas.width, canvas.height);
}
function copyToMain () {
maincontext.drawImage(canvas, 0, 0);
clearPage();
}
canvas.onmousedown = function(e) {
isDrawing = true;
canvas.addEventListener("mousemove", drawDirectPath, false);
lastPoint = { x: e.clientX, y: e.clientY };
lastPoint = { x: e.offsetX, y: e.offsetY };
// lastPoint = { x: e.offsetX, y: e.PageY };
lastPoint = getMousePos(e);
};
function drawDirectPath(e) {
if (!isDrawing) return;
context.beginPath();
context.setLineDash([0, 0]);
context.lineWidth = 3;
context.strokeStyle = 'red';
context.fillStyle = 'red';
//show_mouse_info(e, 'GetMousePos:' + getMousePos(e).x + ', ' + getMousePos(e).y);
//show_mouse_info(e, 'boundrect:' + canrect.x + ', ' + canrect.y);
//mx = e.clientX;
//my = e.clientY;
mx = e.offsetX;
my = e.offsetY;
context.moveTo(lastPoint.x, lastPoint.y);
context.lineTo(mx, my);
context.stroke();
lastPoint = { x: mx, y: my };
}
canvas.onmouseup = function() {
isDrawing = false;
copyToMain ();
};
canvas.onmouseleave = function() {
isDrawing = false;
copyToMain ();
};
drawGuideLines();
</script>
I have tried using OffsetX/Y, PageX/Y, clinetX/Y to see if these make a difference but I cannot solve the problem.
The test, click the mouse in the top right or top left and drag/draw down diagonally to the opposite bottom corner to see the effect.

Don't give the canvas a width and height of 100% using CSS.
.canvas {
cursor: crosshair;
position:absolute;
left:0px;
top:0px;
z-index: 2;
}
Couple other things you may want to consider
Always declare variables for (let i =...
Don't make getBoundingClientRect() a const. The reason for this is if you needed to add a resize function you wouldn't be able to change the bounds because the variable holds the original bounds.
You are assigning lastPoint over and over. Not really sure what this is about.
lastPoint = { x: e.clientX, y: e.clientY };
lastPoint = { x: e.offsetX, y: e.offsetY };
// lastPoint = { x: e.offsetX, y: e.PageY };
lastPoint = getMousePos(e);

Related

Scrolling issue with Canvas style.left/top

I am making a magnifying glass for a canvas application but ive run into an issue with scrolling.
Essentially the goal is to take a canvas and when an image is added (in this case a cgm image) and make a snapshot of it, scale it up and draw it on to a second smaller canvas that overlays it and follow the mouse (preferably a drag/mousedown and up, but i am using mousemove for testing purposes). I believe the issue is in the zoom.style.top & zoom.style.left NOTE on my main aplication I have a top margin of 70px so keep that in mind.
here is a quick example I wrote up
<!-- language: lang-js -->
var canvas = document.getElementById("canvas1");
var ctx = canvas.getContext("2d");
var ox = canvas.width / 2;
var oy = canvas.height / 2;
ctx.font = "42px serif";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillStyle = "blue";
ctx.fillRect(ox / 2, oy / 2, ox, oy);
function magnify() {
var main = document.getElementById("canvas1");
var ctx = main.getContext('2d')
var base64 = main.toDataURL('image/png', 0);
drawing = new Image();
drawing.onload = () => {
var zoom = document.getElementById("tCanvas");
var zoomCtx = zoom.getContext('2d');
zoomCtx.drawImage(drawing, 0, 0);
}
main.addEventListener("mousemove", function(e) {
var zoom = document.getElementById("tCanvas");
var zoomCtx = zoom.getContext('2d');
zoomCtx.clearRect(0, 0, zoom.width, zoom.height);
zoomCtx.drawImage(main, e.x, e.y, 200, 200, 0, 0, 300, 300);
zoom.style.top = e.pageY - 70 + "px"
zoom.style.left = e.pageX - 10 + "px"
e.pageY = -150
e.pageX = -150
zoom.style.display = "block";
});
main.addEventListener("mouseleave", function() {
var zoom = document.getElementById("tCanvas");
zoom.style.display = "none";
});
drawing.src = base64;
};
<canvas id="tCanvas" class="cgm" height="100" width="100" style="background-color:white; position: absolute; display: none; z-
index:1;border:1px solid red;"> </canvas>
<canvas tabindex=1 class="cgm" id="canvas1" style="position:relative; background:white;
left:0;right:0;margin:auto;z-index:1;margin-top:70px; "></canvas>
<p></p>
<button id="zoom" onclick="magnify();">Zoom</button>
Here's a fiddle for reference (I fixed the height to display the scroll issue).
JSFiddle
I simplified a lot of the code on your JSFiddle.
See if this is the behavior you are looking for:
const canvas = document.getElementById("canvas1");
const ctx = canvas.getContext("2d");
const zoom = document.getElementById("tCanvas");
const zoomCtx = zoom.getContext('2d');
for (let i = 10; i < 20; i++)
for (let j = -60; j < 800; j += 60)
ctx.fillText(j + i, i * 20 - 180, i * 9 + j);
function getMousePos(evt) {
var rect = canvas.getBoundingClientRect()
return {
x: evt.clientX - rect.left,
y: evt.clientY - rect.top
}
}
canvas.addEventListener("mousemove", function(e) {
zoomCtx.clearRect(0, 0, zoom.width, zoom.height);
let pos = getMousePos(e)
zoomCtx.drawImage(canvas, pos.x, pos.y, 200, 200, 0, 0, 300, 300);
zoom.style.top = e.pageY - 10 + "px"
zoom.style.left = e.pageX - 10 + "px"
zoom.style.display = "block";
});
canvas.addEventListener("mouseleave", function() {
zoom.style.display = "none";
});
#tCanvas {
border: 1px solid red;
display: none;
position: absolute;
background-color: #808080;
pointer-events: none;
}
#canvas1 {
background-color: #808080;
}
<canvas id="tCanvas" width=100 height=100></canvas>
<canvas id="canvas1" width=240 height=800></canvas>
I think that your issue was with pointer-events: none; the top canvas was preventing the events from reaching the bottom canvas.
Also you don't have to declare your document.getElementById(".. on every function, those should be global constants
For the issue with a long canvas we need to use canvas.getBoundingClientRect in the calculation to get the real position of the mouse in the canvas

Zoom the canvas with respect to mouse cordinates

I have a very simple application, on which I draw the image on canvas perfectly. I am translating the context by image.width/2, image.height/2 and set the destination point as -image.width/2, -image.height/2.
function draw() {
window.canvas = document.getElementById('canvas');
window.ctx = canvas.getContext('2d');
window.image = document.createElement("img");
image.onload = function() {
canvas.width = image.width;
canvas.height = image.height;
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.translate(image.width/2,image.height/2);
ctx.drawImage(image,-image.width/2,-image.height/2);
};
image.src = 'rec.JPG';
}
This is displaying the image as it supposed to be. So considering this conept that if I want to make the center point of image in center of canvas I should do this as I did above. Now, I want user to click on canvas, I am getting mouse coordinates. Then I want to scale this image and redraw the canvas such that user will see that part of the image in center (zoomed version). The code is as follow:
function zoom(evt) {
var x = getMousePos(canvas, evt).x;
var y = getMousePos(canvas, evt).y;
canvas.width = image.width;
canvas.height = image.height;
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.translate(x,y);
ctx.scale(1.5, 1.5);
ctx.drawImage(image,-x,-y);
}
function getMousePos(canvas, evt) {
var rect = canvas.getBoundingClientRect();
return {
x: evt.clientX - rect.left,
y: evt.clientY - rect.top
};
}
But I am not getting the right results somehow. I want to see the region where user clicked and then I again want to click to zoom out and thats it. I can do a later part easily but having problem with zooming the perfect region. I saw similiar questions on stackoverflow, but they aren't meeting my requirement. This is my html and css:
<style>
.container {
height: 500px;
}
.container .image-container {
height: 500px;
width: 50%;
overflow: auto;
position: absolute;
border: 1px solid;
}
img {
display: none;
}
canvas {
width: 100%;
}
</style>
<body onload="draw()">
<div class="container">
<div class="image-container">
<canvas
id="canvas"
onclick="zoom(event)"
>
</canvas>
</div>
</div>
</body>
P.S: I don't want panning. Just zoom-in and out on clicks. You can play with the snippet.
function draw() {
window.canvas = document.getElementById('canvas');
window.ctx = canvas.getContext('2d');
window.image = document.createElement("img");
window.isZoom = false;
image.onload = function() {
canvas.width = image.width;
canvas.height = image.height;
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.translate(image.width/2,image.height/2);
ctx.drawImage(image,-image.width/2,-image.height/2);
};
image.src = 'https://d85ecz8votkqa.cloudfront.net/support/help_center/Print_Payment_Receipt.JPG';
}
function zoom(evt) {
if(!isZoom) {
var x = getMousePos(canvas, evt).x;
var y = getMousePos(canvas, evt).y;
canvas.width = image.width;
canvas.height = image.height;
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.translate(x,y);
ctx.scale(2, 2);
ctx.drawImage(image,-x,-y);
canvas.style.cursor = 'zoom-out';
isZoom = true;
} else {
canvas.width = image.width;
canvas.height = image.height;
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.translate(image.width/2,image.height/2);
ctx.drawImage(image,-image.width/2,-image.height/2);
isZoom=false;
canvas.style.cursor = 'zoom-in';
}
}
function getMousePos(canvas, evt) {
var rect = canvas.getBoundingClientRect();
return {
x: evt.clientX - rect.left,
y: evt.clientY - rect.top
};
}
<style>
.container {
height: 500px;
}
.container .image-container {
height: 500px;
width: 50%;
overflow: auto;
position: absolute;
border: 1px solid;
}
img {
display: none;
}
canvas {
width: 100%;
cursor: zoom-in;
}
</style>
<body onload="draw()">
<div class="container">
<div class="image-container">
<canvas
id="canvas"
onclick="zoom(event)"
>
</canvas>
</div>
</div>
</body>
You where really close, just have to mix the scale into your drawing of the image
function draw() {
window.canvas = document.getElementById('canvas');
window.ctx = canvas.getContext('2d');
window.image = document.createElement("img");
window.isZoom = false;
image.onload = function() {
canvas.width = image.width;
canvas.height = image.height;
ctx.drawImage(image, 0,0);
};
image.src = 'https://d85ecz8votkqa.cloudfront.net/support/help_center/Print_Payment_Receipt.JPG';
}
function zoom(evt) {
canvas.width = image.width;
canvas.height = image.height;
ctx.clearRect(0, 0, canvas.width, canvas.height);
var scale = 2
if (!isZoom) {
var pos = getMousePos(canvas, evt);
ctx.scale(scale, scale);
ctx.drawImage(image, -pos.x*scale*scale, -pos.y*scale*scale);
canvas.style.cursor = 'zoom-out';
} else {
ctx.drawImage(image, 0, 0);
canvas.style.cursor = 'zoom-in';
}
isZoom = !isZoom
}
function getMousePos(canvas, evt) {
var rect = canvas.getBoundingClientRect();
return {
x: evt.clientX - rect.left,
y: evt.clientY - rect.top
};
}
.container {
height: 500px;
}
.container .image-container {
height: 500px;
width: 50%;
overflow: auto;
position: absolute;
border: 1px solid;
}
img {
display: none;
}
canvas {
width: 100%;
cursor: zoom-in;
}
<body onload="draw()">
<div class="container">
<div class="image-container">
<canvas id="canvas" onclick="zoom(event)">
</canvas>
</div>
</div>
</body>
Besides the addition of var scale I did some cleanup to your code:
var x = getMousePos(canvas, evt).x;
var y = getMousePos(canvas, evt).y;
That was kind of a wasteful double call to getMousePos

canvas drawing margin correction

I'm trying some draw techniques I found in this URL:
http://perfectionkills.com/exploring-canvas-drawing-techniques/
I just noticed that the higher level css properties are not applied to the canvas element mouse events. Is there a easy way to fix this?
<head>
<meta charset="utf-8">
<title>Spray Can</title>
<style>
body {
margin: 0;
padding: 0;
}
#container {
border: 1px solid #ccc;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
#canvas {
}
</style>
<script>
document.addEventListener('DOMContentLoaded', function () {
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
var isDrawing;
canvas.onmousedown = function(e) {
isDrawing = true;
context.moveTo(e.clientX, e.clientY);
};
canvas.onmousemove = function(e) {
if (isDrawing) {
var k = 4;
var radgrad = context.createRadialGradient(e.clientX, e.clientY, k, e.clientX, e.clientY, k * 2);
radgrad.addColorStop(0, 'rgba(0,0,0,1)');
radgrad.addColorStop(0.5, 'rgba(0,0,0,0.5)');
radgrad.addColorStop(1, 'rgba(0,0,0,0)');
context.fillStyle = radgrad;
context.fillRect(e.clientX - k * 2, e.clientY - k * 2, k * 2 * 2, k * 2 * 2);
}
};
canvas.onmouseup = function() {
isDrawing = false;
};
});
</script>
</head>
<body>
<div id="container">
<canvas id="canvas" width="400" height="400"></canvas>
</div>
</body>
https://jsfiddle.net/crpq8t5q/1/
What you need is to convert your mouse event's coordinates to be relative to the canvas ones.
Since here you don't touch the scale nor rotation this is just a simple canvasX = mouseX - canvas.offsetLeft and canvasY = mouseY - canvas.offsetTop.
These offsetXXX properties are available on the canvas, but you can also use getBoundingClientRect()which will return better results if your css is more complicated (e.g nested elements with different scrollable areas).
But since this offset will change each time you scroll or resize the page, you need to update these values.
Also, it is a very bad idea to create a readialGradient in a mouse event. This event can fire at really high rate, and creating gradients eats memory.
It is then better to create a single gradient, and modify your whole context's matrix so that the gradient be placed at the mouse coordinates :
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
var isDrawing;
var k = 4;
// create the gradient only once
var radgrad = context.createRadialGradient(0, 0, k, 0, 0, k * 2);
radgrad.addColorStop(0, 'rgba(0,0,0,1)');
radgrad.addColorStop(0.5, 'rgba(0,0,0,0.5)');
radgrad.addColorStop(1, 'rgba(0,0,0,0)');
// get our canvas margins;
var rect;
function getRect() {
rect = canvas.getBoundingClientRect();
}
canvas.onmousedown = function(e) {
isDrawing = true;
context.moveTo(e.clientX, e.clientY);
};
canvas.onmousemove = function(e) {
if (isDrawing) {
// normalize our mouse event's coordinates
var x = e.clientX - rect.left;
var y = e.clientY - rect.top;
// change the canvas matrix coordinates so we draw at mouse positions
context.setTransform(1, 0, 0, 1, x, y)
context.fillStyle = radgrad;
context.fillRect(-k * 2, -k * 2, k * 2 * 2, k * 2 * 2);
}
};
canvas.onmouseup = function() {
isDrawing = false;
};
var debouncing = false;
function resizeHandler() {
debouncing = false;
getRect();
}
window.onscroll = window.onresize = function() {
// debounce the events
if (!debouncing) {
requestAnimationFrame(resizeHandler);
}
debouncing = true;
}
getRect();
body {
margin: 0;
padding: 0;
}
#container {
border: 1px solid #ccc;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
#canvas {}
<div id="container">
<canvas id="canvas" width="400" height="400"></canvas>
</div>

Save Canvas as Image Internet Explorer

I've built a couple of web resources to add signature functionality to a Microsoft Dynamics CRM 2011 form one bit built in silverlight as a file uploader etc. and the other in javascript and HTML to do the signature on the screen and save as an image but the code only works in google chrome.
I've done some reading around looked on endless amounts of stackoverflow threads but can't find any answers.
Is there anyway in which I can somehow either
save the canvas image in IE, the way the current solution already works in chrome
Open up a new window so the canvas image appears and can be right clicked and saved
I've tried doing both of the above already but had no luck.
It has to be in javascript also
<html><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);
}
function color(obj) {
switch (obj.id) {
case "black":
x = "black";
break;
case "white":
x = "white";
break;
}
if (x == "white") y = 50;
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 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();
}
}
}
function downloadCanvas(link, canvasId, filename) {
var test = document.getElementById(canvasId).toDataURL();
link.href = document.getElementById(canvasId).toDataURL();
link.download = filename;
window.open(test);
}
/**
* The event handler for the link's onclick event. We give THIS as a
* parameter (=the link element), ID of the canvas and a filename.
*/
document.getElementById('download').addEventListener('click', function() {
downloadCanvas(this, 'can', 'signature.png');
}, false);
</script>
<meta><meta><meta charset="utf-8"></head>
<body onload="init()">
<canvas width="400" height="200" id="can" style="border: 2px solid currentColor; left: 10px; top: 10px; position: absolute; background-color: rgb(255, 255, 255);"></canvas>
<div style="left: 450px; top: 10px; position: absolute;">Choose Color</div>
<div id="black" style="background: black; left: 550px; top: 15px; width: 19px; height: 19px; position: absolute;" onclick="color(this)"></div>
<div style="left: 450px; top: 40px; position: absolute;">Eraser</div>
<div id="white" style="background: white; border: 2px solid currentColor; left: 550px; top: 45px; width: 15px; height: 15px; position: absolute;" onclick="erase()"></div>
<img id="canvasimg" style="left: 52%; top: 10%; position: absolute;">
<a class="button" id="download" style="left: 10px; top: 220px; position: absolute;" onclick="downloadCanvas(this, 'can', 'test.png')" href="#">Download</a>
</body></html>
[FINAL ANSWER/EDIT]
Open a new window, and set the innerHTML property of the body to:
<img src="[DATA URI GOES HERE]"/>
According to MSDN, the toDataURL() method works in IE >= 9.
canvasElement.toDataURL(imageMimeType,quality)
imageMimeType can be "image/png", " image/jpg", "image/webp" and I believe "image/gif" as well.
To achieve what you wish to do, you could set an anchor with the href of the canvas data URI, or even use window.open to open the data URI in a new tab.
Saving Image in Same Tab: Another StackOverflow Question
Resources for Data URIs:
http://en.wikipedia.org/wiki/Data_URI_scheme
https://developer.mozilla.org/en-US/docs/Web/HTTP/data_URIs

how to add tools to my canvas app

I have a fully working canvas app but its a bit plain at the moment and i would like to add tool, i am very new to java, but still now alot so i will be able to understand most of it, all i want to add is like shapes like rectangle and triangle and squares.
Here is a working demo of it so far.
HTML
<!doctype html>
<html>
<head>
<link rel="shortcut icon" type="image/x-icon" href="SiteIcon.ico">
<title>Canvas</title>
<link rel="stylesheet" href="style.css">
<span style="cursor:crosshair">
</head>
<body>
<div id="toolbar">
<div id="rad">
Radius <span id="radval">10</span>
<div id="decrad" class="radcontrol">-</div>
<div id="incrad" class="radcontrol">+</div>
<font color="white">BACK</font>
<font color="white">CLEAR</font>
</div>
<div id="colors">
. Colour:
<input type="color" name="color1" id="color1" />
<br />
<br />
</div>
<canvas id="canvas" style="display: block;">sorry, your browser does not support our canvas tag.</canvas>
<script src="jQuery.js"></script>
</body>
</html>
CSS
* {
box-sizing: border-box;
-moz-box-sizing: border-box;
font-family: sans-serif;
margin: 0;
user-select:none;
-webkit-user-select:none;
-moz-user-select:none;
-ms-user-select:none;
}
#toolbar {
width: 100%;
height: 50px;
padding: 10px;
position: fixed;
top: 0;
background-color: #2f2f2f;
color: white;
}
.radcontrol {
width: 30px;
height: 30px;
background-color: #4f4f4f;
display: inline-block;
text-align: center;
padding: 5px;
}
#rad {
float: left;
}
#colors {
}
.swatch {
width: 30px;
height: 30px;
border-radius: 15px;
box-shadow: inset 0px 1px 0px rgba(255, 255, 255, 0.5), 0px 2px 2px rgba(0, 0, 0, 0.5);
display: inline-block;
margin-left: 10px;
}
.swatch.active {
border: 2px solid white;
box-shadow: inset 0px 1px 2px rgba(0, 0, 0, 0.5);
}
#back {
width: 60px;
height: 5px;
padding: 5%;
background-color: white;
}
JavaScript
function processData(c1, c2) {
var cv1 = document.getElementById(c1).value;
var cv2 = document.getElementById(c2).value;
alert(cv1 + "\n" + cv2);
}
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
var radius = 10;
var dragging = false;
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
context.lineWidth = radius * 2;
var putPoint = function (e) {
if (dragging) {
var bounds = canvas.getBoundingClientRect();
var mouseX = e.clientX + bounds.left;
var mouseY = e.clientY - bounds.top;
var mouseX = e.clientX + bounds.left - 20;
context.lineTo(mouseX, mouseY)
context.strokeStyle = document.getElementById('color1').value;
context.stroke();
context.beginPath();
context.arc(mouseX, mouseY, radius, 0, Math.PI * 2);
context.fillStyle = document.getElementById('color1').value;
context.fill();
context.beginPath();
context.moveTo(mouseX, mouseY);
}
}
var engage = function (e) {
dragging = true;
putPoint(e);
}
var disengage = function () {
dragging = false;
context.beginPath();
}
canvas.addEventListener('mousedown', engage);
canvas.addEventListener('mousemove', putPoint);
canvas.addEventListener('mouseup', disengage);
var setRadius = function (newRadius) {
if (newRadius < minRad) newRadius = minRad;
else if (newRadius > maxRad) newRadius = maxRad;
radius = newRadius;
context.lineWidth = radius * 2;
radSpan.innerHTML = radius;
}
var minRad = 1,
maxRad = 100,
defaultRad = 20,
interval = 5,
radSpan = document.getElementById('radval'),
decRad = document.getElementById('decrad'),
incRad = document.getElementById('incrad');
decRad.addEventListener('click', function () {
setRadius(radius - interval);
});
incRad.addEventListener('click', function () {
setRadius(radius + interval);
});
setRadius(defaultRad);
The first step would be extending the engage (mousedown) function because the way you have it now only works for a single functionality (putPoint). Then, you need to set a different event handler for mousemove for the same reason.
Your app needs to have states, like free drawing, rectangle, triangle, etc. The engage function first needs to read the current state (you can store the state in a variable), so if it's free drawing, it would work just as it works now, but if it is, for example, rectangle, then it would call a different function that you use for drawing rectangles.
You can go different ways about the implementation: you can simply click on the starting coordinates and click another time on the ending coordinates. Or you can start drawing the rectangle on mousedown and finish drawing it on mouseup. Both approaches still use 2 parameters: starting coordinates and ending coordinates.
With triangles it's a bit different since you obviously can't use the second approach from above (holding the mouse down while creating it) because the triangle requires three parameters (three dots).
UPDATE
And here is a super simple example of rectangle drawing using the mouse-down method I described above.
http://jsfiddle.net/egpr99k9/45/
This is the function that does the trick - it's call both on mouseup and mousedown events:
var rectData = {};
function drawRect(e, start) {
var bounds = canvas.getBoundingClientRect();
var mouseX = e.clientX + bounds.left - 20;
var mouseY = e.clientY - bounds.top;
if (start) {
rectData.start = {
x: mouseX,
y: mouseY
}
}
else {
if (rectData.start) {
context.beginPath();
context.rect(
rectData.start.x,
rectData.start.y,
mouseX - rectData.start.x,
mouseY - rectData.start.y
);
context.fillStyle = document.getElementById('color1').value;
context.fill();
}
rectData.start = {};
}
}
The next step here would be creating a live preview of the rectangle you're currently drawing (currently you won't see anything until you actually finish drawing it by releasing the mouse button). The best way to do it would be to use a temporary canvas on top of the main one, so that you can avoid problems with keeping the content "below" the rectangle you're currently drawing because you'd need to clear the canvas and refresh the preview on each mouse move while the button is down.
And once you release the mouse button, you would simply draw the contents of the temporary canvas to the main one, and delete or remove the temporary canvas.
I hope this is enough to get you started.
UPDATE
I went ahead and wrote you a simple example with live preview on a temporary canvas: http://jsfiddle.net/egpr99k9/46/
function processData(c1, c2) {
var cv1 = document.getElementById(c1).value;
var cv2 = document.getElementById(c2).value;
alert(cv1 + "\n" + cv2);
}
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
var radius = 10;
var dragging = false;
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
context.lineWidth = radius * 2;
var putPoint = function (e) {
if (dragging) {
var bounds = canvas.getBoundingClientRect();
var mouseX = e.clientX + bounds.left;
var mouseY = e.clientY - bounds.top;
var mouseX = e.clientX + bounds.left - 20;
context.lineTo(mouseX, mouseY)
context.strokeStyle = document.getElementById('color1').value;
context.stroke();
context.beginPath();
context.arc(mouseX, mouseY, radius, 0, Math.PI * 2);
context.fillStyle = document.getElementById('color1').value;
context.fill();
context.beginPath();
context.moveTo(mouseX, mouseY);
}
}
var tmpCanvas = document.getElementById('tmp');
tmpCanvas.width = canvas.width;
tmpCanvas.height = canvas.height;
tmpCanvas.id = 'tmp';
document.body.appendChild(tmpCanvas);
var tmpCtx = tmpCanvas.getContext('2d');
var rectData = {};
function drawRect(e, start, move) {
var bounds = canvas.getBoundingClientRect();
var mouseX = e.clientX + bounds.left - 20;
var mouseY = e.clientY - bounds.top;
if (start) {
rectData.start = {
x: mouseX,
y: mouseY
}
} else if (move) {
tmpCanvas.width = tmpCanvas.width;
tmpCtx.beginPath();
tmpCtx.rect(
rectData.start.x,
rectData.start.y,
mouseX - rectData.start.x,
mouseY - rectData.start.y);
tmpCtx.fillStyle = document.getElementById('color1').value;
tmpCtx.fill();
} else {
if (rectData.start) {
tmpCanvas.width = tmpCanvas.width;
context.beginPath();
context.rect(
rectData.start.x,
rectData.start.y,
mouseX - rectData.start.x,
mouseY - rectData.start.y);
context.fillStyle = document.getElementById('color1').value;
context.fill();
}
rectData.start = {};
}
}
var engage = function (e) {
dragging = true;
//putPoint(e);
drawRect(e, true);
}
var disengage = function (e) {
dragging = false;
context.beginPath();
drawRect(e, false);
}
canvas.addEventListener('mousedown', engage);
canvas.addEventListener('mousemove', function(e){
drawRect(e, false, true);
});
canvas.addEventListener('mouseup', disengage);
var setRadius = function (newRadius) {
if (newRadius < minRad) newRadius = minRad;
else if (newRadius > maxRad) newRadius = maxRad;
radius = newRadius;
context.lineWidth = radius * 2;
radSpan.innerHTML = radius;
}
var minRad = 1,
maxRad = 100,
defaultRad = 20,
interval = 5,
radSpan = document.getElementById('radval'),
decRad = document.getElementById('decrad'),
incRad = document.getElementById('incrad');
decRad.addEventListener('click', function () {
setRadius(radius - interval);
});
incRad.addEventListener('click', function () {
setRadius(radius + interval);
});
setRadius(defaultRad);
* {
box-sizing: border-box;
-moz-box-sizing: border-box;
font-family: sans-serif;
margin: 0;
user-select:none;
-webkit-user-select:none;
-moz-user-select:none;
-ms-user-select:none;
}
#toolbar {
width: 100%;
height: 50px;
padding: 10px;
position: fixed;
top: 0;
background-color: #2f2f2f;
color: white;
}
.radcontrol {
width: 30px;
height: 30px;
background-color: #4f4f4f;
display: inline-block;
text-align: center;
padding: 5px;
}
#rad {
float: left;
}
#colors {
}
.swatch {
width: 30px;
height: 30px;
border-radius: 15px;
box-shadow: inset 0px 1px 0px rgba(255, 255, 255, 0.5), 0px 2px 2px rgba(0, 0, 0, 0.5);
display: inline-block;
margin-left: 10px;
}
.swatch.active {
border: 2px solid white;
box-shadow: inset 0px 1px 2px rgba(0, 0, 0, 0.5);
}
#back {
width: 60px;
height: 5px;
padding: 5%;
background-color: white;
}
canvas {
cursor: crosshair;
display: block;
}
#tmp {
pointer-events: none;
display: block; position: absolute;left: 10px;top: 50px;
opacity: 0.5;
}
<div id="toolbar">
<div id="rad">Radius <span id="radval">10</span>
<div id="decrad" class="radcontrol">-</div>
<div id="incrad" class="radcontrol">+</div> <font color="white">BACK</font>
<font color="white">CLEAR</font>
</div>
<div id="colors">. Colour:
<input type="color" name="color1" id="color1" />
<br />
<br />
</div>
<canvas id="canvas">sorry, your browser does not support our canvas tag.</canvas>
<canvas id="tmp">sorry, your browser does not support our canvas tag.</canvas>
</div>
Look, I rewrote your code with jQuery and Fabric.
function getRandInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
var canvas = new fabric.Canvas('canvas', {
isDrawingMode: true
});
var w = canvas.width;
var h = canvas.height;
$('#tri').click(function () {
var triangle = new fabric.Triangle({
width: getRandInt(10, w / 2),
height: getRandInt(10, h / 2),
fill: $('#color1').val(),
left: getRandInt(10, w / 2),
top: getRandInt(10, h / 2)
});
canvas.add(triangle);
});
$("#rect").click(function () {
var rect = new fabric.Rect({
width: getRandInt(10, w / 2),
height: getRandInt(10, h / 2),
fill: $('#color1').val(),
left: getRandInt(10, w / 2),
top: getRandInt(10, h / 2)
});
canvas.add(rect);
});
$("#circle").click(function () {
var circle = new fabric.Circle({
radius: getRandInt(10, w / 4),
fill: $('#color1').val(),
left: getRandInt(10, w / 2),
top: getRandInt(10, h / 2)
});
canvas.add(circle);
});
$('#radius').change(function () {
canvas.freeDrawingBrush.width = parseInt($(this).val(), 10) || 1;
});
$('#color1').change(function () {
canvas.freeDrawingBrush.color = $(this).val();
});
$('#edit').click(function () {
canvas.isDrawingMode = !canvas.isDrawingMode;
$("#draw").prop('disabled', false);
$(this).prop('disabled', true);
});
$('#draw').click(function () {
canvas.isDrawingMode = !canvas.isDrawingMode;
$("#edit").prop('disabled', false);
$(this).prop('disabled', true);
});
$('#c').click(function () {
canvas.clear();
});
Result JSFiddle!

Categories

Resources