RXJS draw line on html5 canvas - javascript

I'm trying to achieve the same effect I'm posting here using Reactive Extensions for Javascript (RX-JS).
I'm a bit puzzled on how to do it.
Here is the page:
<!DOCTYPE html>
<html>
<head>
<title>drag and drop</title>
</head>
<style type="text/css">
canvas {
border:1px solid steelblue;
background-color: whitesmoke;
}
</style>
<body>
<canvas id="canvas" width=300 height=300></canvas>
<script type="text/javascript" src="http://code.jquery.com/jquery-1.11.0.min.js"></script>
<script type="text/javascript">
$(function() {
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var canvasOffset = $("#canvas").offset();
var offsetX = canvasOffset.left;
var offsetY = canvasOffset.top;
var drawing = false;
var mouseX = 0;
var mouseY = 0;
function handleMouseDown(e) {
mouseX = parseInt(e.clientX - offsetX);
mouseY = parseInt(e.clientY - offsetY);
drawing= true;
}
function handleMouseUp(e) {
drawing = false;
}
function handleMouseMove(e) {
if(drawing){
mouseeX = parseInt(e.clientX - offsetX);
mouseeY = parseInt(e.clientY - offsetY);
$("#movelog").html("Move: " + mouseX + " / " + mouseY);
var ctx = canvas.getContext("2d");
// some cleanup code
ctx.save();
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.restore();
ctx.beginPath();
ctx.moveTo(mouseX,mouseY);
ctx.lineTo(mouseeX,mouseeY);
ctx.stroke();
}
}
$("#canvas").mousedown(function(e) {
handleMouseDown(e);
});
$("#canvas").mousemove(function(e) {
handleMouseMove(e);
});
$("#canvas").mouseup(function(e) {
handleMouseUp(e);
});
});
</script>
</body>
</html>
I think I should create observables for the mouseDown, mouseMove and mouseUp events.
var mouseDown = Rx.Observable.fromEvent(canvas, 'mousedown');
var mouseMove = Rx.Observable.fromEvent(canvas, 'mousemove');
var mouseUp = Rx.Observable.fromEvent(canvas, 'mouseup');
but I don't know how to combine them. I think should start observing the mousedown, then collect all the moves until the mouseup is raised, and in the whil redraw the line from the starting point to the current point where the mouse is during the mousemove.
Do you have any ideas? Thanks a lot.
°°°°°°°°°°°°°°°°°°°°°°EDIT°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
Here is my code after the answer by Brandon:
$(function() {
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext("2d");
var canvasOffset = $("#canvas").offset();
var offsetX = canvasOffset.left;
var offsetY = canvasOffset.top;
var mouseDown = Rx.Observable.fromEvent($("#canvas"), 'mousedown');
var mouseMove = Rx.Observable.fromEvent($("#canvas"), 'mousemove');
var mouseUp = Rx.Observable.fromEvent($("#canvas"), 'mouseup');
// keep a reference to the pisition when the mouse down was fired
// then flatten the stream with concatAll
var traceLineStream = mouseDown.map(function(md) {
var movesFromMouseDown = mouseMove.takeUntil(mouseUp);
var movesFromMouseDownAndMouseDown = movesFromMouseDown.map(function(mm) {
return {
mouseDownPoint: md,
mouseMovePoint: mm
}
});
return movesFromMouseDownAndMouseDown;
}).concatAll();
var subscription = traceLineStream.subscribe(
function(y) {
var mouseDown = y.mouseDownPoint;
var mouseMove = y.mouseMovePoint;
var mouseDownX = parseInt(mouseDown.clientX - offsetX);
var mouseDownY = parseInt(mouseDown.clientY - offsetY);
var mouseMoveX = parseInt(mouseMove.clientX - offsetX);
var mouseMoveY = parseInt(mouseMove.clientY - offsetY);
ctx.save();
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.restore();
ctx.beginPath();
ctx.moveTo(mouseDownX, mouseDownY);
ctx.lineTo(mouseMoveX, mouseMoveY);
ctx.stroke();
},
function(e) {
console.log('onError: ' + e.message);
},
function() {
console.log('onCompleted');
});
});

First, combined the streams so you have a stream of events that represent a single drag.
var drag = mouseDown.first().concat(mouseMove.takeUntil(mouseUp));
Next, project this stream of events into a stream of previous,current tuples.
var moves = drag
.scan({}, function(acc, x) {
return { previous: acc.current, current: x };
})
.skip(1);
Now we have a stream that only works the first time. When it ends, we want to start listening for the next drag:
var allMoves = moves.repeat();
Finally, subscribe:
allMoves.subscribe(function (move) {
var mouseX = move.previous.clientX - offsetX,
mouseY = move.previous.clientY - offsetY,
mouseeX = move.current.clientX - offsetX,
mouseeY = move.current.clientY - offsetY,
...
});
Putting it all together without all of the intermediate variables:
mouseDown
.first()
.concat(mouseMove.takeUntil(mouseUp))
.scan({}, function(acc, x) {
return { previous: acc.current, current: x };
})
.skip(1)
.repeat()
.subscribe(function (move) {
var mouseX = move.previous.clientX - offsetX,
mouseY = move.previous.clientY - offsetY,
mouseeX = move.current.clientX - offsetX,
mouseeY = move.current.clientY - offsetY,
...
});

Related

Drawing multiple rectangles on canvas without clearing the back image

I am trying to draw multiple rectangles on canvas. I am able to do it except its not clearing rectangles as the mouse moves.
And when i try to clear rectangle using clearRect then the back image on canvas is also gets cleared. So I have commented out //ctx.clearRect(0, 0, canvas.width, canvas.height); in the code below
I have gone through several SO posts with similar questions but doesn't seems work
$(function(){
var canvas = document.getElementById('myCanvas');
if (canvas.getContext){
var ctx = canvas.getContext('2d');
ctx.fillText("Sample String", 20, 50);
}
var ctx = canvas.getContext('2d');
//Variables
var canvasx = $(canvas).offset().left;
var canvasy = $(canvas).offset().top;
var last_mousex = last_mousey = 0;
var mousex = mousey = 0;
var mousedown = false;
//Mousedown
$(canvas).on('mousedown', function (e) {
last_mousex = parseInt(e.clientX - canvasx);
last_mousey = parseInt(e.clientY - canvasy);
mousedown = true;
});
//Mouseup
$(canvas).on('mouseup', function (e) {
mousedown = false;
});
//Mousemove
$(canvas).on('mousemove', function (e) {
mousex = parseInt(e.clientX - canvasx);
mousey = parseInt(e.clientY - canvasy);
if (mousedown) {
//ctx.clearRect(0, 0, canvas.width, canvas.height);
var width = mousex - last_mousex;
var height = mousey - last_mousey;
ctx.beginPath();
ctx.rect(last_mousex, last_mousey, width, height);
ctx.strokeStyle = 'black';
ctx.lineWidth = 1;
ctx.stroke();
}
//Output
$('#results').html('current: ' + mousex + ', ' + mousey + '<br/>last: ' + last_mousex + ', ' + last_mousey + '<br/>mousedown: ' + mousedown);
});
})
canvas { border: 1px solid black; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<h3>
Use mouse to draw multiple rectangles with in the canvas
</h3>
<canvas id="myCanvas"></canvas>
<div id="results">
</div>
your mistake was you cleared all the canvas:
ctx.clearRect(0, 0, canvas.width, canvas.height);
instead of clearing just the area you drew before:
ctx.clearRect(prev_x-1, prev_y-1, prev_w+2, prev_h+2);
I wrote the basic idea here, but you need to add some code to clear the area depends on the direction the mouse was, and moving to (try to move your mouse to each of the corners and see what happens).
$("#clear").click(function(){
var canvas = document.getElementById('myCanvas');
var ctx = canvas.getContext('2d');
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillText("Sample String", 20, 50);
});
$(function(){
var canvas = document.getElementById('myCanvas');
if (canvas.getContext){
var ctx = canvas.getContext('2d');
ctx.fillText("Sample String", 20, 50);
}
var ctx = canvas.getContext('2d');
//Variables
var canvasx = $(canvas).offset().left;
var canvasy = $(canvas).offset().top;
var last_mousex = last_mousey = w = h = 0;
var prev_x = prev_y = prev_w = prev_h = 0;
var mousex = mousey = 0;
var mousedown = false;
//Mousedown
$(canvas).on('mousedown', function (e) {
last_mousex = parseInt(e.clientX - canvasx);
last_mousey = parseInt(e.clientY - canvasy);
mousedown = true;
});
//Mouseup
$(canvas).on('mouseup', function (e) {
w = h = 0;
mousedown = false;
});
//Mousemove
$(canvas).on('mousemove', function (e) {
mousex = parseInt(e.clientX - canvasx);
mousey = parseInt(e.clientY - canvasy);
if (mousedown) {
prev_x = last_mousex;
prev_y = last_mousey;
prev_w = w;
prev_h = h;
ctx.clearRect(prev_x-1, prev_y-1, prev_w+2, prev_h+2);
w = mousex - last_mousex;
h = mousey - last_mousey;
ctx.beginPath();
ctx.rect(last_mousex, last_mousey, w, h);
ctx.strokeStyle = 'black';
ctx.lineWidth = 1;
ctx.stroke();
}
//Output
$('#results').html('current: ' + mousex + ', ' + mousey + '<br/>last: ' + last_mousex + ', ' + last_mousey + '<br/>mousedown: ' + mousedown);
});
})
canvas { border: 1px solid black; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<h3>
Use mouse to draw multiple rectangles with in the canvas
</h3>
<button id="clear">clear</button>
<br />
<canvas id="myCanvas"></canvas>
<div id="results">
</div>
I think you can come to another approach
By using mousedown event only then save all rectangle to an array variable
Then you can clear and redraw the whole canvas with the saved variable
var shapes = [];
canva.addEventListener('mousedown', mouseDownListener);
class Rectangle() {
public ctx, x, y, w, h;
public Rectangle(ctx, x, y, w, h) {
this.ctx = ctx;
this.x = x;
this.y = y;
this.w = w;
this.h = h;
}
public draw() {
// draw using ctx here
}
}
function mouseDownListener() {
// create rectable
var rectangle = new Rectangle(ctx, x, y, width, height);
// save rectangle to an array
shapes.push(rectangle);
// redraw canvas
redraw();
}
function redraw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
// draw all rectangle
shapes.forEach(function(shape) {
// draw shape
shape.draw();
})
}

How to store boxes from canvas?

I have a canvas, which will display videos taken from file upload on the first layer, and the second layer allow rectangles to be drawn. As I am trying to annotate videos and not still images, I will need the annotations to be saved then cleared, and then redrawn again and again until the video is over. However, I have no clue how I should do so as I am relatively new to JavaScript.
This is the code I have for now for the drawing of the annotations:
// Drawing boxes
function handleMouseDown(e) {
mouseX = parseInt(e.clientX - offsetX);
mouseY = parseInt(e.clientY - offsetY);
console.log(mouseX, mouseY);
$("#downlog").html("Down: " + mouseX + " / " + mouseY);
// Put your mousedown stuff here
if (mouseIsDown) {
console.log('1');
canvas2.style.cursor = "crosshair";
mouseIsDown = false;
mouseIsUp = false;
console.log(mouseIsDown);
} else {
handleMouseUp();
}
mouseIsDown = false;
mouseIsUp = true;
}
function handleMouseUp(e) { // array? user input?
mouseIsDown = false;
startX = parseInt(e.clientX - offsetX);
startY = parseInt(e.clientY - offsetY);
/*if (mouseIsUp) {
console.log('2');*/
draw();
}
function draw() {
/* context2.clearRect(0, 0, canvas2.width, canvas2.height);*/
context2.beginPath();
context2.rect(startX, startY, mouseX - startX, mouseY - startY);
context2.strokeStyle = "limegreen";
context2.lineWidth = 2;
context2.stroke();
canvas2.style.cursor = "default";
}
$("#canvas2").mousedown(function(e) {
handleMouseDown(e);
});
$("#canvas2").mouseup(function(e) {
handleMouseUp(e);
});
function clearcanvas()
{
var canvas2 = document.getElementById('canvas2'),
context2 = canvas2.getContext("2d");
context2.clearRect(0, 0, canvas2.width, canvas2.height);
}
I will really appreciate any help, thank you!
Please read the comments. I hope my code is clear enough.
let c = document.getElementById("canvas");
let ctx = c.getContext("2d");
// the array of all rectangles
let rectsRy = [];
// the actual rectangle, the one that is beeing drawn
let o={};
// a variable to store the mouse position
let m = {},
// a variable to store the point where you begin to draw the rectangle
start = {};
// a boolean
let isDrawing = false;
function handleMouseDown(e) {
start = oMousePos(c, e);
isDrawing = true;
//console.log(start.x, start.y);
c.style.cursor = "crosshair";
}
function handleMouseMove(e) {
if(isDrawing){
m = oMousePos(c, e);
draw();
}
}
function handleMouseUp(e) { // array? user input?
c.style.cursor = "default";
isDrawing = false;
// push a new rectangle into the rects array
rectsRy.push({x:o.x,y:o.y,w:o.w,h:o.h});
}
function draw() {
o.x = start.x;
o.y = start.y;
o.w = m.x - start.x;
o.h = m.y - start.y;
clearcanvas();
// draw all the rectangles saved in the rectsRy
rectsRy.map(r => {drawRect(r)})
// draw the actual rectangle
drawRect(o);
}
c.addEventListener("mousedown", handleMouseDown);
c.addEventListener("mousemove", handleMouseMove);
c.addEventListener("mouseup", handleMouseUp);
function clearcanvas(){
ctx.clearRect(0, 0, c.width, c.height);
}
function drawRect(o){
ctx.strokeStyle = "limegreen";
ctx.lineWidth = 2;
ctx.beginPath(o);
ctx.rect(o.x,o.y,o.w,o.h);
ctx.stroke();
}
// a function to detect the mouse position
// the function returns an object
function oMousePos(canvas, evt) {
let ClientRect = canvas.getBoundingClientRect();
return {
x: Math.round(evt.clientX - ClientRect.left),
y: Math.round(evt.clientY - ClientRect.top)
}
}
canvas{border:1px solid #d9d9d9;}
<canvas id="canvas" width="600" height="300">

how to draw control on canvas using mobile touch while having scroll on screen

I'm trying to create a mobile app in which user can draw multiple controls (rectangle) on a canvas.
It works fine if canvas fits the screen, but the controls are not drawn well when I increase the canvas height and width out of the screen (a scroll appear when i increase the size of canvas more than the size of mobile screen).
How to fix this in scroll mode?
Use Chrome Inspect mobile view (iPhone 6/7/8) to test this code
Use Pointer drag to draw controls
<html>
<body>
<canvas id="canvas_transparent" height="3000" width="1000" style="border: 1px solid black;"></canvas>
</body>
</html>
<script type="text/javascript">
var canvas = document.getElementById("canvas_transparent");
var ctx = canvas.getContext("2d");
var endX;
var startX;
var endY;
var startY;
var positions = [];
//--------------------------------------------------------
canvas.addEventListener("touchstart", function(e) {
mousePos = getTouchPos(canvas, e);
startX = mousePos.x;
startY = mousePos.y;
var touch = e.touches[0];
var mouseEvent = new MouseEvent("mousedown", {
clientX: touch.clientX,
clientY: touch.clientY
});
canvas.dispatchEvent(mouseEvent);
}, false);
//---------------------------------------------------------
canvas.addEventListener("touchend", function(e) {
positions.push({
s_x: startX,
s_y: startY,
e_x: endX,
e_y: endY
});
drawAllControls();
console.log(positions);
var mouseEvent = new MouseEvent("mouseup", {});
canvas.dispatchEvent(mouseEvent);
}, false);
//---------------------------------------------------------
canvas.addEventListener("touchmove", function(e) {
var touch = e.touches[0];
endX = touch.clientX;
endY = touch.clientY;
drawSquare()
var mouseEvent = new MouseEvent("mousemove", {
clientX: touch.clientX,
clientY: touch.clientY
});
canvas.dispatchEvent(mouseEvent);
}, false);
//--------------------------------------------------------
// Get the position of a touch relative to the canvas
function getTouchPos(canvasDom, touchEvent) {
var rect = canvasDom.getBoundingClientRect();
return {
x: touchEvent.touches[0].clientX - rect.left,
y: touchEvent.touches[0].clientY - rect.top
};
}
//--------------------------------------------------------
function drawSquare() {
// creating a square
var w = endX - startX;
var h = endY - startY;
var offsetX = (w < 0) ? w : 0;
var offsetY = (h < 0) ? h : 0;
var width = Math.abs(w);
var height = Math.abs(h);
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.beginPath();
ctx.rect(startX + offsetX, startY + offsetY, width, height);
ctx.fillStyle = "#222222";
ctx.fill();
ctx.lineWidth = 2;
ctx.strokeStyle = 'black';
ctx.stroke();
}
//----------------------------------------------------------
function drawAllControls() {
for (var i = 0; i < positions.length; i++) {
var w = positions[i].e_x - positions[i].s_x;
var h = positions[i].e_y - positions[i].s_y;
var offsetX = (w < 0) ? w : 0;
var offsetY = (h < 0) ? h : 0;
var width = Math.abs(w);
var height = Math.abs(h);
ctx.beginPath();
ctx.rect(positions[i].s_x + offsetX, positions[i].s_y + offsetY, width, height);
ctx.fillStyle = "#222222";
ctx.fill();
ctx.lineWidth = 2;
ctx.strokeStyle = 'black';
ctx.stroke();
}
}
</script>
clientX/clientY do not factor in scroll offset. I think you want pageX/pageY
https://developer.mozilla.org/en-US/docs/Web/API/Touch
// Get the position of a touch relative to the canvas
function getTouchPos(canvasDom, touchEvent) {
var rect = canvasDom.getBoundingClientRect();
return {
x: touchEvent.touches[0].pageX - rect.left,
y: touchEvent.touches[0].pageY - rect.top
};
}
Hi i think this will resolve your problem. Put this in the head tag
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
working link page

Draw multiple rectangle on canvas

I am trying to add multiple rectangle on canvas on mouse move. But when I am drawing rectangle on image, back image on canvas is also cleared. I don't want to remove it. I want multiple rectangle on canvas without clearing canvas image. Please check below JavaScript code.
var canvas = document.getElementById('canvasarea'),
context = canvas.getContext('2d');
base_image = new Image();
base_image.src = 'http://www.html5canvastutorials.com/demos/assets/darth-vader.jpg'; //Load Image ;
base_image.onload = function(){
context.drawImage(base_image,0,0);
}
var strokeWidth = 4;
drawCount = 1;
canvas.addEventListener("mousemove", function (e) {
drawRectangleOnCanvas.handleMouseMove(e);
}, false);
canvas.addEventListener("mousedown", function (e) {
drawRectangleOnCanvas.handleMouseDown(e);
}, false);
canvas.addEventListener("mouseup", function (e) {
drawRectangleOnCanvas.handleMouseUp(e);
}, false);
canvas.addEventListener("mouseout", function (e) {
drawRectangleOnCanvas.handleMouseOut(e);
}, false);
function reOffset(){
var BB=canvas.getBoundingClientRect();
recOffsetX=BB.left;
recOffsetY=BB.top;
}
var recOffsetX,recOffsetY;
reOffset();
window.onscroll=function(e){ reOffset(); }
window.onresize=function(e){ reOffset(); }
var isRecDown=false;
var startX,startY;
var rects=[];
var newRect;
var drawRectangleOnCanvas={
handleMouseDown:function(e){
// tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
startX=parseInt(e.clientX-recOffsetX);
startY=parseInt(e.clientY-recOffsetY);
// Put your mousedown stuff here
isRecDown=true;
},
handleMouseUp:function(e){
// tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
mouseX=parseInt(e.clientX-recOffsetX);
mouseY=parseInt(e.clientY-recOffsetY);
// Put your mouseup stuff here
isRecDown=false;
//if(!willOverlap(newRect)){
rects.push(newRect);
//}
drawRectangleOnCanvas.drawAll();
},
drawAll:function(){
context.clearRect(0, 0, canvas.width, canvas.height);
context.lineWidth=strokeWidth;
//context.strokeStyle=$('div.dropdown-menu').find('.selected').attr('data-color');
for(var i=0;i<rects.length;i++){
var r=rects[i];
context.strokeStyle = r.color;
context.globalAlpha=1;
context.strokeRect(r.left,r.top,r.right-r.left,r.bottom-r.top);
context.beginPath();
context.arc(r.left, r.top, 15, 0, Math.PI*2, true);
context.closePath();
context.fillStyle = r.color;
context.fill();
var text = drawCount;
context.fillStyle = "#fff";
var font = "bold " + 2 +"px serif";
context.font = font;
var width = context.measureText(text).width;
var height = context.measureText("w").width; // this is a GUESS of height
context.fillText(text, r.left - (width/2) ,r.top + (height/2));
}
},
handleMouseOut:function(e){
// tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
mouseX=parseInt(e.clientX-recOffsetX);
mouseY=parseInt(e.clientY-recOffsetY);
// Put your mouseOut stuff here
isRecDown=false;
},
handleMouseMove:function(e){
if(!isRecDown){return;}
// tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
mouseX=parseInt(e.clientX-recOffsetX);
mouseY=parseInt(e.clientY-recOffsetY);
newRect={
left:Math.min(startX,mouseX),
right:Math.max(startX,mouseX),
top:Math.min(startY,mouseY),
bottom:Math.max(startY,mouseY),
color:"#000000"
}
drawRectangleOnCanvas.drawAll();
context.strokeStyle = "#000000";
context.lineWidth = strokeWidth;
context.globalAlpha=1;
context.strokeRect(startX,startY,mouseX-startX,mouseY-startY);
}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<canvas id="canvasarea"></canvas>
After clearing the canvas you need to draw the image as well in your drawAll() method. context.drawImage(base_image, 0, 0);
var canvas = document.getElementById('canvasarea'),
context = canvas.getContext('2d');
var base_image = new Image();
base_image.src = 'http://www.html5canvastutorials.com/demos/assets/darth-vader.jpg'; //Load Image ;
base_image.onload = function() {
context.drawImage(base_image, 0, 0);
}
function drawImage() {
}
var strokeWidth = 4;
drawCount = 1;
canvas.addEventListener("mousemove", function(e) {
drawRectangleOnCanvas.handleMouseMove(e);
}, false);
canvas.addEventListener("mousedown", function(e) {
drawRectangleOnCanvas.handleMouseDown(e);
}, false);
canvas.addEventListener("mouseup", function(e) {
drawRectangleOnCanvas.handleMouseUp(e);
}, false);
canvas.addEventListener("mouseout", function(e) {
drawRectangleOnCanvas.handleMouseOut(e);
}, false);
function reOffset() {
var BB = canvas.getBoundingClientRect();
recOffsetX = BB.left;
recOffsetY = BB.top;
}
var recOffsetX, recOffsetY;
reOffset();
window.onscroll = function(e) {
reOffset();
}
window.onresize = function(e) {
reOffset();
}
var isRecDown = false;
var startX, startY;
var rects = [];
var newRect;
var drawRectangleOnCanvas = {
handleMouseDown: function(e) {
// tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
startX = parseInt(e.clientX - recOffsetX);
startY = parseInt(e.clientY - recOffsetY);
// Put your mousedown stuff here
isRecDown = true;
},
handleMouseUp: function(e) {
// tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
mouseX = parseInt(e.clientX - recOffsetX);
mouseY = parseInt(e.clientY - recOffsetY);
// Put your mouseup stuff here
isRecDown = false;
//if(!willOverlap(newRect)){
rects.push(newRect);
//}
drawRectangleOnCanvas.drawAll();
},
drawAll: function() {
context.clearRect(0, 0, canvas.width, canvas.height);
context.drawImage(base_image, 0, 0);
context.lineWidth = strokeWidth;
for (var i = 0; i < rects.length; i++) {
var r = rects[i];
context.strokeStyle = r.color;
context.globalAlpha = 1;
context.strokeRect(r.left, r.top, r.right - r.left, r.bottom - r.top);
context.beginPath();
context.arc(r.left, r.top, 15, 0, Math.PI * 2, true);
context.closePath();
context.fillStyle = r.color;
context.fill();
var text = drawCount;
context.fillStyle = "#fff";
var font = "bold " + 2 + "px serif";
context.font = font;
var width = context.measureText(text).width;
var height = context.measureText("w").width; // this is a GUESS of height
context.fillText(text, r.left - (width / 2), r.top + (height / 2));
}
},
handleMouseOut: function(e) {
// tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
mouseX = parseInt(e.clientX - recOffsetX);
mouseY = parseInt(e.clientY - recOffsetY);
// Put your mouseOut stuff here
isRecDown = false;
},
handleMouseMove: function(e) {
if (!isRecDown) {
return;
}
// tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
mouseX = parseInt(e.clientX - recOffsetX);
mouseY = parseInt(e.clientY - recOffsetY);
newRect = {
left: Math.min(startX, mouseX),
right: Math.max(startX, mouseX),
top: Math.min(startY, mouseY),
bottom: Math.max(startY, mouseY),
color: "#000000"
}
drawRectangleOnCanvas.drawAll();
context.strokeStyle = "#000000";
context.lineWidth = strokeWidth;
context.globalAlpha = 1;
context.strokeRect(startX, startY, mouseX - startX, mouseY - startY);
}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<canvas id="canvasarea"></canvas>

Drawing a rectangle on Canvas

I am trying to create a simple canvas program where the user can consistently create new shapes. This one is just a basic rectangle creator (I am hoping to expand it more to circles, lines, and maybe even other stuff). Right now though I have created something that is working in a really weird way.
<html>
<head>
<meta chartset="utf-8">
<title>Dragging a square</title>
<script type="text/javascript">
var canvas, context, startX, endX, startY, endY;
var mouseIsDown = 0;
function init() {
canvas = document.getElementById("canvas");
context = canvas.getContext("2d");
canvas.addEventListener("mousedown", mouseDown, false);
canvas.addEventListener("mousemove", mouseXY, false);
document.body.addEventListener("mouseup", mouseUp, false);
}
function mouseUp() {
mouseIsDown = 0;
//mouseXY();
}
function mouseDown() {
mouseIsDown = 1;
startX = event.clientX;
startY = event.clientY;
mouseXY();
}
function mouseXY(eve) {
if (!eve) {
var eve = event;
}
endX = event.pageX - canvas.offsetLeft;
endY = event.pageY - canvas.offsetTop;
drawSquare();
}
function drawSquare() {
// creating a square
var width = Math.abs(startX - endX);
var height = Math.abs(startY - endY);
context.beginPath();
context.rect(startX, startY, width, height);
context.fillStyle = "yellow";
context.fill();
context.lineWidth = 7;
context.strokeStyle = 'black';
context.stroke();
}
</script>
</head>
<body onload="init()">
<canvas id="canvas" width="400" height="400" style="border: 1px solid black; cursor: pointer;"></canvas>
</body>
</html>
Sorry about the slightly weird formatting when I copy and pasted my code. I think the problem is my mouseXY function. What I want is the user to click somewhere on the canvas a drag the mouse to create a rectangle, when the user lets go that is the end of that operation and they can create a whole new rectangle right after. At this point the program kind of just lets me click and create a new rectangle but if I let go of the mouse button it doesn't stop, in fact I have to click again to make it stop which then creates a new rectangle. I am still very new to this and I am having a lot of trouble with this, I will continue to work on this and if I figure it out I will let the site know. Thank you and have a great day!
Well I got this to work (thanks to #Ken) but now I am trying to solve a new problem. I want to be able to put multiple rectangles on the canvas. I created a function that represents the Rectangle and then created a draw function within the rectangle function to draw out a rectangle. I created a new function called addShape() that ideally creates the rectangle object and pushes into an array called square and drawShapes() that is supposed to erase everything on the canvas and redraws everything. Here is what I have so far:
<html>
<head>
<meta chartset="utf-8">
<title>Dragging a square</title>
<script type="text/javascript">
function Rectangle(canvas, x, y, width, height,color) {
//this.context = canvas.getContext("2d");
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.color = color;
this.draw = function() {
this.context.globalAlpha = 0.85;
this.context.beginPath();
this.context.rect(this.x, this.y, this.width, this.height);
this.context.fillStyle = this.color;
this.context.strokeStyle = "black";
this.context.lineWidth = 1;
this.context.fill();
this.context.stroke();
};
};
// hold the canvas and context variable, as well as the
// starting point of X and Y and the end ones
var canvas, context, startX, endX, startY, endY;
var mouseIsDown = 0;
// An array that holds all the squares
var squares = [];
window.onload = function() {
canvas = document.getElementById("canvas");
context = canvas.getContext("2d");
canvas.addEventListener("mousedown", mouseDown, false);
canvas.addEventListener("mousemove", mouseXY, false);
canvas.addEventListener("mouseup", mouseUp, false);
}
function mouseUp(eve) {
if (mouseIsDown !== 0) {
mouseIsDown = 0;
var pos = getMousePos(canvas, eve);
endX = pos.x;
endY = pos.y;
//Square(); //update on mouse-up
addShape(); // Update on mouse-up
}
}
function mouseDown(eve) {
mouseIsDown = 1;
var pos = getMousePos(canvas, eve);
startX = endX = pos.x;
startY = endY = pos.y;
// Square(); //update
addShape();
}
function mouseXY(eve) {
if (mouseIsDown !== 0) {
var pos = getMousePos(canvas, eve);
endX = pos.x;
endY = pos.y;
//Square();
addShape();
}
}
function getMousePos(canvas, evt) {
var rect = canvas.getBoundingClientRect();
return {
x: evt.clientX - rect.left,
y: evt.clientY - rect.top
};
}
function addShape() {
var w = endX - startX;
var h = endY - startY;
var offsetX = (w < 0) ? w : 0;
var offsetY = (h < 0) ? h : 0;
var width = Math.abs(w);
var height = Math.abs(h);
var s = new Rectangle(startX + offsetX, startY + offsetY, width, height, "yellow");
squares.push(s);
// Update the display
drawShapes();
}
function drawShapes() {
context.clearRect(0,0,canvas.width,canvas.height);
for (var i = 0; i < squares.length; i++) {
var shape = squares[i];
shape.draw();
};
}
function clearCanvas() {
squares = [];
drawShapes();
}
</script>
</head>
<body onload="addShape()">
<canvas id="canvas" width="400" height="400" style="border: 1px solid black; cursor: pointer;"></canvas><br>
<button onclick="clearCanvas()">Clear Canvas</button>
</body>
</html>
I am pretty sure I broke the original code... thank you for any help!
You need to modify a couple of things in the code: (edit: there are many issues with this code. I went through some of them inline here, but haven't tested. If you put it in a fiddle it's easier for us to check)..
Fiddle
When mouse down occur initialize both start and end points. Call a common draw function that is not dependent on the event itself:
function mouseDown(eve) {
mouseIsDown = 1;
var pos = getMousePos(canvas, eve);
startX = endX = pos.x;
startY = endY = pos.y;
drawSquare(); //update
}
At mouse up, only register if isMouseDown is true, else this function will handle all incoming up-events (as you have attatched it to document, which is correct - window could have been used too):
function mouseUp(eve) {
if (mouseIsDown !== 0) {
mouseIsDown = 0;
var pos = getMousePos(canvas, eve);
endX = pos.x;
endY = pos.y;
drawSquare(); //update on mouse-up
}
}
Only draw if mouseisdown is true:
function mouseXY(eve) {
if (mouseIsDown !== 0) {
var pos = getMousePos(canvas, eve);
endX = pos.x;
endY = pos.y;
drawSquare();
}
}
In addition you will need to clear the previous area of the rectangle before drawing a new or else it won't show when you draw a bigger rectangle and then move the mouse back to draw a smaller one.
For simplicity you can do:
function drawSquare() {
// creating a square
var width = Math.abs(startX - endX);
var height = Math.abs(startY - endY);
context.clearRect(0, 0, context.width, context.height);
//or use fillRect if you use a bg color
context.beginPath();
context.rect(startX, startY, width, height);
context.fillStyle = "yellow";
context.fill();
context.lineWidth = 7;
context.strokeStyle = 'black';
context.stroke();
}
Use this for mouse position:
function getMousePos(canvas, evt) {
var rect = canvas.getBoundingClientRect();
return {
x: evt.clientX - rect.left,
y: evt.clientY - rect.top
};
}

Categories

Resources