Draw a cross at the mouses location on a canvas, non persistently - javascript

I have this bound to the mousemove event of my canvas:
function(e){
var contDiv = $('#current_system_map');
var offset = contDiv.offset();
x = e.clientX-offset.left;
y = e.clientY-offset.top;
context.beginPath();
context.moveTo(0,y);
context.lineTo(595,y);
context.moveTo(x,0);
context.lineTo(x,595);
context.strokeStyle = "rgb(255,255,255)";
context.stroke();
}
and it works fine, to a point. The drawn cross is persistent, so when the mouse moves a new cross is drawn but the old one remains. I've tried essentially re-drawing the canvas but that cause the cross to be laggy and remain quite away behind the mouse.
So i need to know how to draw the cross and make it dis-appear without having to re-draw everything on the canvas

http://jsfiddle.net/PgKEt/2/
This is the best that I can do.
If you try to use setInterval and such to animate it, it will keep redrawing even when it does not need to. So by doing this, you essentially only redraw when the mouse moves, and only draw 2 lines, instead of whatever content you want it on top.
In addition, if you have any detection such as mousedown and such, it has to be on whatever canvas is on the top, otherwise it will not detect them anymore.

Usually if you draw something on the canvas you will have to redrawn the canvas contents to erase it. I suggest you use an image element as a cursor and position it absolutely above the
Or you could try the old trick and draw the cursor in the canvas with globalCompositeOperation='xor', then draw it again in the same place to erase it. Afterwards you will need to restore globalCompositeOperation to source-over.

This approach works fast enough for me in Firefox 3.6.8 to do in a mousemove event. Save the image before you draw the crosshair and then restore it to erase:
To save:
savedImage = new Image()
savedImage.src = canvas.toDataURL("image/png")
The to restore:
ctx = canvas.getContext('2d')
ctx.drawImage(savedImage,0,0)

If you do not want to store it persistently, you can also take a look at SVG.

Try
ctx.clearRect(0,0,YourCanvasHeight,YourCanvasWidth);
In my case I implemented a circle and every time the user clicks inside it, this instruction returns and deletes the previous points.
This is the complete code:
function getMousePosition(canvas, event) {
let rect = canvas.getBoundingClientRect();
let x = event.offsetX; //event.clientX - rect.left;
let y = event.offsetY; //event.clientY - rect.top;
drawPoint(canvas,x,y);
};
function drawPoint(canvas,x,y) {
if (canvas.getContext){
var ctx = canvas.getContext('2d');
ctx.clearRect(0,0,200,200);
ctx.beginPath();
ctx.arc(x, y, 5, 0, 2 * Math.PI);
ctx.fillStyle = "white";
ctx.fill();
}
};
$(document).ready(function(){
let canvasElem = document.getElementById("circle");
canvasElem.addEventListener("click", function(e)
{
getMousePosition(canvasElem, e);
});
});

Related

How do I make a HTML canvas rectangle resize along with mouse drag?

I am creating a web-based annotation application for annotating images via the HTML canvas element and Javascript. I would like the user to mouse down to indicate the start of the rectangle, drag to the desired end coordinate and let go to indicate the opposite end of the rectangle.
Currently, I am able to take the starting coordinates and end coordinates to create a rectangle on the image with the context.rects() function, however as I am uncertain on how to resize a specific rectangle on the canvas, that leaves me with the rectangle only being drawn after the user has released the mouse click.
How would I be able to resize a specific rectangle created onmousedown while dragging?
The following is the code snippet that performs the function:
var isMouseDown = false;
var startX;
var startY;
canvas.onmousedown = function(e) {
if(annMode){
isMouseDown = true;
var offset = $(this).offset();
startX = parseInt(e.pageX - offset.left);
startY = parseInt(e.pageY - offset.top);
}
};
canvas.onmousemove = function(e) {
if(isMouseDown) {
var offset = $(this).offset();
var intermediateX = parseInt(e.pageX - offset.left);
var intermediateY = parseInt(e.pageY - offset.top);
console.log(intermediateX);
}
};
canvas.onmouseup = function(e) {
if(annMode&&isMouseDown){
isMouseDown = true;
var offset = $(this).offset();
var endX = parseInt(e.pageX - offset.left);
var endY = parseInt(e.pageY - offset.top);
var width = endX - startX;
var height = endY - startY;
context.strokeStyle = "#FF0000";
context.rect(startX, startY, width, height);
context.stroke();
}
isMouseDown = false
};
Here my handy-front-end scripts come in handy!
As I understood the question, you wanted to be able to move your mouse to any point on the canvas, hold the left mouse button, and drag in any direction to make a rectangle between the starting point and any new mouse position. And when you release the mouse button it will stay.
Scripts that will help you accomplish what you are trying to do:
https://github.com/GustavGenberg/handy-front-end/blob/master/README.md#canvasjs
https://github.com/GustavGenberg/handy-front-end/blob/master/README.md#pointerjs
Both scripts just makes the code a lot cleaner and easier to understand, so I used those.
Here is a fiddle as simple as you can make it really using
const canvas = new Canvas([]);
and
const mouse = new Pointer();
https://jsfiddle.net/0y8cbao3/
Did I understand your question correctly?
Do you want a version with comments describing every line and what is does?
There are still some bugs at the moment but im going to fix those soon!
EDIT
After reading your questions again, I reacted to: "...however as I am uncertain on how to resize a specific rectangle on the canvas...".
Canvas is like an image. Once you have drawn to it, you can NOT "resize" different shapes. You can only clear the whole canvas and start over (ofcourse you can clear small portions too).
That's why the Canvas helper is so helpful. To be able to "animate" the canvas, you have to create a loop that redraws the canvas with a new frame each 16ms (60 fps).
The canvas API does not preserve references to specific shapes drawn with it (unlike SVG). The canvas API simply provides convenient functions to apply operations to the individual pixels of the canvas element.
You have a couple options to achieve a draggable rectangle:
You can position a styled div over your canvas while the user is dragging. Create a container for your canvas and the div, and update the position and size the div. When the user releases, draw your rectangle. Your container needs to have position: relative and the div needs to be absolutely positioned. Ensure the div has a higher z-index than the canvas.
In your mouse down method, set div.style.display to block. Then update the position (style.left, style.top, style.width, and style.height) as the mouse is dragged. When the mouse is released, hide it again (style.display = 'none').
You can manually store references to each item you want to draw, clear the canvas (context.clearRect), and redraw each item on the canvas each frame. This kind of setup is usually achieved through recursive usage of the window.requestAnimationFrame method. This method takes a callback and executes on the next draw cycle of the browser.
The first option is probably easier to achieve in your case. If you plan to expand the capabilities of your app further, the 2nd will provide more versatility. A basic loop would be implemented as so:
// setup code, create canvas & context
function mainLoop() {
context.clearRect(0, 0, canvas.width, canvas.height);
/** do your logic here and re-draw **/
requestAnimationFrame(mainLoop);
}
function startApp() {
requestAnimationFrame(mainLoop)
}
This tutorial has detailed explanation of event loops for HTML canvas: http://www.isaacsukin.com/news/2015/01/detailed-explanation-javascript-game-loops-and-timing
I also have a fully featured implementation on my GitHub that's part of rendering engine I wrote: https://github.com/thunder033/mallet/blob/master/src/mallet/webgl/webgl-app.ts#L115

How to clear an element of canvas that is moving (leaves a streak) without clearing canvas

Say I have drawn a circle on a canvas that has something else drawn on it that stops me from clearing the canvas - due to the other element being randomly generated
var circleX = 50;
var circleY = 10;
var moveCircX = 2;
var moveCircY = 3;
function createCirc(){
ctx.fillStyle = 'red';
ctx.beginPath();
ctx.arc(circleX, circleY, 10, 0, Math.PI*2, true);
ctx.fill();
}
function circMove(){
circleY = (circleY + circMoveY)
//then validation to stop it from being drawn of the canvas
So what I'm trying to do is move the circle but clear the previous drawn circle from the canvas. So is there a solution to clearing the circle or would it be easier to create a sprite that replicates the circle?
Since your background isn't changing, the simplest strategy is to copy the background before you first draw your circle, then draw your circle. When you're moving, redraw that part of the background from the copy you kept, then draw your circle in the new place.
An efficient way to do that is to use getImageData and putImageData.
So, (my javascript is rusty, so this may not be perfect. Feel free to correct any mistakes), before the first time you createCirc, simply do:
imageData = ctx.getImageData(0,0, ctx.canvas.width, ctx.canvas.height)
And, in your circMove function, before you move and redraw the circle, you want:
ctx.putImageData(imageData, circleX, circleY, circleX, circleY, 2*circle_radius, 2*circle_radius)
(You don't define circle_radius, but I'm sure you must have a similar value. I'm using 2x the radius to presumably be the size of the image that is drawn.)

JavaScript: Is it possible to cut a shape out of a rectangle to make a transparent hole in it?

I am trying to make a 2d top-down game in Javascript at the moment. I've currently got a day/night system working where a black rectangle gradually becomes more opaque (as the day goes on) before it finally is fully opaque, simulating the peak of the night where the player can not see.
I want to implement an artificial light system, where the player could use a torch that will illuminate a small area in-front of them. However, my problem is that I don't seem to be able to find a way to 'cut out' a shape from my opaque rectangle. By cutting out a shape, it would look like the player has a torch.
Please find an example mock-up image I made below to show what I mean.
http://i.imgur.com/VqnTwoR.png
Obviously the shape shouldn't be as roughly drawn as that :)
Thanks for your time,
Cam
EDIT: The code used to draw the rectangle is as follows:
context.fillStyle = "#000033";
context.globalAlpha = checkLight(gameData.worldData.time);
context.fillRect(0, 0, 512, 480);
//This is where you have to add the cut out triangles for light!
context.stroke();
Instead of drawing a rectangle over the scene to darken it when the "light" is on, instead draw an image with the "lit" area completely transparent and the rest of the "dark" area more opaque.
One way is to use declare a triangular clipping area and draw your revealed scene. The scene would display only inside the defined clipping area.
Example code and a Demo:
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
var x = canvas.width / 2;
var y = canvas.height / 2;
var radius = 75;
var offset = 50;
var img = new Image();
img.onload = function() {
knockoutAndRefill(50,100,300,50,75,350);
};
img.src = 'http://guideimg.alibaba.com/images/trip/1/03/18/7/landscape-arch_68367.jpg';
function knockoutAndRefill(x0,y0,x1,y1,x2,y2){
context.save();
context.fillStyle='black';
context.fillRect(0,0,canvas.width,canvas.height);
context.beginPath();
context.moveTo(x0,y0);
context.lineTo(x1,y1);
context.lineTo(x2,y2);
context.closePath();
context.clip();
context.drawImage(img,0,0);
context.restore();
}
<canvas id=myCanvas width=500 height=400>

Html 5 canvas and background

after trying a few ways to preserve a background under a canvas drawing a moving rectangle (animation code not reproduced), here is my best try :
canvas {background-image:url('background.png');}
var x,y, pixels;
function draw() {
if(pixels) {
context.putImageData(pixels,x,y);
}
x = //calculate new X
y = //calculate new Y
pixels = context.getImageData(x, y, 10, 10);
context.fillStyle = 'red';
context.fillRect(x, y, 10, 10);
}
My first question is : why won't replacing the first two lines of draw() with :
context.fillStyle = 'rgba(10,10,10,0)';
context.fillRect(x,y,10,10);
work to clear the previously drawn pixels?
And my second question is : is there really no better way than get and putImageData(), which are very labor-intensive?
EDIT: in particular is there a div containing the background image trick that would maybe work without the get and putImageData calls?
Thanks!
By default, whatever you draw gets drawn on top of each other, instead of being replaced.
Use context.clearRect(x,y,10,10); instead.
There is also the context.globalCompositeOperation property, which could be set to "destination-out" before using fillRect .. but you're better off with clearRect.

Canvas images and Click event

I have currently two circles in a <canvas> tag with HTML5 & JavaScript.
Now I'm trying to add an image (done) that changes based on mouse-over and click.
It's basically an implementation of a play / pause button with an extra color change when the user mouse-overs the button.
I can't seem to figure out how events work on shapes in HTML5 since they are not objects ... Here is my code at the moment :
window.onload = function() {
var canvas = document.getElementsByTagName('canvas')[0];
var context = canvas.getContext('2d');
var centerX = canvas.width / 2;
var centerY = canvas.height / 2;
//Outer circle
context.beginPath();
context.arc(centerX, centerY, 150, 0, Math.PI * 2, false);
context.fillStyle = "#000";
context.fill();
context.stroke();
//Inner cicle
context.beginPath();
context.arc(centerX, centerY, 75, 0, Math.PI * 2, false);
context.fillStyle = "#fff";
context.fill();
context.stroke();
//Play / Pause button
var imagePlay = new Image();
var imageHeight = 48/2;
imagePlay.onload = function() {
context.drawImage(imagePlay, centerX - imageHeight, centerY - imageHeight);
};
imagePlay.src = "images/play.gif";
}
How to handle events on shapes created with <canvas>?
How to clean-up / remove images on the <canvas> when replacing it with another one?
There is technically no way to register mouse events on canvas-drawn shapes. However, if you use a library, like Raphael (http://raphaeljs.com/), it can keep track of shape positions and thus figure out what shape is receiving the mouse event. here's an example:
var circle = r.circle(50, 50, 40);
circle.attr({fill: "red"});
circle.mouseover(function (event) {
this.attr({fill: "red"});
});
As you can see, it's very simple this way. For modifying shapes, this library will also come in handy. Without it you would need to remember how to redraw everything each time you make a change
Well The simple answer is you can't. You either will have to find the coordinates of the click event and calculate whether you want to perform an option or not or you can use area and map tags and overlay the canvas element with it. To change a canvas use the clearRect function to draw paint a rectangle over everything and then redraw what you want.
There is no "built-in" way of keeping track of shapes drawn on the canvas. They are not treated as objects, but rather just pixels in an area. If you want to handle events on shapes drawn on the canvas, you would need to keep track of the area each shape covers, and then determine which shape you're triggering the event for based on the mouse position.
You can just draw over other shapes if you want to replace it with something. You might want to take a look at globalCompositeOperation.
If you want to treat your drawings as objects, I would recommend using SVG instead of canvas.
Another option is to use buttons, and then style them using CSS.
Basically, what you're doing now really wasn't the intended purpose or use of the canvas. It's like using a pencil to hammer in nails - you're using the wrong tool for the job.
While it's true that you cannot create click events for objects drawn on the canvas there is a workaround: Wrap the canvas in a DIV tag and then add the images within the DIV tag above the CANVAS tag.
<div id="wrapper">
<img src="img1.jpg" id="img1"></img>
<canvas id="thecanvas"></canvas>
</div>
Then use CSS to make the images position:absolute and use left:*px and top:*px attributes to place the image over the canvas where you would have normally drawn it.
#img1{
position:absolute;
left: 10px;
top: 10px;
}
You can then add click events to the image which is placed over the canvas giving the impression that you are clicking on the canvas(the below example uses the jQuery click() function)
$( "#img1" ).click(function(){
alert("Thanks for clicking me");
});
You can cast a ray into the canvas and manually test your images for intersection with the ray. You should look at it like you press, and send a ray into the screen. You should write a
objectsUnderPoint( x, y );
function that returns an array of all the images that intersect with the ray at x, y.
This is the only real right answer, and this is how it is usually done in 3D engines as well.

Categories

Resources