how to add tools to my canvas app - javascript

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!

Related

HTML5 Canvas stroke not following mouse Y point

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);

Need help writing a function to make any box inside the grid clickable and filled with colorwheel

I'm working on a pixel art project. What I was expecting to do here is be able to click any box inside the grid and render it with the the color from a color input.
I managed to understand how the loop works to create a grid using canvas and javascript. Next, I created a function called colorPicker() which allows me to click on any of the grid cells and take the value from the color input and render it that color. I used canvas.addEventListener("click", function(event) { ... })
and passed variables xCoord and yCoord with the value of event.x and event.y. This allows me to return the position of the mouse on click inside the grid.
The problem I'm running into right now is that it is only rendering 1 square out of all 400 I have drawn on the grid. The rendering works, it's just I can only click one box and render its color from the input.
Can anyone provide me with some help to solve this function just using the javascript logic I currently have right now?
var title = document.getElementById("title");
var canvas = document.getElementById("canvas");
var color = document.getElementById("color");
var btn = document.getElementById("btn");
var ctx = canvas.getContext("2d");
// The function that draws the grid
function drawGrid() {
ctx.fillStyle = "#009EFF";
ctx.fillRect(0, 0, 400, 400);
ctx.strokeStyle = "black";
ctx.lineWidth = 1;
// The for loop that draws the x-axis
for (x = 0; x <= 400; x += 20) {
ctx.moveTo(x, 0);
ctx.lineTo(x, 400);
ctx.stroke();
}
// The for loop that draws the y-axis
for (y = 0; y <= 400; y += 20) {
ctx.moveTo(0, y);
ctx.lineTo(400, y);
ctx.stroke();
}
}
drawGrid();
// Function that clicks and fill grid boxes w / color
function colorPicker() {
canvas.addEventListener("click", function(event) {
var newColor = color.value;
ctx.fillStyle = newColor;
xCoord = event.x;
yCoord = event.y;
ctx.fillRect(0, 0, 20, 20);
console.log(xCoord, yCoord);
console.log(newColor);
});
}
colorPicker();
#canvas {
border: 1px solid black;
background-size: 100%;
display: block;
margin-top: 50px;
margin-left: auto;
margin-right: auto;
padding-left: 0;
padding-right: 0;
}
<div class="color-wheel">
Color: <input type="color" id="color" />
</div>
<canvas id="canvas" width="400" height="400"></canvas>
Your problem is this line:
ctx.fillRect(0, 0, 20, 20);
Instead of filling the clicked cell, you’re filling only the upper left cell on every click.
Currently you’re just using (0, 0) here, when you should be calculating this position from the cursor. The coordinates used by the canvas and the cursor are different though, so you’ll need to write a conversion function:
function mousePosToCanvasPos(mouseX, mouseY) {
var canvasPos = canvas.getBoundingClientRect();
return {
x: Math.floor((mouseX - canvasPos.x) / 20) * 20,
y: Math.floor((mouseY - canvasPos.y) / 20) * 20,
};
}
The mousePosToCanvasPos() function gets the current rendered position of the canvas (canvasPos), and calculates the offset of the cursor from the upper left corner of the canvas (mouse_ - canvasPos._). It then rounds this value down to the nearest multiple of 20 to return the upper left corner of the clicked cell (Math.floor((mouse_ - canvasPos._) / 20) * 20). If you change your cell size to something other than 20, be sure to change it in this function too. Or better yet, extract the constant (var cellSize = 20).
Adding this function to your code gives us:
var title = document.getElementById("title");
var canvas = document.getElementById("canvas");
var color = document.getElementById("color");
var btn = document.getElementById("btn");
var ctx = canvas.getContext("2d");
// The function that draws the grid
function drawGrid() {
ctx.fillStyle = "#009EFF";
ctx.fillRect(0, 0, 400, 400);
ctx.strokeStyle = "black";
ctx.lineWidth = 1;
// The for loop that draws the x-axis
for (x = 0; x <= 400; x += 20) {
ctx.moveTo(x, 0);
ctx.lineTo(x, 400);
ctx.stroke();
}
// The for loop that draws the y-axis
for (y = 0; y <= 400; y += 20) {
ctx.moveTo(0, y);
ctx.lineTo(400, y);
ctx.stroke();
}
}
drawGrid();
function mousePosToCanvasPos(mouseX, mouseY) {
var canvasPos = canvas.getBoundingClientRect();
return {
x: Math.floor((mouseX - canvasPos.x) / 20) * 20,
y: Math.floor((mouseY - canvasPos.y) / 20) * 20,
};
}
// Function that clicks and fill grid boxes w / color
function colorPicker() {
canvas.addEventListener("click", function(event) {
var newColor = color.value;
ctx.fillStyle = newColor;
var canvasCellPos = mousePosToCanvasPos(event.x, event.y);
ctx.fillRect(canvasCellPos.x, canvasCellPos.y, 20, 20);
console.log(event.x, event.y);
console.log(newColor);
});
}
colorPicker();
#canvas {
border: 1px solid black;
background-size: 100%;
display: block;
margin-top: 50px;
margin-left: auto;
margin-right: auto;
padding-left: 0;
padding-right: 0;
}
<div class="color-wheel">
Color: <input type="color" id="color" />
</div>
<canvas id="canvas" width="400" height="400"></canvas>
You’ll notice that something’s still not quite right here: every time you fill a cell, it makes its border thinner. To solve this, you need to remove one pixel from each side of your fillRect:
ctx.fillRect(canvasCellPos.x + 1, canvasCellPos.y + 1, 18, 18);
var title = document.getElementById("title");
var canvas = document.getElementById("canvas");
var color = document.getElementById("color");
var btn = document.getElementById("btn");
var ctx = canvas.getContext("2d");
// The function that draws the grid
function drawGrid() {
ctx.fillStyle = "#009EFF";
ctx.fillRect(0, 0, 400, 400);
ctx.strokeStyle = "black";
ctx.lineWidth = 1;
// The for loop that draws the x-axis
for (x = 0; x <= 400; x += 20) {
ctx.moveTo(x, 0);
ctx.lineTo(x, 400);
ctx.stroke();
}
// The for loop that draws the y-axis
for (y = 0; y <= 400; y += 20) {
ctx.moveTo(0, y);
ctx.lineTo(400, y);
ctx.stroke();
}
}
drawGrid();
function mousePosToCanvasPos(mouseX, mouseY) {
var canvasPos = canvas.getBoundingClientRect();
return {
x: Math.floor((mouseX - canvasPos.x) / 20) * 20,
y: Math.floor((mouseY - canvasPos.y) / 20) * 20,
};
}
// Function that clicks and fill grid boxes w / color
function colorPicker() {
canvas.addEventListener("click", function(event) {
var newColor = color.value;
ctx.fillStyle = newColor;
var canvasCellPos = mousePosToCanvasPos(event.x, event.y);
ctx.fillRect(canvasCellPos.x + 1, canvasCellPos.y + 1, 18, 18);
console.log(event.x, event.y);
console.log(newColor);
});
}
colorPicker();
#canvas {
border: 1px solid black;
background-size: 100%;
display: block;
margin-top: 50px;
margin-left: auto;
margin-right: auto;
padding-left: 0;
padding-right: 0;
}
<div class="color-wheel">
Color: <input type="color" id="color" />
</div>
<canvas id="canvas" width="400" height="400"></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

Categories

Resources