I'm trying to implement a function as a part of a firefox add-on with canvas which gives the user the ability to draw.
function draw(event,context,drawit) {
var drawx = event.layerX;
var drawy = event.layerY;
if (!drawit) {
context.beginPath();
context.strokeStyle='rgb(0,255,0)';
context.lineWidth=1;
context.moveTo(drawx,drawy);
drawit = true;
} else {
context.lineTo(drawx,drawy);
context.stroke();
}
};
This works, but there seems to be a difference between the result of layerX/layerY and the line drawn. It's only possible to draw in the upper left part of the canvas element. When the mouse pointer reaches about the half of the element the line doesn't go further.
I already checked the position of the elements in Firebug and it seems ok: the canvas is inside a div-element and both have a defined width of 100%, while the drawing ends at about 50% of the element. It also works to set the values manually so that the line is also drawn in the right part of the canvas element.
Does anyone have an idea what is going wrong?
When the mouse pointer reaches about the half of the element the line doesn't go further.
This could happen for a lot of reasons. Mis-translated context (from rotation/translation/scaling) and mismatched canvas size (what you wrote it as in html and what you're considering it in code).
Are you certain that LayerX and LayerY are getting you the right mouse co-ordinates? What exactly do you mean by "difference" between layerx/y and whats drawn? Is there an offset? I ask because my mouse code is a bit more complex:
// Sets mx,my to the mouse position relative to the canvas
// unfortunately this can be tricky, we have to worry about padding and borders
function getMouse(e) {
var element = canvas, offsetX = 0, offsetY = 0;
if (element.offsetParent) {
do {
offsetX += element.offsetLeft;
offsetY += element.offsetTop;
} while ((element = element.offsetParent));
}
// Add padding and border style widths to offset
offsetX += stylePaddingLeft;
offsetY += stylePaddingTop;
offsetX += styleBorderLeft;
offsetY += styleBorderTop;
mx = e.pageX - offsetX;
my = e.pageY - offsetY
}
Related
I am writing an application that draws rectangles on a HTML canvas using the fillRect function. I currently track the movement of the mouse and detect when the mouse pointer hovers over a rectangle to highlight it:
This is how I am currently detecting collision which works great.
//boxes2 is my array of rectangles
var l = boxes2.length;
for (var i = l - 1; i >= 0; i--) {
if (mouseX >= boxes2[i].x && mouseX <= (boxes2[i].x + boxes2[i].w ) &&
mouseY >= boxes2[i].y && mouseY <= (boxes2[i].y + boxes2[i].h )) {
selectedBoxNum = i;
}
}
My problem is that this hover detection no longer works well after zooming in/out as the actual bounds of the rectangles desync from their values in my rectangle array.
var currentZoomValue = 1;
function myOnMouseWheel(event) {
event.preventDefault();
// Normalize wheel to +1 or -1.
var wheel = event.wheelDelta / 120;
if (wheel == 1) {
zoom = 1.1;
}
else {
zoom = .9;
}
currentZoomValue = currentZoomValue * zoom
canvas.style.transform = "scale(" + currentZoomValue + ")";
}
What I have tried:
Scaling the values in the array as I zoom in/out so that the rectangle bounds will stay in sync
This will not work for me because the scale function is stretching my canvas to make the rectangles look bigger. If I also actually make them bigger, they will be doubly enlarged and outpace the zoom of my canvas background.
Compensating my hover detection based upon my current zoom level
I have tried something like:
if (mouseX >= boxes2[i].x && mouseX <= (boxes2[i].x + (boxes2[i].w * currentZoomValue) ) &&
mouseY >= boxes2[i].y && mouseY <= (boxes2[i].y + (boxes2[i].h * currentZoomValue) )) {
selectedBoxNum = i;
}
My attempts at this do not work because while the rectangle height and width do scale in an easily predictable way, the x,y coordinates do not. When zooming in, the rectangles will radiate out from the center so some rectangles will gain x value and other lose based upon their position. I also considered maintaining a second rectangle array that I could use just for hover detection but decided against it for this reason.
A good solution would be to actually scale the rectangle's sizes to give the illusion of zooming, but the rectangles positions on the background image is important, and this technique will not affect the background.
Since there is no standard way of knowing page zoom level, I would suggest catching the click event with an absolutely-positioned div.
You can get the offset of your canvas element with the getBoundingClientRect() method.
Then, the code would look something like this:
boxes2.forEach(function(box, i) {
cx.fillRect(box.x, box.y, box.w, box.h);
/* We create an empty div */
var div = document.createElement("div");
/* We get the position of the canvas */
var rect = canvas.getBoundingClientRect();
div.style.position = "absolute";
div.style.left = (rect.left + box.x) + "px"; //Don't forget the pixels!!!
div.style.top = (rect.top + box.y) + "px";
div.style.width = box.w + "px";
div.style.height = box.h + "px";
/* For demonstration purposes we display a border */
div.style.border = "1px dashed black"
div.onclick = function() {/* Your event handler */}
document.body.appendChild(div);
});
Here's a live demonstration. At least in my browser, regions stay consistent even if I zoom in and out the page.
I am trying to use getBoundingClientRect to get the coordinates of my click on canvas, but am always getting the same result.
My code is here: http://fiddle.jshell.net/nH74F/1/
As you can see i always get 8,8
No idea why, is there another way to get this info?
That's because you always use the absolute position of the element returned by getBoundingClientRect, and not the mouse position.
Try this instead:
canvas.addEventListener('click', function(e) { // use event argument
var rect = canvas.getBoundingClientRect(); // get element's abs. position
var x = e.clientX - rect.left; // get mouse x and adjust for el.
var y = e.clientY - rect.top; // get mouse y and adjust for el.
alert('Mouse position: ' + x + ',' + y);
...
Modified fiddle
I'm using a canvas at the top of a page. Im writing out the pixel coordinates from the canvas at a mousemove event. Normally, the most bottom Y-value is equal to the canvas height, i e. 700px. But after scrollbar is used to scroll down a bit on the page, the bottom y-coordinate in the canvas will change accordingly to say 400px instead.
document.getElementById("mapcanvas").addEventListener("mousemove", getPosition, false);
function getPosition(event)
{
var x = event.x;
var y = event.y;
var canvas = document.getElementById("mapcanvas");
x -= canvas.offsetLeft;
y -= canvas.offsetTop;
document.getElementById("canvascoords").innerHTML = "Canvascoords: "+ "x=" + x + ", y=" + y;
}
... Where "mapcanvas" is my div holding the canvas.
Any ideas of making the y-coordinate independent from usage of scroll bar so that the lower y-coordinate always i 700px?
As you've discovered, canvas.offsetLeft & canvas.offsetTop do not account for scrolling.
To account for scrolling, you can use canvas.getBoundingClientRect
var BB=canvas.getBoundingClientRect();
var x=event.clientX-BB.left;
var y=event.clientY-BB.top;
BTW, you might want to fetch a reference to the canvas element just once outside your getPosition() function instead of fetching it repeatedly inside getPosition().
var canvas = document.getElementById("mapcanvas");
function getPosition(event){
...
I am trying to drag a container using transform translate but something is causing a jumpy behavior and I can't figure out what is the cause.
UPDATE: This must work on elements when their container is not always positioned at 0,0 from the document.
http://jsfiddle.net/dML5t/2/
HTML:
<div id=container style="position:absolute;left:50px;top:50px;width:500px;height:500px;background-color:red;">
<div id=tcontainer style="position:relative;left:50px;top:50px;width:400px;height:400px;background-color:green;">
<div id=move style="position:relative;left:20px;top:20px;height:150px;width:150px;background-color:lightgray;">
</div>
</div>
Javascript:
obj = {startPositionX:0,startPositionY:0};
$('#move').on("mousedown",function(){
var move = $(this);
obj.startPositionX=event.offsetX+$('#tcontainer').offset().left;
obj.startPositionY=event.offsetY+$('#tcontainer').offset().top;
$(document).on("mousemove",function(e){
console.log("dragging", e.pageX-obj.startPositionX, e.pageY-obj.startPositionY);
move.css('transform','translate('+(e.pageX-obj.startPositionX)+'px, '+(e.pageY-obj.startPositionY)+'px)');
});
});
$(document).on("mouseup",function(){
$(this).off("mousemove");
});
OffsetX and OffsetY may give you cursor pos relative to an element which is hovered now. You saved initial coordinates in mousedown. When mousemove was triggered your this coordinates changed a little, so when you subtract one from initials you got zeros (or 1px of difference) and your div went to top left corner. After it happaned your cursor hovered body element and in mousemove you get coordinates related to body. So when you subtract your zeros from the new coordinates you get real coordinates and your div go to the right place. Then you will get coordinates related to dragging div and will get zeros again, then real coords and so on.
Use pageX and pageY instead! fiddle
$('.move').on("mousedown",function(me){
var move = $(this);
var lastOffset = move.data('lastTransform');
var lastOffsetX = lastOffset ? lastOffset.dx : 0,
lastOffsetY = lastOffset ? lastOffset.dy : 0;
var startX = me.pageX - lastOffsetX, startY = me.pageY - lastOffsetY;
$(document).on("mousemove",function(e){
var newDx = e.pageX - startX,
newDy = e.pageY - startY;
console.log("dragging", e.pageX-startX, e.pageY-startY);
move.css('transform','translate(' + newDx + 'px, ' + newDy + 'px)');
// we need to save last made offset
move.data('lastTransform', {dx: newDx, dy: newDy });
});
});
$(document).on("mouseup",function(){
$(this).off("mousemove");
});
You need save original coords of your div (move.offset()) and use mouse offset (e.pageX-startX, e.pageY-startY) to get new coords.
Note: I'm using Google Chrome
Currently I have this test page
http://www.evecakes.com/doodles/canvas_size_issue.htm
It should draw a rectangle right below the mouse cursor, but there are some really weird scaling issues which is causing it to draw at a varying offset. I think it's because the canvas coordinate space is not increasing along with its html size. Is there a way to increase that coordinate space size?
Here's the javascript function
$('#mapc').mousemove(function (event) {
var canvas = $('#mapc');
var ctx = canvas[0].getContext('2d');
ctx.fillStyle = "rgb(200,0,0)";
ctx.fillRect(event.clientX, event.clientY, 55, 50);
document.title = event.clientX + " --- " + event.clientY;
});
Set the width and height HTML attributes of the canvas. Right now, it's assuming its default width and the CSS is just stretching it, and it appears as if it is scaling.
A side-note, you can use this in the mousemove event - it is a reference to #mapc element, so you won't have to query the DOM on every mouse move.
var offset = $('#mapc').offset();
$('#mapc').mousemove(function (event) {
var ctx = this.getContext('2d');
ctx.fillStyle = "rgb(200,0,0)";
ctx.fillRect(event.pageX - offset.left, event.pageY - offset.top, 1, 1);
});