I was searching for an answer but couldn't find any. My problem is:
I need an object to appear on canvas when I touch it and to disappear when I cancel the touch event (lift my finger).
However, I need the object to have the initial coordinates of the original touch position, meaning I don't want it to move with my finger.
var can = document.getElementById('can');
var ctx = can.getContext('2d');
var WIDTH = can.width;
var HEIGHT = can.height;
var canX;
var canY;
var mouseIsDown = 0;
var requestAnimFrame = window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame;
var image = new Image();
image.src = '1.png';
function init(){
can.addEventListener("mousedown", mouseDown, false);
can.addEventListener("mousemove", mouseXY, false);
document.body.addEventListener("mouseup", mouseUp, false);
loop();
}
function mouseUp() {
mouseIsDown = 0;
mouseXY();
}
function mouseDown() {
mouseIsDown = 1;
mouseXY();
}
function mouseXY(e) {
if (!e)
e = event;
canX = e.pageX - can.offsetLeft;
canY = e.pageY - can.offsetTop;
}
function loop(){
if(mouseIsDown===1){
draw();
} else
ctx.clearRect(0,0,WIDTH, HEIGHT);
requestAnimFrame(loop);
}
function draw(){
ctx.clearRect(0,0,WIDTH, HEIGHT);
ctx.drawImage(image,0,0,40,40,canX-20,canY-20,40,40);
}
You need to store the initial mouse/touch position in a separate pair of variables.
var initialCanX, initialCanY;
function mouseDown(e) {
mouseIsDown = 1;
initialCanX = e.pageX - can.offsetLeft;
initialCanY = e.pageY - can.offsetTop;
mouseXY(e);
}
And then, in the drawing function, use these coordinates to draw the second image.
function draw(){
ctx.clearRect(0, 0, WIDTH, HEIGHT);
ctx.drawImage(image, canX, canY); // the moving image
ctx.drawImage(image2, initialCanX, initialCanY); // the static image
}
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 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!
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've been studying and trying to write an application that takes whatever image is on an HTML5 canvas element and send it to clients. When trying to take the image using canvas.toDataURL(), compressing and sending it back to server, the client was sometimes displaying the image and sometimes not displaying what was sent. First I thought that it could be that the data was corrupted, but working on a local server and having 29/30 corrupted data didn't make sense to me, so I've tried to see what was going on and registered the length of the base64 image and noticed that it's length was changing, although the content of the canvas wasn't changing. Why is that happening? I wrote a simple page in which you can edit the canvas content by drawing on it that shows that happening:
<!DOCTYPE html>
<html>
<head>
<title>Draw</title>
<style>
body{margin:0;}
</style>
<script>
addEventListener("load", Load, false);
var board = undefined;
var ctx = undefined;
var loaded = false;
var mousehold = false;
var mousex = 0;
var mousey = 0;
var lastx = 0;
var lasty = 0;
var firstclick = false;
function Load(e)
{
loaded = true;
board = document.getElementById("board");
ctx = board.getContext("2d");
board.width = window.innerWidth;
board.height = window.innerHeight;
addEventListener("mousedown", function(e)
{
mousehold = true;
}, false);
addEventListener("mouseup", function(e)
{
mousehold = false;
firstclick = false;
}, false);
addEventListener("mousemove", function(e)
{
mousex = e.clientX;
mousey = e.clientY;
// if(mousehold == true) console.log(mousex + " " + mousey);
}, false);
ctx.beginPath();
ctx.lineWidth = 5;
UpdateBoard();
}
function UpdateBoard()
{
if(mousehold == true)
{
if(firstclick == false)
{
lastx = mousex;
lasty = mousey;
firstclick = true;
}
ctx.moveTo(lastx, lasty);
ctx.lineTo(mousex, mousey);
ctx.stroke();
lastx = mousex, lasty = mousey;
}
window.requestAnimationFrame(UpdateBoard);
}
function send()
{
var img = board.toDataURL();
console.log(img.length);
}
</script>
</head>
<body>
<canvas id="board"></canvas>
<button style="right:10px;bottom:10px;position:fixed;z-index:999999;" onclick="send();">Send</button>
</body>
</html>
Clicking "send" button will log the base64 image length on console. If you draw something to the screen, the content of the canvas will obviously change, but if you stop drawing and click "send" button a few times (without touching canvas content) you'll see that it seems to be generating different base64 images. Why is that happening? Is it possible to prevent that from happening? My application needs to update content constantly (compressed, but I've tried it without compression and problem was the same).To demonstrate the problem I've uploaded an image to imgur: http://imgur.com/a/iQ70T (ignore the typo in the image).Thank you for your attention.
The reason why this happens is that you have attached all the event listeners to the window object and not the canvas (mousedown, the others can be on window) the mousedown will be registered also when you hit the send button.
This means the last path is redrawn for each frame since mousehold will be true, accumulating anti-aliasing pixels affecting the alpha channel, hence will change the bitmap.
Example fix (just attach the mousedown handler to canvas):
addEventListener("load", Load, false);
var board = undefined;
var ctx = undefined;
var loaded = false;
var mousehold = false;
var mousex = 0;
var mousey = 0;
var lastx = 0;
var lasty = 0;
var firstclick = false;
function Load(e) {
loaded = true;
board = document.getElementById("board");
ctx = board.getContext("2d");
board.width = window.innerWidth;
board.height = window.innerHeight;
// this must be at canvas element
board.addEventListener("mousedown", function(e) {
mousehold = true;
}, false);
addEventListener("mouseup", function(e) {
mousehold = false;
firstclick = false;
}, false);
addEventListener("mousemove", function(e) {
mousex = e.clientX;
mousey = e.clientY;
// if(mousehold == true) console.log(mousex + " " + mousey);
}, false);
ctx.beginPath();
ctx.lineWidth = 5;
UpdateBoard();
}
function UpdateBoard() {
if (mousehold == true) {
if (firstclick == false) {
lastx = mousex;
lasty = mousey;
firstclick = true;
}
ctx.moveTo(lastx, lasty);
ctx.lineTo(mousex, mousey);
ctx.stroke();
lastx = mousex, lasty = mousey;
}
window.requestAnimationFrame(UpdateBoard);
}
function send() {
var img = board.toDataURL();
console.log(img.length);
}
body {margin:0}
<canvas id="board"></canvas>
<button style="right:10px;bottom:10px;position:fixed;z-index:999999;" onclick="send();">Send</button>
i'm creating a web page where you dynamically draw multiple rectangles. I can draw the single object, but once I tried to draw another one, the previous one is gone away. I tried to save the state using save() and restore(), and it seems that I can't put it here. Isn't it logical that save method is put in the mouseup and restore method is called in the mousedown event? Any help or advice will be appreciated.
const canvas = document.getElementById("circle"),
ctx = canvas.getContext("2d"),
circle = {},
drag = false,
circleMade = false,
mouseMoved = false;
function draw() {
ctx.beginPath();
ctx.arc(circle.X, circle.Y, circle.radius, 0, 2.0 * Math.PI);
ctx.stroke();
}
function mouseDown(e) {
ctx.restore();
circle.startX = e.pageX - this.offsetLeft;
circle.startY = e.pageY - this.offsetTop;
circle.X = circle.startX;
circle.Y = circle.startY;
if (!circleMade) {
circle.radius = 0;
}
drag = true;
}
function mouseUp() {
drag = false;
circleMade = true;
if (!mouseMoved) {
circle = {};
circleMade = false;
ctx.clearRect(0, 0, canvas.width, canvas.height);
}
mouseMoved = false;
ctx.save();
}
function mouseMove(e) {
if (drag) {
mouseMoved = true;
circle.X = e.pageX - this.offsetLeft;
circle.Y = e.pageY - this.offsetTop;
if (!circleMade) {
circle.radius = Math.sqrt(Math.pow((circle.X - circle.startX), 2) + Math.pow((circle.Y - circle.startY), 2));
}
ctx.clearRect(0, 0, canvas.width, canvas.height);
draw();
}
}
function init() {
canvas.addEventListener('mousedown', mouseDown, false);
canvas.addEventListener('mouseup', mouseUp, false);
canvas.addEventListener('mousemove', mouseMove, false);
}
init();
you need to save the information about what you are drawing in a separate object, every time you make a draw to the canvas you will wipe and redraw the new object.
so when you clearRect and then draw you are clearing and then drawing a fresh one but the old ones are being left behind. A good example:
var SavedCircles = [];
var circleInfo = function()
{
this.x = 0;
this.y = 0;
this.startX = 0;
this.startY = 0;
this.radius = 0;
}
circle = {};
function draw()
{
for(var x=0;x<SavedCircles.length;x++)
{
ctx.beginPath();
ctx.arc(SavedCircles[x].X, SavedCircles[x].Y, SavedCircles[x].radius, 0, 2.0 * Math.PI);
ctx.stroke();
}
}
function mouseDown()
{
circle = new circleInfo();
}
function mouseUp()
{
SavedCircles.push(circle);
}
function mouseMove()
{
draw();
}
so you can get rid of save and restore, also its much faster to clear a canvas simply by:
canvas.width = canvas.width;
this should help you keep all circles ever drawn. fill in the rest with your code.