HTML5 Canvas line draw between two clicks, live - javascript

You might see that this is my first post so excuse any beginner mistakes using stackoverflow.
I'm currently working on a floor plan web app where you can simply draw lines to create the floor plan of your housing.
The wanted effect is:
When clicking once the user draws a temporary line where the start point X is the clicked point and the target point Z is the mouse which the user can move around.
I'm currently using canvas for this effect but somehow the line is invisible or just not there. I've tried some debugging which brought me here.
This is the current code:
function drawLineXY(fromXY, toXY) {
if (!lineElem) {
lineElem = document.createElement('canvas');
lineElem.style.position = "absolute";
lineElem.style.zIndex = 100;
document.body.appendChild(lineElem);
console.log("Added line element");
}
var leftpoint, rightpoint;
if (fromXY.x < toXY.x) {
leftpoint = fromXY;
rightpoint = toXY;
} else {
leftpoint = toXY;
rightpoint = fromXY;
}
var lineWidthPix = 4;
var gutterPix = 0;
var origin = {
x: leftpoint.x - gutterPix,
y: Math.min(fromXY.y, toXY.y) - gutterPix
};
lineElem.width = "1000px";
lineElem.height = "1000px";
lineElem.style.left = "0px";
lineElem.style.top = "0px";
var ctx = lineElem.getContext('2d');
// Use the identity matrix while clearing the canvas
ctx.save();
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.clearRect(0, 0, lineElem.width, lineElem.height);
ctx.restore();
ctx.lineWidth = 4;
ctx.strokeStyle = '#09f';
ctx.beginPath();
ctx.moveTo(fromXY.x - origin.x, fromXY.y - origin.y);
ctx.lineTo(toXY.x - origin.x, toXY.y - origin.y);
ctx.stroke();
console.log("drawing line..");
}
function moveHandler(evt) {
var startCentre, startBounds;
var targets = [];
if (clicked.length === 2) {
targets = clicked;
} else if (clicked.length === 1) {
targets.push(clicked[0]);
if (typeof hoverElement !== 'undefined') {
targets.push(hoverElement);
}
}
if (targets.length == 2) {
var start = {
x: targets[0],
y: targets[0]
};
var end = {
x: targets[1],
y: targets[1]
};
drawLineXY(start, end);
} else if (targets.length == 1) {
var start = {
x: targets[0],
y: targets[0]
};
drawLineXY(start, {
x: evt.clientX,
y: evt.clientY
});
}
};
function clickHandler(e) {
if (clicked.length == 2) {
clicked = [];
}
clicked.push(e.target);
};
document.onclick = clickHandler;
document.onmousemove = moveHandler;
As you can see in drawLineXY's last line I've made a debug console log "drawing line"
This works as I move the mouse around. Like it should.
But there is no line, does someone has help?
PS: #canvas is specified in style.css.

I created a very basic example of probably what you are trying to achieve:
let c, ctx, fromXY, toXY;
window.onload = function(){
document.onclick = clickHandler;
document.onmousemove = moveHandler;
c = document.getElementById("myCanvas");
ctx = c.getContext("2d");
reset();
}
function draw(){
clear();
ctx.beginPath();
ctx.moveTo(fromXY.x, fromXY.y);
ctx.lineTo(toXY.x, toXY.y);
ctx.stroke();
ctx.closePath();
}
function clear(){
ctx.clearRect(0, 0, c.width, c.height);
}
function reset() {
fromXY = {};
toXY = {};
}
function moveHandler(e) {
if(typeof fromXY.x !== "undefined"){
toXY.x = e.clientX;
toXY.y = e.clientY;
draw();
}
}
function clickHandler(e) {
if(typeof fromXY.x === "undefined"){
fromXY.x = e.clientX;
fromXY.y = e.clientY;
}else{
reset();
}
}
<canvas id="myCanvas" height="500" width="500"></canvas>
You can set line options in the draw() function, and if you want the lines to persist, you would save their fromXY and toXY in an array and redraw them as well.

Related

How i can draw new lines with convas js

i want to draw lines with convas but my previous lines was deleted when i try create a new line. I wark with convas the first time and i will be happy if you can say me about my mistakes and solutions of the problem
const convas = document.querySelector(".v");
const ctx = convas.getContext("2d");
let startPositionLine = { x: 0, y: 0 };
let endPositionLine = { x: 0, y: 0 };
let { xStart, yStart } = startPositionLine;
let { xEnd, yEnd } = endPositionLine;
function moveMouseEvent(e) {
xEnd = e.offsetX;
yEnd = e.offsetY;
ctx.beginPath();
ctx.clearRect(0, 0, convas.width, convas.height);
ctx.moveTo(xStart, yStart);
ctx.lineTo(xEnd, yEnd);
ctx.stroke();
}
convas.onmousedown = (e) => {
ctx.beginPath();
xStart = e.offsetX;
yStart = e.offsetY;
ctx.stroke();
convas.onmousemove = (e) => moveMouseEvent(e);
};
convas.onmouseup = () => {
convas.onmousemove = null;
};
I mentioned this in my comment to your post above, but thought I would just give an example you can run and test here. So you are correct in what you are doing, but you just need to save each drawn line to an array AS YOU DRAW THEM. You will see the code for this in the mousedown event handler here. Then you need to redraw those saved lines each time after you clear the canvas in mousemove event handler.
const canvas = document.getElementById('canvas');
var canvasrect = canvas.getBoundingClientRect();
var Xstart = 0;
var Ystart = 0;
var Xend = 0
var Yend = 0
var isDrawing = false;
var linesArray = [{}];
// MOUSE CLICK / BUTTON DOWN LISTENER
canvas.addEventListener('mousedown', e => {
e.preventDefault();
e.stopPropagation();
canvasrect = canvas.getBoundingClientRect();
Xstart = e.clientX - canvasrect.left;
Ystart = e.clientY - canvasrect.top;
// We need to know if the user is actuallydrawing or not.
// Each time the user clicks their canvas we toggle whether to start or stop drawing.
if (isDrawing) {
// If this is the end of a line, save the end coordinates to the latest array element.
linesArray[linesArray.length - 1].xe = Xend;
linesArray[linesArray.length - 1].ye = Yend;
isDrawing = false;
} else {
// If this is the start of a new line, save the start coordinates in a new array element along with end coordinate placeholders.
linesArray.push({
xs: Xstart,
ys: Ystart,
xe: 0,
ye: 0
})
isDrawing = true;
}
});
// MOUSE MOVE
canvas.addEventListener('mousemove', e => {
e.preventDefault();
e.stopPropagation();
// get the current mouse x & y locations along with any scrolling and window resizing offsets.
Xend = e.clientX - canvasrect.left;
Yend = e.clientY - canvasrect.top;
if (isDrawing === true) {
var ctx = canvas.getContext('2d');
//clear the canvas
ctx.clearRect(0, 0, canvasrect.width, canvasrect.height);
// Draw a line from the initial click coordinates to the current mouse pointer coordinates.
ctx.strokeStyle = '#fe0101';
ctx.lineWidth = 3;
ctx.beginPath();
ctx.moveTo(Xstart, Ystart);
ctx.lineTo(Xend, Yend);
ctx.stroke();
// now redraw the previous lines saved in the linesArray
if (linesArray.length >= 1) {
for (let i = 0; i < linesArray.length; i++) {
if (linesArray[i].xe != 0) {
ctx.strokeStyle = '#fe0101';
ctx.lineWidth = 3;
ctx.beginPath();
ctx.moveTo(linesArray[i].xs, linesArray[i].ys);
ctx.lineTo(linesArray[i].xe, linesArray[i].ye);
ctx.stroke();
}
}
}
}
});
<canvas id="canvas" width="500" height="500" style="background-color:lightGray"></canvas>

How to make a ctrl+z keyboard event in a canvas?

I am trying to make an 'undo' action like Ctrl + Z.
I made a simple canvas paint example to make it easier to understand what I would like. When the user moves the mouse without releasing mouse1, something will be drawn on the canvas. Then, when they stop pressing mouse1, the drawing ends. The Ctrl + Z shortcut will undo these drawings.
Here is the code:
//var
const canvas = document.getElementById('canvas');
const cC = canvas.getContext('2d');
//trigger for write or not
var pressedQ = false;
//
function getMousePosition(evt) {
var rect = canvas.getBoundingClientRect();
var root = document.documentElement;
var mouseX = evt.clientX - rect.left - root.scrollLeft;
var mouseY = evt.clientY - rect.top - root.scrollTop;
return {
x: mouseX,
y: mouseY
};
}
function writeCircle(posX, posY, size, color) {
cC.fillStyle = 'black';
cC.beginPath();
cC.arc(posX, posY, size, 0, Math.PI*2, true);
cC.fill();
}
function pencil(evt) {
if (pressedQ == true) {
var mousePos = getMousePosition(evt);
writeCircle(mousePos.x, mousePos.y, 50);
}
else{}
}
function activate(textmode, evt) {
pressedQ = true;
console.log('start');
}
function deactivate() {
pressedQ = false;
console.log('finish');
}
window.onload = function() {
cC.clearRect(0, 0, canvas.width, canvas.height);
canvas.addEventListener('mousedown', activate);
canvas.addEventListener('mousemove', pencil);
canvas.addEventListener('mouseup', deactivate);
}
<canvas id="canvas" width="700" height="490"></canvas>
Thanks!
Note: I cant understand ES2015+ syntax
What you're looking for is the command design pattern. Since you're using functions here, you just need to store the function name and its parameters. Then, you can use that data to call the function again later. The following example isn't perfect, but it should demonstrate the basic idea. (I tried to avoid any JavaScript syntax that was added after the 2015 update)
var canvas = document.getElementById('canvas');
var cC = canvas.getContext('2d');
var pressedQ = false; //trigger for write or not
var commands = [];
var commandTypes = {
drawCircle: function (posX, posY, size, color) {
cC.fillStyle = 'black';
cC.beginPath();
cC.arc(posX, posY, size, 0, Math.PI * 2, true);
cC.fill();
}
};
function execute() {
var commandType = arguments[0];
var data = Array.prototype.slice.call(arguments, 1);
if (!commandTypes.hasOwnProperty(commandType))
throw new Error(commandType + ' is not a real command');
commandTypes[commandType].apply(null, data);
}
function pushAndExecute() {
commands.push(arguments);
execute.apply(null, arguments);
}
function getMousePosition(evt) {
var rect = canvas.getBoundingClientRect();
var root = document.documentElement;
return {
x: evt.offsetX - rect.left - root.scrollLeft,
y: evt.offsetY - rect.top - root.scrollTop
};
}
function pencil(evt) {
if (!pressedQ) return;
var mousePos = getMousePosition(evt);
pushAndExecute('drawCircle', mousePos.x, mousePos.y, 50);
}
function activate(evt) {
pressedQ = true;
// console.log('start');
pencil(evt);
}
function deactivate() {
pressedQ = false;
// console.log('finish');
}
function handleKeys(evt) {
if (evt.ctrlKey && evt.key === 'z') {
// console.log('undo');
// Remove the most recent command from the list
commands.splice(-1, 1);
// Clear canvas
cC.clearRect(0, 0, canvas.width, canvas.height);
// Re-play all commands (re-draw canvas from scratch)
commands.forEach(function (command) {
execute.apply(null, command);
});
}
}
window.onload = function() {
cC.clearRect(0, 0, canvas.width, canvas.height);
canvas.addEventListener('mousedown', activate);
canvas.addEventListener('mousemove', pencil);
canvas.addEventListener('mouseup', deactivate);
window.addEventListener('keydown', handleKeys);
}
<canvas id="canvas" width="700" height="490"></canvas>

JavaScript Canvas on draw vanishes

I have a canvas function which draws a square if I click on the canvas field and move the mouse, that works so far.
My Problem is that if I release the mouse and click at the canvas again the old drawn rectangle vanishes.
How do I make it possible that the old drawn does not get vanished.
My function:
function foo() {
var tool = this;
this.started = false;
var canvasx = canvas.offsetLeft;
var canvasy = canvas.offsetTop;
var last_mousex = 0;
var last_mousey = 0;
var mousex = 0;
var mousey = 0;
this.mousedown = function (ev) {
if(checkboxSquare.checked) {
last_mousex = parseInt(ev.clientX-canvasx);
last_mousey = parseInt(ev.clientY-canvasy);
context.strokeStyle = $('#selectColor').val();
context.lineWidth = $('#selectWidth').val();
tool.started = true;
}
};
this.mousemove = function (ev) {
if (tool.started && checkboxSquare.checked) {
mousex = parseInt(ev.clientX-canvasx);
mousey = parseInt(ev.clientY-canvasy);
context.clearRect(0, 0, canvas.width, canvas.height); // clear canvas
context.beginPath();
var width = mousex-last_mousex;
var height = mousey-last_mousey;
context.rect(last_mousex,last_mousey,width,height);
context.stroke();
}
};
this.mouseup = function (ev) {
if (tool.started && checkboxSquare.checked) {
tool.mousemove(ev);
tool.started = false;
}
};
}
It Looks something like this: http://jsfiddle.net/AbdiasSoftware/kqW4X/
The old drawn rectangle vanishes on click because, you are clearing the entire canvas each time before drawing a rectangle.
The easiest workaround would be to save the entire canvas as an image on mouseup and draw that image before drawing each rectangle.
var canvas;
var _foo = new foo();
canvas.onmousedown = _foo.mousedown;
canvas.onmousemove= _foo.mousemove;
canvas.onmouseup = _foo.mouseup;
function foo() {
canvas = $('#canvas')[0];
var context = canvas.getContext('2d');
var checkboxSquare = $('#checkboxSquare')[0];
var img = new Image();
var tool = this;
this.started = false;
var last_mousex = 0;
var last_mousey = 0;
var mousex = 0;
var mousey = 0;
this.mousedown = function (ev) {
if(checkboxSquare.checked) {
last_mousex = ev.offsetX;
last_mousey = ev.offsetY;
context.strokeStyle = $('#selectColor').val();
context.lineWidth = $('#selectWidth').val();
tool.started = true;
}
};
this.mousemove = function (ev) {
if (tool.started && checkboxSquare.checked) {
mousex = ev.offsetX;
mousey = ev.offsetY;
context.clearRect(0, 0, canvas.width, canvas.height); // clear canvas
context.drawImage(img, 0, 0); // draw saved canvas (image)
context.beginPath();
var width = mousex-last_mousex;
var height = mousey-last_mousey;
context.rect(last_mousex,last_mousey,width,height);
context.stroke();
}
};
this.mouseup = function (ev) {
if (tool.started && checkboxSquare.checked) {
tool.mousemove(ev);
img.src = canvas.toDataURL(); // save canvas as image
tool.started = false;
}
};
}
canvas {
border: 1px solid black;
cursor: default;
margin-top: 5px
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type="checkbox" id="checkboxSquare">Square | Color
<select id="selectColor">
<option value="red">red</option>
<option value="green">green</option>
<option value="blue">blue</option>
</select> | Width
<select id="selectWidth">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<canvas id="canvas" width="400" height="400"></canvas>
Just create a background canvas same as the main canvas. When you drag out a new box, first draw the background canvas (with all the past boxes) on the main canvas then the current box being drawn. When you finish dragging the box, just daw it to the background canvas.
const canvas = document.createElement("canvas");
const background = document.createElement("canvas");
canvas.style.border="2px solid black";
canvas.style.cursor = "crosshair";
background.width = canvas.width = innerWidth - 24;
background.height = canvas.height = innerHeight - 24;
const ctx = canvas.getContext("2d");
background.ctx = background.getContext("2d");
document.body.appendChild(canvas);
const bounds = canvas.getBoundingClientRect();
var currentBox;
const boxStyle = {
fillStyle : "#4aF",
strokeStyle : "black",
lineWidth : 3,
lineJoin : "round",
}
const mouse = { x : 0, y : 0,button : false, changed : false };
["mousemove","mousedown","mouseup"].forEach(en => document.addEventListener(en, mouseEvent));
function createBox(x,y,w,h,style){ return {x,y,w,h,style,draw : drawBox} }
function drawBox(ctx){
setStyle(ctx, this.style);
ctx.beginPath();
ctx.rect(this.x,this.y,this.w,this.h);
ctx.fill();
ctx.stroke();
}
function setStyle(ctx, style){ Object.keys(style).forEach(key => ctx[key] = style[key]) }
function mouseEvent(event) {
mouse.x = event.pageX - bounds.left - scrollX;
mouse.y = event.pageY - bounds.top - scrollY;
if(event.type === "mousedown"){ mouse.button = true }
else if(event.type === "mouseup"){ mouse.button = false }
mouse.changed = true;
}
function mainLoop(){
var b = currentBox; // alias for readability
if(mouse.changed){
if(mouse.button){
if(!b){
b = currentBox = createBox(mouse.x,mouse.y,0,0,boxStyle);
}else{
b.w = mouse.x - b.x;
b.h = mouse.y - b.y;
}
}else if(b){
b.draw(background.ctx);
b = currentBox = undefined;
}
if(b){
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.drawImage(background,0,0);
b.draw(ctx);
canvas.style.cursor = "none";
}else{
canvas.style.cursor = "crosshair";
}
mouse.changed = false;
}
requestAnimationFrame(mainLoop);
}
requestAnimationFrame(mainLoop);
Extra Note. Capture the mouse using the Document
When you create canvas drawing apps you should listen to the document mouse events rather than the canvas. When the mouse button is down the mouse is captured and will continue to send mouse events while the mouse is down, even if you have moved off the canvas, document, or event outside the browser window.
This means you can drag content of the canvas and not worry about losing the mouseup event.
Burn some time.
I have some time to burn so will extend the demo above to include selecting and moving existing boxes. Draw boxes as normal. Mouse over boxes will highlight them, click to select them. When selected can be dragged. Uses the same method background image to hold old boxes. But have added a box list to hold old boxes
A more extensive example
const canvas = document.createElement("canvas");
const background = document.createElement("canvas");
canvas.style.border="2px solid black";
canvas.style.cursor = "crosshair";
background.width = canvas.width = innerWidth - 24;
background.height = canvas.height = innerHeight - 24;
const ctx = canvas.getContext("2d");
background.ctx = background.getContext("2d");
document.body.appendChild(canvas);
const bounds = canvas.getBoundingClientRect();
var currentBox;
var selectedBox;
var mouseOverBox;
const styles = {
box : {
fillStyle : "#4aF",
strokeStyle : "black",
lineWidth : 3,
lineJoin : "round",
},
highlight : {
strokeStyle : "white",
lineWidth : 1,
lineJoin : "round",
setLineDash : [[10,10]],
},
selected : {
strokeStyle : "red",
lineWidth : 2,
lineJoin : "round",
setLineDash : [[5,5]],
},
}
const boxes = {
items : [],
add(box){ // add a box and fix width and height to positive
if(box.w < 0){
box.x += box.w;
box.w = -box.w;
}
if(box.h < 0){
box.y += box.h;
box.h = -box.h;
}
boxes.items.push(box)
},
apply(name, ...args){
for(var i = 0; i < boxes.items.length; i ++ ){
boxes.items[i][name](...args);
}
},
};
const mouse = { x : 0, y : 0,button : false, changed : false };
["mousemove","mousedown","mouseup"].forEach(en => document.addEventListener(en, mouseEvent));
const boxBehaviours = {
draw(ctx, style = this.style){
if(!this.hide){
setStyle(ctx, style);
ctx.beginPath();
ctx.rect(this.x,this.y,this.w,this.h);
if(style.fillStyle) { ctx.fill() }
if(style.strokeStyle) {ctx.stroke() }
}
},
isPointOver(x,y){
var b = this;
if(x >= b.x && x < b.x + b.w && y >= b.y && y < b.y + b.h){
b.mouseOver = true;
boxBehaviours.topMouseBox = b;
}else {
b.mouseOver =false;
}
},
}
function createBox(x,y,w,h,style){
return {x,y,w,h,style, ...boxBehaviours};
}
function setStyle(ctx, style){
Object.keys(style).forEach(key => {
if(typeof ctx[key] === "function"){
ctx[key](...style[key]);
}else{
ctx[key] = style[key];
}
})
}
function mouseEvent(event) {
mouse.x = event.pageX - bounds.left - scrollX;
mouse.y = event.pageY - bounds.top - scrollY;
if(event.type === "mousedown"){ mouse.button = true }
else if(event.type === "mouseup"){ mouse.button = false }
}
function redrawBackground(){
background.ctx.clearRect(0,0,canvas.width,canvas.height)
boxes.apply("draw",background.ctx);
}
function mainLoop(time){
var b = currentBox; // alias for readability
var mob = mouseOverBox; // alias for readability
var sb = selectedBox; // alias for readability
// first check mouse button. If button down could be
// dragging a selected box or creating a new box
if(mouse.button){
if(sb){ // is selected box
if(!mouse.drag){ // start the drag
mouse.drag = {x : mouse.x - sb.x, y : mouse.y - sb.y}
}else{ // move the box
sb.x = mouse.x- mouse.drag.x;
sb.y = mouse.y- mouse.drag.y;
}
}else{ // else muse be create (or select click)
if(!b){
b = currentBox = createBox(mouse.x,mouse.y,0,0,styles.box);
}else{
b.w = mouse.x - b.x;
b.h = mouse.y - b.y;
}
}
}else if(b || sb){ // mouse up and there is a box
if(sb){ // if selected box
if(mouse.drag){ // is dragging then drop it
mouse.drag = undefined;
sb.hide = false;
redrawBackground();
sb = selectedBox = undefined;
}
// is the mouse is down and has not moved over 2 pixels
// and there is a mob (mouseOverBox) under it
// then dump the new box and select the mob box
}else if(Math.abs(b.w) < 2 && Math.abs(b.h) < 2 && mob){
sb = selectedBox = mob;
mob = mouseOverBox = undefined;
b = currentBox = undefined;
sb.hide = true;
redrawBackground();
}else{
// just a normal box add it to box array
// draw it and remove it from currentBox
boxes.add(b);
b.draw(background.ctx);
b = currentBox = undefined;
}
}
// clear andf draw background
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.drawImage(background,0,0);
if(b){ // is there a current box then draw that
b.draw(ctx);
canvas.style.cursor = "none";
} else { // no current box so
// find any boxes under the mouse
boxBehaviours.topMouseBox = null;
boxes.apply("isPointOver",mouse.x, mouse.y);
// is there a selected box (sb)
if(sb){ // yes selected box then draw it
ctx.save();
styles.selected.lineDashOffset = time / 25;
sb.hide = false;
sb.draw(ctx,styles.selected);
sb.hide = true;
ctx.restore();
canvas.style.cursor = "move";
// no selected box sp then just high light the box under the
// mouse and assign it to mouseOverBox (mob);
}else if(boxBehaviours.topMouseBox){
mob = mouseOverBox = boxBehaviours.topMouseBox;
ctx.save();
styles.highlight.lineDashOffset = time / 20;
mob.draw(ctx, styles.highlight);
ctx.restore();
canvas.style.cursor = "pointer";
}else{
canvas.style.cursor = "crosshair";
}
}
requestAnimationFrame(mainLoop);
}
requestAnimationFrame(mainLoop);
var point = [];
var clicks = 0;
var sketch = document.querySelector('#sketch');
var sketch_style = getComputedStyle(sketch);
// Creating a tmp canvas
var tmp_canvas = document.createElement('canvas');
var tmp_ctx = tmp_canvas.getContext('2d');
tmp_canvas.id = 'tmp_canvas';
tmp_canvas.width = parseInt(sketch_style.getPropertyValue('width'));
tmp_canvas.height = parseInt(sketch_style.getPropertyValue('height'));
sketch.appendChild(tmp_canvas);
var canvas = document.createElement('canvas');
var context = canvas.getContext('2d');
canvas.id = 'paint';
canvas.width = parseInt(sketch_style.getPropertyValue('width'));
canvas.height = parseInt(sketch_style.getPropertyValue('height'));
sketch.appendChild(canvas);
tmp_canvas.addEventListener('mousedown', mousedown, false);
tmp_canvas.addEventListener('mousemove', mousemove, false);
tmp_canvas.addEventListener('mouseup', mouseup, false);
function mousemove(e) {
if (clicks == 1) {
x = e.layerX - this.offsetLeft;
y = e.layerY - this.offsetTop;
showRect(x, y);
}
}
function showRect(x, y) {
tmp_ctx.clearRect(0, 0, canvas.width, canvas.height); // clear canvas
tmp_ctx.beginPath();
var width = x - point[0].x;
var height = y - point[0].y;
tmp_ctx.rect(point[0].x, point[0].y, width, height);
tmp_ctx.stroke();
}
function mousedown(e) {
x = e.layerX - this.offsetLeft;
y = e.layerY - this.offsetTop;
point.push({
x,
y
});
clicks++;
};
function mouseup() {
context.drawImage(tmp_canvas, 0, 0);
clicks = 0;
point.length = 0;
}
html, body {
width: 100% ;
height: 100% ;
}
#sketch {
border: 10px solid gray;
height: 100% ;
position: relative;
}
#tmp_canvas {
position: absolute;
left: 0px;
right: 0;
bottom: 0;
top: 0;
cursor: crosshair;
}
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<div id="sketch">
</div>
</body>
</html>
Try to do in temporary canvas and redraw all in main.
jsfiddle:-https://jsfiddle.net/durga598/v0m06faz/
I'm assuming that the foo() function is being called for every frame, either through setInterval or requestAnimationFrame. If my assumption is right, the reason why your previously drawn square disappears is because you are only storing the x and y coordinates of one rectangle, and every time you click on the canvas again, it gets overwritten by the new values for the new rectangle.
To solve your problem, you should store the x and y coordinates as well as the dimensions of the square on mouseup. These coordinates can be stored in an array.
var squares = [];
this.mouseup = function (ev) {
// other code
var square = {
x: last_mousex,
y: last_mousey,
width: mousex - last_mousex,
height: mousey - last_mousey
};
squares.push(square);
};
Now every time you draw the square, draw the squares stored in the squares array first.
this.mousemove = function (ev) {
if (tool.started && checkboxSquare.checked) {
// other code
context.clearRect(0, 0, canvas.width, canvas.height); // clear canvas
// draw each square in the squares array after clearning the canvas
squares.forEach(function(square) {
context.beginPath();
context.rect(square.x, square.y, square.width, square.height);
});
context.beginPath();
var width = mousex - last_mousex;
var height = mousey - last_mousey;
context.rect(last_mousex, last_mousey, width, height);
context.stroke();
}
};
You'll see some code repetitions in drawing the squares, it's a good opportunity to abstract it into a separate function.

Drawing multiple polygons in js

I'm using this code, and it works with charm - It draws a polygon (with canvas) on a given picture, however I'm struggling to modify code, so that it would enable me to draw multiple polygons instead of just one.
I'm a noob to js and haven't found yet anything that might solve it, I'd appreciate any help/hint in some direction.
Code:
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" type="text/css" href="main.css">
<script type="text/javascript" src="https://code.jquery.com/jquery-2.1.4.js"></script>
<script>
//radius of click around the first point to close the draw
var END_CLICK_RADIUS = 15;
//the max number of points of your polygon
var MAX_POINTS = 8;
var mouseX = 0;
var mouseY = 0;
var isStarted = false;
var points = null;
var canvas = null;
window.onload = function() {
background = document.getElementById('justanimage');
//initializing canvas and draw color
canvas = document.getElementById("canvas");
ctx = canvas.getContext("2d");
changeColor("blue");
image = new Image();
image.onload = function() {
ctx.drawImage(image, 0, 0, canvas.width, canvas.height);
};
image.src = 'justanimage.gif';
canvas.addEventListener("click", function(e) {
var x = e.clientX-canvas.offsetLeft;
var y = e.clientY-canvas.offsetTop;
if(isStarted) {
//drawing the next line, and closing the polygon if needed
if(Math.abs(x - points[0].x) < END_CLICK_RADIUS && Math.abs(y - points[0].y) < END_CLICK_RADIUS) {
isStarted = false;
} else {
points[points.length] = new Point(x, y);
if(points.length >= MAX_POINTS) {
isStarted = false;
}
}
} else if(points == null) {
//opening the polygon
points = new Array();
points[0] = new Point(x, y);
isStarted = true;
}
}, false);
//we just save the location of the mouse
canvas.addEventListener("mousemove", function(e) {
mouseX = e.clientX - canvas.offsetLeft;
mouseY = e.clientY - canvas.offsetTop;
}, false);
//refresh time
setInterval("draw();", 5);
}
//object representing a point
function Point(x, y) {
this.x = x;
this.y = y;
}
//resets the application
function reset() {
isStarted = false;
points = null;
document.getElementById("coordinates").innerHTML = " ";
}
//displays coordinates of the the point list
function save() {
if(points == null) {
alert("No picture!");
} else {
var s = "";
for(var a in points) {
//inversing y axis by (canvas.height - points[a].y)
s += "(" + points[a].x + "," + (canvas.height - points[a].y) + ")\n";
}
document.getElementById("coordinates").innerHTML = s + '\n';
}
}
//draws the current shape
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.beginPath();
if(points != null && points.length > 0) {
ctx.moveTo(points[0].x, points[0].y);
for(i = 1 ; i < points.length ; i++) {
ctx.lineTo(points[i].x, points[i].y);
}
if(isStarted) {
ctx.lineTo(mouseX, mouseY);
} else {
ctx.lineTo(points[0].x, points[0].y);
}
}
ctx.drawImage(image, 0, 0, canvas.width, canvas.height);
ctx.stroke();
}
</script>
</head>
<body>
<div id="outer">
<canvas id="canvas" width=300 height=300; ></canvas>
</div>
<p id="coordinates"> </p>
<input type="button" value="Save" onclick="save();" />
<input type="button" value="Reset" onclick="reset();" />
</body>
</html>
Basically this solution adds a new array polygons, which keeps all polygons. The points array is now included in the polygons.
var polygons = [];
window.onload = function () {
// ...
canvas.addEventListener("click", function (e) {
// ...
if (isStarted) {
//drawing the next line, and closing the polygon if needed
if (Math.abs(x - polygons[polygons.length - 1][0].x) < END_CLICK_RADIUS && Math.abs(y - polygons[polygons.length - 1][0].y) < END_CLICK_RADIUS) {
isStarted = false;
} else {
polygons[polygons.length - 1].push(new Point(x, y));
if (polygons[polygons.length - 1].length >= MAX_POINTS) {
isStarted = false;
}
}
} else {
//opening the polygon
polygons.push([new Point(x, y)]);
isStarted = true;
}
}, false);
// ...
}
function draw() {
ctx.drawImage(image, 0, 0, canvas.width, canvas.height);
polygons.forEach(function (points, i) {
ctx.beginPath();
points.forEach(function (p, j) {
if (j) {
ctx.lineTo(p.x, p.y);
} else {
ctx.moveTo(p.x, p.y);
}
});
if (i + 1 === polygons.length && isStarted) { // just the last one
ctx.lineTo(mouseX, mouseY);
} else {
ctx.lineTo(points[0].x, points[0].y);
}
ctx.stroke();
});
}
(Another solution might be to save the image with the last polygon and use it again for the new polygon.)
Working example:
//radius of click around the first point to close the draw
var END_CLICK_RADIUS = 15;
//the max number of points of your polygon
var MAX_POINTS = 8;
var mouseX = 0;
var mouseY = 0;
var isStarted = false;
var polygons = [];
var canvas = null;
var ctx;
var image;
window.onload = function () {
var background = document.getElementById('justanimage');
//initializing canvas and draw color
canvas = document.getElementById("canvas");
ctx = canvas.getContext("2d");
//changeColor("blue"); // <-- is missing!
image = new Image();
image.onload = function () {
ctx.drawImage(image, 0, 0, canvas.width, canvas.height);
};
image.src = 'http://lorempixel.com/10/10/';
canvas.addEventListener("click", function (e) {
var x = e.clientX - canvas.offsetLeft;
var y = e.clientY - canvas.offsetTop;
if (isStarted) {
//drawing the next line, and closing the polygon if needed
if (Math.abs(x - polygons[polygons.length - 1][0].x) < END_CLICK_RADIUS && Math.abs(y - polygons[polygons.length - 1][0].y) < END_CLICK_RADIUS) {
isStarted = false;
} else {
polygons[polygons.length - 1].push(new Point(x, y));
if (polygons[polygons.length - 1].length >= MAX_POINTS) {
isStarted = false;
}
}
} else {
//opening the polygon
polygons.push([new Point(x, y)]);
isStarted = true;
}
}, false);
//we just save the location of the mouse
canvas.addEventListener("mousemove", function (e) {
mouseX = e.clientX - canvas.offsetLeft;
mouseY = e.clientY - canvas.offsetTop;
}, false);
//refresh time
setInterval("draw();", 5);
}
//object representing a point
function Point(x, y) {
this.x = x;
this.y = y;
}
//resets the application
function reset() {
isStarted = false;
points = null;
document.getElementById("coordinates").innerHTML = " ";
}
//draws the current shape
function draw() {
ctx.drawImage(image, 0, 0, canvas.width, canvas.height);
polygons.forEach(function (points, i) {
ctx.beginPath();
points.forEach(function (p, j) {
if (j) {
ctx.lineTo(p.x, p.y);
} else {
ctx.moveTo(p.x, p.y);
}
});
if (i + 1 === polygons.length && isStarted) { // just the last one
ctx.lineTo(mouseX, mouseY);
} else {
ctx.lineTo(points[0].x, points[0].y);
}
ctx.stroke();
});
}
<canvas id="canvas" width="500" height="500"></canvas>
<img id="justanimage" />

how to remove a unconnected points when i mouseout from canvas

i need to remove unconnected points when i mouseout from canvas. I just draw lines when mousemove using moveTo and LineTo. when mouseout from canvas have to omit the unconnected points.
Here is code for jquery:
<script src="http://code.jquery.com/jquery-1.9.1.js"></script>
<script src="http://code.jquery.com/jquery.js"></script>
<script type="text/javascript">
$(function() {
var canvas = $('#canvas');
var context = canvas.get(0).getContext("2d");
var clicked = false;
var b=0;
var storedLines = [];
var storedLine = {};
var mouse = {
x: -1,
y: -1
}
var parentOffset = $('#canvas').offset();
canvas.click(function(e) {
if (b==1)
{
$(this).unbind(e);
}
else
{
clicked = true;
mouse.x = e.pageX - parentOffset.left;
mouse.y = e.pageY - parentOffset.top;
context.moveTo(mouse.x, mouse.y);
if (clicked) {
storedLines.push({
startX: storedLine.startX,
startY: storedLine.startY,
endX: mouse.x,
endY: mouse.y
});
}
storedLine.startX = mouse.x;
storedLine.startY = mouse.y;
$(this).mousemove(function(k) {
context.clearRect(0, 0, 960, 500);
context.beginPath();
context.strokeStyle = "blue";
for (var i = 0; i < storedLines.length; i++) {
var v = storedLines[i];
context.moveTo(v.startX, v.startY);
context.lineTo(v.endX, v.endY);
context.stroke();
}
context.moveTo(mouse.x, mouse.y);
context.lineTo(k.pageX - parentOffset.left, k.pageY - parentOffset.top);
context.stroke();
context.closePath();
});
}
});
$('#canvas').mouseout(function(e){
$(this).unbind("mousemove");
b=1;
});
});
HTML code:
<html>
<body>
<canvas id="canvas" width=600 height=600 ></canvas>
</body>
</html>
First thing to do is to clarify the code : Have one part that deals only with the mouse, and another part that deals only with the lines.
This way you will have a much better view on what will happen on each event.
I started a bit to clarify the code, you should even make a class handling lines (which will be very useful if you handle several of them).
jsbin is here : http://jsbin.com/eseTODo/2/edit?js,output
var canvas = $('#canvas');
var context = canvas.get(0).getContext("2d");
// -----------------------------------------
// Mouse
var clicked = false;
var onCanvas = false;
var mouse = {
x: -1,
y: -1
}
var parentOffset = $('#canvas').offset();
canvas.mousedown(function (e) {
clicked = true;
if (!onCanvas) return;
mouse.x = e.pageX - parentOffset.left;
mouse.y = e.pageY - parentOffset.top;
addPoint(mouse.x, mouse.y);
clearScreen();
drawLines();
});
canvas.mouseup(function (e) {
clicked = false;
if (!onCanvas) return;
});
canvas.mousemove(function (e) {
if (!onCanvas) return;
clearScreen();
drawLines();
drawPendingLine(e.pageX - parentOffset.left,
e.pageY - parentOffset.top);
});
canvas.mouseout(function (e) {
onCanvas = false;
clearScreen();
drawLines();
clearLines();
});
canvas.mouseenter(function (e) {
onCanvas = true;
});
// -----------------------------------------
// Lines
var storedLines = [];
var storedLine = {};
var startedALine = false;
function clearLines() {
storedLines.length = 0;
startedALine = false;
}
function addPoint(x, y) {
if (startedALine) {
storedLines.push({
startX: storedLine.startX,
startY: storedLine.startY,
endX: x,
endY: y
});
}
startedALine = true;
storedLine.startX = x;
storedLine.startY = y
}
function drawLines() {
context.strokeStyle = "blue";
if (!startedALine) return;
if (!storedLines.length) return;
for (var i = 0; i < storedLines.length; i++) {
var v = storedLines[i];
context.beginPath();
context.moveTo(v.startX, v.startY);
context.lineTo(v.endX, v.endY);
context.stroke();
context.closePath();
}
context.stroke();
}
function drawPendingLine(lastX, lastY) {
if (!startedALine) return;
context.beginPath();
context.strokeStyle = "green";
context.moveTo(storedLine.startX, storedLine.startY);
context.lineTo(lastX, lastY);
context.stroke();
context.closePath();
}
function clearScreen() {
context.clearRect(0, 0, 600, 600);
}
Can't you set a flag like
var hasLeftCanvas = false;
and set it to true when you leave the canvas?
canvas.onmouseleave = function() {
hasLeftCanvas = true;
}
and then, in your script:
$(this).mousemove(function(k) {
if(!hasLeftCanvas) {
context.clearRect(0, 0, 960, 500);
context.beginPath();
context.strokeStyle = "blue";
for (var i = 0; i < storedLines.length; i++) {
var v = storedLines[i];
context.moveTo(v.startX, v.startY);
context.lineTo(v.endX, v.endY);
context.stroke();
}
context.moveTo(mouse.x, mouse.y);
context.lineTo(k.pageX - parentOffset.left, k.pageY - parentOffset.top);
context.stroke();
context.closePath();
}
});
remember to set it back to false when the cursor re enters the canvas

Categories