HTML5 Canvas Mouse - javascript

Something like I am have:
function mouseClick(event) {
...
}
canvas.addEventListener("mousedown", mouseClick, false);
function drawRect(x, y) {
context.fillRect(x, y, 16, 16);
};
drawRect(10, 10);
How to do something like if I am click on my Rect in Canvas get something? something like If I am click on Rect get alert;
Sorry for my English language.

A canvas is nothing more than a bitmap. There is no record kept that there was a rectangle drawn on the canvas, so if you want to detect that the click was inside an area that you drew a rectangle on, you have to keep a record of the areas you've drawn and test against them. eg:
var rects= [];
function mouseClick(event) {
// Get position of click relative to canvas. This is not reliable! Requires
// standards mode, and the canvas not being nested in other offsetParents.
// Getting page-relative co-ordinates reliably in all cases is outside the
// scope of this question...
//
var x= event.clientX-document.documentElement.scrollLeft-canvas.offsetLeft;
var y= event.clientY-document.documentElement.scrollTop-canvas.offsetTop;
// Hit-test each rectangle in the list of those drawn
//
for (var i= rects.length; i-->0;) {
var x0= rects[i][0], y0= rects[i][1], x1= rects[i][2], y1= rects[i][3];
if (x0<=x && x<x1 && y0<=y && y<y1) {
alert('you clicked on a rectangle!');
}
}
}
function drawRect(x, y) {
rects.push([x, y, x+16, y+16])
context.fillRect(x, y, 16, 16);
};
drawRect(10, 10);
If you are doing a lot of this sort of thing you are likely to be better off using a retained-mode drawing system like SVG instead of the pure-bitmap Canvas. In SVG you can listen for click events directly on a rectangle object, move the rectangle, re-stack it and so on.

$(canvas).mousedown(function myDown(e)
{
var position = $(canvas).position();
var x = e.pageX-position.left;
var y = e.pageY-position.top;
...
});
This is much better way to do this!

I think what you're saying is that you want events to occur when you click on objects you have drawn on a canvas. I have written a few tutorials on how to make persistent shapes and move them around with mouse clicks. That should give you a good starting point for this.

You could also try out Concrete.js http://www.concretejs.com which is a lightweight canvas framework that adds interactivity. You would just do something like this:
var key = canvas.getIntersection(x,y);
if x,y intersects a graphic you've drawn with a given key, it returns the key, and you know what was hovered/clicked on.
This is a much better solution because it will work on anything you draw, including circles, lines, curves, polygons, arbitrary blobs, etc.

addEventListener is tricky. What I would try is
canvas.addEventListener.apply(canvas, ["mousedown", mouseClick, false]);
or use an anonymous function.
canvas.addEventListener.apply(canvas, ["mousedown", function(){ mouseClick(); }, false]);
Using the apply function can help make sure the eventListener is applied to the right element. You can read about it here.

Related

How to check if mouse position is inside HTML5 Canvas path?

I'm new to HTML5 Canvas and Javascript. I'm currently working on a HTML5 Canvas project (I wrote a separate question about it here and here).
The logic of the game is pretty simple: track the mouse position and if the mouse position exits the blue region, the game resets. The user starts on level 1 (function firstLevel). If their mouse position enters the red box region, they advance onto the next level (return secondLevel()). I did this previously by a hefty list of if statements that compared the mouse's x and y coordinates.
I've corrected the code to create a global constructor function I can reference in each of the level functions:
function Path(array, color) {
let path = new Path2D();
path.moveTo(array[0][0], array[0][1]);
for (i = 1; i < array.length; i++) {
path.lineTo(array[i][0], array[i][1]);
}
path.lineTo(array[0][0], array[0][1]);
return path;
}
And its referenced in the level functions as such:
// In the individual levels, put code that describes the shapes locally
var big = [[350,200], [900,200], [900,250], [700,250], [600,250], [600,650], [350,650]];
var blue = Path(big);
var small = [[900,200], [900,250], [850,250], [850, 200], [900, 200]];
var red = Path(small);
// 2. create cursor
c.beginPath();
c.rect(mouseX, mouseY, mouseWidth, mouseHeight);
c.fillStyle = "#928C6F";
c.fill();
// Local code that draws shapes:
c.beginPath()
c.fillStyle = '#C1EEFF';
c.fill(blue);
c.beginPath()
c.fillStyle = "#FF4000";
c.fill(red);
I want to know how can I check for the mouse position so it can run those conditional statements using the isPointInPath method? I now have a reference (refs. blue and red) for the Canvas shape, so I'm hoping there's a way I can check if the mouse's x and y position is a point that is inside the shape/path.
Link to my project: https://github.uconn.edu/pages/ssw19002/dmd-3475/final-project/maze-page-1.html
Source code: https://github.uconn.edu/ssw19002/dmd-3475/tree/master/final-project
You could use the MouseEvent.offsetX and MouseEvent.offsetY properties of a mouse event such as mousemove. (You would need to add an event listener for mouse events to the canvas element of course.)
Then use the x and y offset values obtained to call isPointInPath like
ctx.isPointInPath(path, x, y)
where path is a return value from function Path.
While MDN lists the offset properties as "experimental" they seem to have been around since IE9 at least.

Image Flickering In Canvas Game

For a university project I have been tasked with creating a Flappy Bird clone. It's being done using the HTML5 canvas.
The issue doesn't happen very often, but it seems that every 6 or so seconds, the grass will flicker. I'm not sure what's causing this, it could be a performance issue.
Here is a link so you may see the issue: http://canvas.pixcelstudios.uk
Here is the function I'm using to the draw the grass:
var drawGrass = function(cWidth, ctx, minusX)
{
var x = bg_grass.x;
var y = bg_grass.y;
var w = bg_grass.w;
var h = bg_grass.h;
var img = bg_grass.img;
if (minusX[0] >= cWidth)
{
bg_grass.x = 0;
minusX[0] = 0;
}
ctx.drawImage(img, x, y, w, h);
if (minusX[0] > 0)
{
ctx.drawImage(img, w-minusX[0], y, w, h);
}
};
Basically, I'm drawing two grass sprites, each taking up a canvas width. One starts with an X of 0 and the other starts at the end of the canvas. Both are decremented each frame, then one is completely off the screen, it's completely reset to keep it looping.
I don't think it's anything to do with my update loop which is as follows:
this.update = function()
{
clearScreen();
updateBackground();
updatePositions();
checkCollisions();
render();
requestAnimFrame(gameSpace.update);
};
I've done a little bit of reading and I've read about having a second canvas to act as a buffer. Apparently this can stop flickering and improve performance? But all of the examples I've seen show the parts being drawn into the canvas out of a loop and I can't really see how doing it within a game loop (moving parts and all) would increase performance rather than decrease it. Surely the same operations are being performed, except now you also have to draw the second canvas onto the first?
Please let me know if you need any more information (although you should be able to see the whole source from the web link).
Thanks!
Okay I found the issue! Was just a simple mistake in my drawGrass function.
Due to the ordering, there'd be just a single frame where I'd set my shorthand X variable to bg_grass.x and THEN set bg_grass.x to something else, therefore drawing the wrong value.
I've now set my shorthand variables after the first if-statement.
However, if anyone could provide any insight into the second part of the question regarding a buffer canvas, I'd still much appreciate that.

canvas scrolling with collision detecting

I'm new in html5+canvas and i can't find more information about my problem and i decided ask here...
I need help with scrolling large map - 2800x1500px in canvas 400x300px and collision detecting on "invisible area" which is outside of canvas.
like this:
Few functions from my code
function Map()
{
this.img = new Image();
this.img.src = "img/map.jpg"; //map picture on main canvas
this.gimg = new Image();
//map with opacity on "ghost" canvas for collision detecting
this.gimg.src = "img/gmap.png";
this.draw = function(ctx,gctx)
{
ctx.drawImage(this.img,-offset.x,-offset.y);
gctx.drawImage(this.gimg,-offset.x,-offset.y);
}
}
function init()
{
var gameLoop = setInterval(function() {
draw(ctx,gctx);
}, 1000/fps);
}
function draw(ctx,gctx)
{
ctx.save();
gctx.save();
ctx.clearRect(-offset.x,-offset.y,2800,1500);
gctx.clearRect(-offset.x,-offset.y,2800,1500);
map.draw(ctx,gctx);
ctx.translate(offset.x,offset.y); //scrolling canvas
gctx.translate(offset.x,offset.y); //scrolling ghost canvas
ctx.restore();
gctx.restore();
}
//collision detecting function
function hitTest(obj,gctx)
{
var imageData = gctx.getImageData(obj.x,obj.y,1,1);
if( imageData.data[3]==255)
{
return true;
}
else return false;
}
for scrolling map i use that example:
http://jsfiddle.net/hKrrY/
my project:
http://game.com.hostinghood.com/
You cant do your collision that way with objects not in the viewable area. The image isn't being rendered onto the canvas in that portion so it will always have a 0 opacity.
There are two ways you can do it. One would be to render your collision map on a separate in-memory canvas that is the same width and height of the collision map, and then compare the coordinates for your enemy on the active active canvas to the area on your collision map. That would probably be the easiest way for you to do it after looking at your code.
The second approach would be to use a tilemap. You would basically have a 2d array where each element represents an area lets say 32x32, if its a 1 its collide-able, if its a 0 you can pass through. This would involve translating the enemies position to the area on the array to check.
I've done it both ways, the best in terms of memory utilization is the tilemap approach.
Here is a great tutorial explaining the tilemap approach

How to drag rotated shapes using Raphael js library

I have done lots of examples on dragging an object created by Raphael's library. Now I am working with sets and was also able to write a code to drag them.
Now my problem appeared when I rotate an object and then drag it.
Check out this code example: demo
var paper = Raphael('stage', 300, 300);
var r = paper.rect(50,100,30,50).attr({fill:"#FFF"}).rotate(45),
t = paper.text(30, 140, "Hello");
var p = paper.set(r, t);
r.set = p, t.set = p;
p.newTX=0,p.newTY=0,p.fDx=0,p.fDy=0,p.tAddX,p.tAddY,p.reInitialize=false,
start = function () {
},
move = function (dx, dy) {
var a = this.set;
a.tAddX=dx-a.fDx,a.tAddY=dy-a.fDy,a.fDx=dx,a.fDy=dy;
if(a.reInitialize)
{
a.tAddX=0,a.fDx=0,a.tAddY=0;a.fDy=0,a.reInitialize=false;
}
else
{
a.newTX+=a.tAddX,a.newTY+=a.tAddY;
a.attr({transform: "t"+a.newTX+","+a.newTY});
}
},
up = function () {
this.set.reInitialize=true;
};
p.drag(move, start, up);
By examining the DEMO you can see that the set is created with rotated rectangle, but as soon you drag it, it goes back to the 0 degree state. Why? Any solutions?
The problem is that whenever an element is transformed by applying a string containing instructions to move, rotate, scale etc, it resets the transformation object, and hence previous transformations get lost. To avoid this, add "..." at the beginning of the transformation string. Like,
var el = paper.rect(10, 20, 300, 200);
// translate 100, 100, rotate 45°, translate -100, 0
el.transform("t100,100r45t-100,0");
// NOW, to move the element further by 50 px in both directions
el.transform("...t50,50");
If "t50,50" is used instead of "...t50,50", then transformation effect for "t100,100r45t-100,0" is lost and transformation effect for "t50,50" rules.
Raphael reference for further study: http://raphaeljs.com/reference.html#Element.transform
Hope this helps.
I found an easy solution to this problem. Since I need to have a diamond instead of rectangle, I have created a path that represents that diamond. Then this path becomes just like a square 45 degree rotated.
This turned out to be easy because dragging functionality I had for my program works perfectly with paths.

How can you keep rotated draggable objects inside the Raphael paper?

I have an application with many draggable objects that can also be rotated in 90 degree increments. I'm trying to figure out how to stop the user from dragging the objects outside the Raphael paper (canvas).
This is fairly simple for unrotated objects. I can simply see if the current x and y coordinates are less than 0 and set them to 0 instead. I can adjust similarly by checking if they are outside the canvas width and height.
However, a problem arises when the object is rotated because for some odd reason the coordinate plane rotates as well. Is there an easy way to keep objects inside the canvas? Or is there an example of some this somewhere?
I have spent many hours fiddling with this and I can't seem to make sense of the rotated coordinate plane in order to adjust my calculations. Even when debugging the current coordinates, they seem to shift oddly if I drag an object, release it, and then drag the object again.
Any help is greatly appreciated.
Thanks,
Ryan
I had a similar problem, I needed to move a shape within the boundaries of another shape, so what I did was:
element.drag(onstart, onmove, onend);
...
onStart: function(x,y,e){
// Initialize values so it doesn't recalculate per iteration
// this allows to resume dragging from the point it were left
App.oldX = 0;
App.oldY = 0;
App.currentCircleX = App.fingerPath.attr('cx');
App.currentCircleY = App.fingerPath.attr('cy');
},
onMove: function(dx,dy,x,y,e){
App.setDirection(dx,dy);
},
onEnd: function(e){
// nothing to do here for now
},
// this function tells the element to move only if it's within the bound area
setDirection: function(dx, dy){
var isXYinside;
this.newX = this.currentCircleX - (this.oldX - dx);
this.newY = this.currentCircleY - (this.oldY - dy);
// HERE is the key, this method receives your bounding path and evaluates the positions given and then returns true or false
isXYinside = Raphael.isPointInsidePath(this.viewportPath, this.newX, this.newY);
this.oldX = dx;
this.oldY = dy;
// so if it is within the bound area, will move, otherwise will just stay there
if (isXYinside) {
this.fingerPath.attr({
"cx": this.newX,
"cy": this.newY
});
this.currentCircleX = this.newX;
this.currentCircleY = this.newY;
}
}
I know this is an old one, but I stumbled upon this question when trying to figure out a way to do it. So here's my 2 cents in case someone has this problem.
Reference:
Raphael.isPointInsidePath
Have you tried Element.getBBox()
There Are 2 flavones which give the result before rotation and after rotation
You should toggle the Boolean argument and test it

Categories

Resources