I am trying to allow users to upload their own video which will then be displayed into a canvas. From there on, they can draw rectangles onto the canvas (to annotate for object detection), and clear the rectangles when they want to. However, what I want is that when the users draw the rectangles and play the video, the rectangles will follow an object on the video. Also, when the user wants to re-draw the rectangles to reposition them, they will clear the rectangles on the canvas - but the previously-drawn rectangles will already be saved.
This is the current code I have:
function update(){
context.drawImage(video,0,0,1580,700);
requestAnimationFrame(update); // wait for the browser to be ready to present another animation fram.
}
function readyToPlayVideo(event){ // this is a referance to the video
// the video may not match the canvas size so find a scale to fit
videoContainer.scale = Math.min(
canvas.width / this.videoWidth,
canvas.height / this.videoHeight);
videoContainer.ready = true;
// the video can be played so hand it off to the display function
requestAnimationFrame(updateCanvas);
// add instruction
/*document.getElementById("playPause").textContent = "Click video to play/pause.";*/
document.querySelector(".mute").textContent = "Mute";
}
function updateCanvas(){
context.clearRect(0,0,canvas.width,canvas.height);
// only draw if loaded and ready
if(videoContainer !== undefined && videoContainer.ready){
// find the top left of the video on the canvas
video.muted = muted;
var scale = videoContainer.scale;
var vidH = videoContainer.video.videoHeight;
var vidW = videoContainer.video.videoWidth;
var top = canvas.height / 2 - (vidH /2 ) * scale;
var left = canvas.width / 2 - (vidW /2 ) * scale;
// now just draw the video the correct size
context.drawImage(videoContainer.video, left, top, vidW * scale, vidH * scale);
/*if(videoContainer.video.paused){ // if not playing show the paused screen
drawPayIcon();
}*/
}
// all done for display
// request the next frame in 1/60th of a second
requestAnimationFrame(updateCanvas);
}
var canvasOffset = $("#canvas2").offset();
var offsetX = canvasOffset.left;
var offsetY = canvasOffset.top;
var isDrawing = false;
var startX;
var startY;
var mouseIsDown = true;
var mouseIsUp = true;
var startX;
var startY;
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) {
mouseIsDown=false;
startX = parseInt(e.clientX - offsetX);
startY = parseInt(e.clientY - offsetY);
if (mouseIsUp) {
console.log('2');
context2.beginPath();
context2.rect(startX,startY,mouseX-startX,mouseY-startY);
context2.strokeStyle="limegreen";
context2.lineWidth=2;
context2.stroke();
canvas2.style.cursor="default";
mouseIsUp=true;
}
}
$("#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);
}
Any help is appreciated, thank you!
Related
I am going to create a canvas to let users can draw some rectangles in canvas. It can show the rectangle when the user is dragging the mouse. Also, it allows the user to draw one or more rectangles in canvas. I've found a solution like this:
// get references to the canvas and context
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
// style the context
ctx.strokeStyle = "blue";
ctx.lineWidth = 3;
// calculate where the canvas is on the window
// (used to help calculate mouseX/mouseY)
var $canvas = $("#canvas");
var canvasOffset = $canvas.offset();
var offsetX = canvasOffset.left;
var offsetY = canvasOffset.top;
var scrollX = $canvas.scrollLeft();
var scrollY = $canvas.scrollTop();
// this flage is true when the user is dragging the mouse
var isDown = false;
// these vars will hold the starting mouse position
var startX;
var startY;
function handleMouseDown(e) {
e.preventDefault();
e.stopPropagation();
// save the starting x/y of the rectangle
startX = parseInt(e.clientX - offsetX);
startY = parseInt(e.clientY - offsetY);
// set a flag indicating the drag has begun
isDown = true;
}
function handleMouseUp(e) {
e.preventDefault();
e.stopPropagation();
// the drag is over, clear the dragging flag
isDown = false;
}
function handleMouseOut(e) {
e.preventDefault();
e.stopPropagation();
// the drag is over, clear the dragging flag
isDown = false;
}
function handleMouseMove(e) {
e.preventDefault();
e.stopPropagation();
// if we're not dragging, just return
if (!isDown) {
return;
}
// get the current mouse position
mouseX = parseInt(e.clientX - offsetX);
mouseY = parseInt(e.clientY - offsetY);
// Put your mousemove stuff here
// clear the canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
// calculate the rectangle width/height based
// on starting vs current mouse position
var width = mouseX - startX;
var height = mouseY - startY;
// draw a new rect from the start position
// to the current mouse position
ctx.strokeRect(startX, startY, width, height);
}
// listen for mouse events
$("#canvas").mousedown(function (e) {
handleMouseDown(e);
});
$("#canvas").mousemove(function (e) {
handleMouseMove(e);
});
$("#canvas").mouseup(function (e) {
handleMouseUp(e);
});
$("#canvas").mouseout(function (e) {
handleMouseOut(e);
});
http://jsfiddle.net/m1erickson/6E2yd/
However, the solution can only allow users to draw one rectangle. If users draw the second rectangle, the previous rectangle will be wiped out because it will call ctx.clearRect() every time during dragging the mouse.
Edit: Sorry for my mistake. I missed the position:relative property for the container. Now it should work.
The code in your jsfiddle redraw repeatedly to give indications of the rectangle. I think it would be better to separate this indication canvas to a new layer, and use an overlapped layer to show the drawn rectangles.
JS:
// get references to the canvas and context
var canvas = document.getElementById("canvas");
var overlay = document.getElementById("overlay");
var ctx = canvas.getContext("2d");
var ctxo = overlay.getContext("2d");
// style the context
ctx.strokeStyle = "blue";
ctx.lineWidth = 3;
ctxo.strokeStyle = "blue";
ctxo.lineWidth = 3;
// calculate where the canvas is on the window
// (used to help calculate mouseX/mouseY)
var $canvas = $("#canvas");
var canvasOffset = $canvas.offset();
var offsetX = canvasOffset.left;
var offsetY = canvasOffset.top;
var scrollX = $canvas.scrollLeft();
var scrollY = $canvas.scrollTop();
// this flage is true when the user is dragging the mouse
var isDown = false;
// these vars will hold the starting mouse position
var startX;
var startY;
var prevStartX = 0;
var prevStartY = 0;
var prevWidth = 0;
var prevHeight = 0;
function handleMouseDown(e) {
e.preventDefault();
e.stopPropagation();
// save the starting x/y of the rectangle
startX = parseInt(e.clientX - offsetX);
startY = parseInt(e.clientY - offsetY);
// set a flag indicating the drag has begun
isDown = true;
}
function handleMouseUp(e) {
e.preventDefault();
e.stopPropagation();
// the drag is over, clear the dragging flag
isDown = false;
ctxo.strokeRect(prevStartX, prevStartY, prevWidth, prevHeight);
}
function handleMouseOut(e) {
e.preventDefault();
e.stopPropagation();
// the drag is over, clear the dragging flag
isDown = false;
}
function handleMouseMove(e) {
e.preventDefault();
e.stopPropagation();
// if we're not dragging, just return
if (!isDown) {
return;
}
// get the current mouse position
mouseX = parseInt(e.clientX - offsetX);
mouseY = parseInt(e.clientY - offsetY);
// Put your mousemove stuff here
// calculate the rectangle width/height based
// on starting vs current mouse position
var width = mouseX - startX;
var height = mouseY - startY;
// clear the canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
// draw a new rect from the start position
// to the current mouse position
ctx.strokeRect(startX, startY, width, height);
prevStartX = startX;
prevStartY = startY;
prevWidth = width;
prevHeight = height;
}
// listen for mouse events
$("#canvas").mousedown(function (e) {
handleMouseDown(e);
});
$("#canvas").mousemove(function (e) {
handleMouseMove(e);
});
$("#canvas").mouseup(function (e) {
handleMouseUp(e);
});
$("#canvas").mouseout(function (e) {
handleMouseOut(e);
});
HTML:
<h4>Drag the mouse to create a rectangle</h4>
<div id = "canvasWrapper">
<canvas id="overlay" width=300 height=300></canvas>
<canvas id="canvas" width=300 height=300></canvas>
</div>
CSS:
body{ background-color: ivory; }
canvas{
border: 1px solid red;
position: absolute;
}
#canvasWrapper{
position:relative;
}
http://jsfiddle.net/xkmqz9ho/
I've never tried anything on canvas, but you could try adding ctx.save() to the mouseout function and replacing ctx.clear(...) with ctx.restore().
more information about this on: https://developer.mozilla.org/es/docs/Web/API/CanvasRenderingContext2D/save
I am trying to make a drawing app inside a bootstrap modal.
So far I have achieved this jsfiddle
Here's my code
var canvasDiv = document.getElementById('canvasDiv');
canvas = document.createElement('canvas');
canvas.setAttribute('width', 570);
canvas.setAttribute('height', 300);
canvas.setAttribute('id', 'canvas');
canvasDiv.appendChild(canvas);
if(typeof G_vmlCanvasManager != 'undefined') {
canvas = G_vmlCanvasManager.initElement(canvas);
}
context = canvas.getContext("2d");
$('#canvas').mousedown(function(e){
var mouseX = e.pageX - this.offsetLeft;
var mouseY = e.pageY - this.offsetTop;
paint = true;
addClick(e.pageX - this.offsetLeft, e.pageY - this.offsetTop);
redraw();
});
$('#canvas').mousemove(function(e){
if(paint){
addClick(e.pageX - this.offsetLeft, e.pageY - this.offsetTop, true);
redraw();
}
});
$('#canvas').mouseup(function(e){
paint = false;
});
$('#canvas').mouseleave(function(e){
paint = false;
});
var clickX = new Array();
var clickY = new Array();
var clickDrag = new Array();
var paint;
function addClick(x, y, dragging)
{
clickX.push(x);
clickY.push(y);
clickDrag.push(dragging);
}
function redraw(){
context.clearRect(0, 0, context.canvas.width, context.canvas.height); // Clears the canvas
context.strokeStyle = "#df4b26";
context.lineJoin = "round";
context.lineWidth = 5;
for(var i=0; i < clickX.length; i++) {
context.beginPath();
if(clickDrag[i] && i){
context.moveTo(clickX[i-1], clickY[i-1]);
}else{
context.moveTo(clickX[i]-1, clickY[i]);
}
context.lineTo(clickX[i], clickY[i]);
context.closePath();
context.stroke();
}
}
But when the mouse is clicked, the drawing is being made way off from where the mouse pointer is. Check the fiddle to get an understanding of what I mean.
Also I would like to know how to make the canvas take the height and width of the modal and not pass in static width and height like this canvas.setAttribute('width', 570);
1.offsetleft is relative to parent element https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/offsetLeft , so you need $.offset
2.height and width is according to parent after it shows
result see https://jsfiddle.net/hj898aqb/1/
I am allowing the user to draw rectangles on an image. At the same time , the user should be able to resize or move any of the rectangles at any point of time.
With some help, i have been able to draw the rectangles but i am unable to come up with resizing and moving part of it.
The rectangles that are being drawn do not overlap one another and the same has to be validated while resizing and moving too.
I am using javascript and jquery.
This is a demo of what i have done so far :
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var canvasOffset = $("#canvas").offset();
var offsetX = canvasOffset.left;
var offsetY = canvasOffset.top;
var startX;
var startY;
var isDown = false;
ctx.strokeStyle = "lightgray";
ctx.lineWidth = 3;
function handleMouseDown(e) {
mouseX = parseInt(e.clientX - offsetX);
mouseY = parseInt(e.clientY - offsetY);
// Put your mousedown stuff here
startX = mouseX;
startY = mouseY;
isDown = true;
}
function handleMouseUp(e) {
mouseX = parseInt(e.clientX - offsetX);
mouseY = parseInt(e.clientY - offsetY);
$("#uplog").html("Up: " + mouseX + " / " + mouseY);
// Put your mouseup stuff here
isDown = false;
}
function handleMouseMove(e) {
mouseX = parseInt(e.clientX - offsetX);
mouseY = parseInt(e.clientY - offsetY);
// Put your mousemove stuff here
if (!isDown) {
return;
}
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawRectangle(mouseX, mouseY);
}
function drawRectangle(mouseX, mouseY) {
var width = mouseX - startX;
var height = mouseY - startY;
ctx.beginPath();
ctx.rect(startX, startY, width, height);
ctx.stroke();
}
$("#canvas").mousedown(function (e) {
handleMouseDown(e);
});
$("#canvas").mousemove(function (e) {
handleMouseMove(e);
});
$("#canvas").mouseup(function (e) {
handleMouseUp(e);
});
as i am running short of time and i am not able to figure out how this can be done.
AFAK, HTML canvas element is simply an array of pixels.
Then drawing/moving/resizing rectangles is, simply again, to keep redrawing canvas.
So first, drawn objects need to be stored (maybe in array).
Second, corresponding mouse events are necessary.
Last, canvas redrawing is required.
Like:
var boxes = [];
var tmpBox = null;
document.getElementById("canvas").onmousedown = function(e) {...};
document.getElementById("canvas").onmouseup = function(e) {...};
document.getElementById("canvas").onmouseout = function(e) {...};
document.getElementById("canvas").onmousemove = function(e) {...};
Here is JSFiddle for demo: https://jsfiddle.net/wiany11/p7hxjmsj/14/
These 2 tutorials explain what you want:
http://simonsarris.com/blog/510-making-html5-canvas-useful
http://simonsarris.com/blog/225-canvas-selecting-resizing-shape
In short you should store the borders of the rectangles yourself and detect when the user clicks in the rectangle or on the border.
First you create an array to store your rectangles in
var rectangles = [];
Then you make a method to call every time you want to draw all your rectangles
function drawRectangles() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
for(var i = 0; i < rectangles.length; i++) {
var rect = rectangles[i];
ctx.beginPath();
ctx.rect(rect.startX, rect.startY, rect.endX, rect.endY);
ctx.stroke();
ctx.closePath();
}
}
In your mouseUp you then push the rectangles you have created to the array
function handleMouseUp() {
...
// store the rectangle as an object in your array
var rectangle = {startX: startX, endX: mouseX, startY: startY, endY: mouseY};
rectangles.push(rectangle);
drawRectangles();
}
In your other handlers you can then detect if you click in a rectangle of when your mouse will move in one
You can't just draw objects onto the canvas if you want to move them. You need to create instances of your shape objects and manage those (hit-testing and rendering as required). It is not very complex, but requires a lot more code than you have so far.
Try this tutorial: http://simonsarris.com/blog/510-making-html5-canvas-useful
I wrote a class for image cropping. It works fine with non-zoomed images (images in original dimensions), but if I want to apply the cropping to a zoomed image, it goes totally wrong.
Here is the cropping class:
var cropFlag = false; //false = disable cropping | true = enables cropping
function Cropper() {
this.flag1X, this.flag1Y; //startpoint (upper left corner) for rectangle
this.flag2X, this.flag2Y; //endpoint (lower right corner) for rectangle
this.mouseDownFlag = false; // true, if mouse is pressed
};
//gets the startposition of the rectangle
Cropper.prototype.handleMouseDown = function (e) {
if (cropFlag) {
var self = this;
//start of the rectangle
this.flag1X = e.clientX;
this.flag1Y = e.clientY;
this.mouseDownFlag = true;
};
};
//gets the endpoint of the rectangle
Cropper.prototype.handleMouseMove = function (e) {
var self = this;
if (this.mouseDownFlag) {
//end of the rectangle
this.flag2X = e.clientX;
this.flag2Y = e.clientY;
//calculate the position to draw the moved and scaled image
var imgDrawPositionX = (calcedPositionX * scale) + pointX;
var imgDrawPositionY = (calcedPositionY * scale) + pointY;
//redraw the image when rectangles size is changing to avoid multiple rectangles
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(img, imgDrawPositionX, imgDrawPositionY, width, height);
//draw the rectangle shape
ctx.beginPath();
ctx.lineWidth = "5"; //thickness
ctx.strokeStyle = "red"; //color
ctx.rect(this.flag1X, this.flag1Y, this.flag2X - this.flag1X, this.flag2Y - this.flag1Y);
ctx.stroke();
var draw = function () {
self.mouseDownFlag = false;
//calculate length and height of the clipped image
var clippedLength = width - (width - (self.flag2X - calcedPositionX) + (self.flag1X - calcedPositionX));
var clippedHeight = height - (height - (self.flag2Y - calcedPositionY) + (self.flag1Y - calcedPositionY));
console.log("clippedLength: " + clippedLength, "clippedHeight: " + clippedHeight);
//calculate the start coordinates of the clipping image
var startClippingX = self.flag1X - calcedPositionX;
var startClippingY = self.flag1Y - calcedPositionY;
console.log("startClippingX: " + startClippingX, "startClippingY: " + startClippingY);
//calculate the coordinates to draw the image in the center of the canvas
var centralDrawPosX = (canvas.width / 2) - (clippedLength / 2);
var centralDrawPosY = (canvas.height / 2) - (clippedHeight / 2);
console.log("centralDrawPosX: " + centralDrawPosX, "centralDrawPosY" + centralDrawPosY);
//clear the canvas and draw the clipped image
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(img, startClippingX, startClippingY, clippedLength, clippedHeight, centralDrawPosX, centralDrawPosY, clippedLength, clippedHeight);
canvas.removeEventListener('mouseup', draw);
};
//draws the rectangle and refreshs the whole canvas
canvas.addEventListener('mouseup', draw);
};
};
//initialize listeners and get button object
Cropper.prototype.initCropper = function () {
var self = this;
var button = document.getElementById('crop-button');
button.addEventListener('click', function () { cropFlag = true; moveFlag = false; });
canvas.addEventListener('mousedown', function (e) { self.handleMouseDown(e); });
canvas.addEventListener('mousemove', function (e) { self.handleMouseMove(e); });
};
var imageCropper = new Cropper();
I have an HTML5 Canvas that can be drawn on with the mouse. I would like to be able to clear the canvas so the user can make a new drawing. I do this with:
myContext.clearRect(0, 0, 500, 700);
The canvas appears clear but as soon as the user begins a new drawing the old drawing reappears. My JavaScript for the mouse drawing part is:
// Variables
var x1;
var y1;
var isPressed = false;
var myCanvas;
var myContext;
function startCanvas() {
// Canvas stuff
myCanvas = document.getElementById("can1");
myContext = myCanvas.getContext("2d");
// Specify a black background, and white lines that are 3 pixels thick.
myContext.fillStyle = '#000000';
myContext.strokeStyle = '#000000';
myContext.fillRect(0, 0, 500, 700);
myContext.lineWidth = 3;
myContext.fill();
}
function functionMouseDown(e) {
// Get coordinates
x1 = e.clientX - myCanvas.offsetLeft;
y1 = e.clientY - myCanvas.offsetTop;
isPressed = true;
}
function functionMouseMove(e) {
// If mouse is down and moved start drawing line
if (isPressed == true) {
drawLine(e);
}
}
function functionMouseUp() {
// Stop drawing line
isPressed = false;
}
function drawLine(e) {
// Draw line
var x = e.clientX - myCanvas.offsetLeft;
var y = e.clientY - myCanvas.offsetTop;
myContext.strokeStyle = '#ffffff';
myContext.lineWidth = 1;
myContext.moveTo(x1, y1);
myContext.lineTo(x, y);
myContext.stroke();
// Set start coordinates to current coordinates
x1 = x;
y1 = y;
}
startCanvas();
The HTML is:
<canvas id="can1" width="500" height="700"></canvas>
myContext.strokeStyle = '#000000';
myContext.beginPath();//<---- add this and read about this.
myContext.fillRect(0, 0, 500, 700);
myContext.lineWidth = 3; //why?
myContext.fill();