HTML5 Canvas : How to handle mousedown mouseup mouseclick - javascript

I been playing around with html5 canvas and ran into a problem.
canvas.onmousedown = function(e){
dragOffset.x = e.x - mainLayer.trans.x;
dragOffset.y = e.y - mainLayer.trans.y;
canvas.onmousemove = mouseMove;
}
canvas.onmouseup = function(e){
canvas.onmousemove = null;
}
canvas.onmouseclick = mouseClick;
function mouseMove(e){
mainLayer.trans.x = e.x - dragOffset.x;
mainLayer.trans.y = e.y - dragOffset.y;
return false;
}
function mouseClick(e){
// click action
}
In this code, I make my mouse click+drag pan the canvas view by translating by the drag offset. But I also have a click event. Right now, whenever I drag my mouse and let go, it runs both onmouseup AND onclick.
Are there any techniques to make them unique?

A click event happens after a successful mousedown and mouseup on an element. Is there any particular reason you are using click on top of the mouse events? You should be fine with just mousedown/mouseup, click is a convenience event that saves you a little bit of coding.
I would generally do it this way:
var mouseIsDown = false;
canvas.onmousedown = function(e){
dragOffset.x = e.x - mainLayer.trans.x;
dragOffset.y = e.y - mainLayer.trans.y;
mouseIsDown = true;
}
canvas.onmouseup = function(e){
if(mouseIsDown) mouseClick(e);
mouseIsDown = false;
}
canvas.onmousemove = function(e){
if(!mouseIsDown) return;
mainLayer.trans.x = e.x - dragOffset.x;
mainLayer.trans.y = e.y - dragOffset.y;
return false;
}
function mouseClick(e){
// click action
}

in the function mouse move set a boolean to say that a move occured, encompase all the code in mouseup and click with an if statement to check that a drag did not occur. After these if statements and before the end of the functions set the dragging boolean to false.

Try declaring a variable such as clickStatus = 0; and at the start of each function check to ensure the correct value.
if (clickstatus == 0) {
clickstatus =1;
...//function code
};

Related

A-Frame/JS mobile "click" fires twice

I have A-Frame scene with 8 cubes. I put this script after it:
document.addEventListener("touchstart", function(evt){
//console.log(evt);
if (!evt.isTrusted) return;
holded_m = true;
var touches_m = evt.changedTouches;
var touch_m = touches_m[touches_m.length-1]
mousePosX_m = touch_m.pageX;
mousePosY_m = touch_m.pageY;
rotX_m = ent_m.getAttribute("rotation")["x"];
newRotX_m = rotX_m;
newRotY_m = rotY_m;
})
document.addEventListener("touchend", function(evt){
holded_m = false;
rotX_m = newRotX_m;
rotY_m = newRotY_m;
})
document.addEventListener('touchmove', function(evt){
if (!holded_m) {return;}
//console.log(rotX - (mousePosY - evt.screenY)/10);
var touches_m = evt.changedTouches;
var touch_m = touches_m[touches_m.length-1];
newRotX_m = rotX_m - (mousePosY_m - touch_m.pageY)/slowing_m;
newRotY_m = rotY_m - (mousePosX_m - touch_m.pageX)/slowing_m;
ent_m.setAttribute("rotation", newRotX_m.toString() + " " + newRotY_m.toString() + " 0");
});
cube_8_m = document.querySelector("#cube_8_m")
cube_8_m.addEventListener("click", function(evt){
console.log(evt);
if (!clicked_m) {
clicked_m = true;
cube_8_m.emit("out");
desc_div_m.style.display = "block";
desc_header_m.innerHTML = "Some text"
desc_text_m.innerHTML = "Some text"
current_block_href = "https://some.com/"
console.log("out");
}
else {
clicked_m = false;
cube_8_m.emit("in");
desc_div_m.style.display = "none";
console.log("in");
}
})
When I click on selected cube with mouse on desctop - "click" event fires only once as should be. But on mobile devices in 90% cases tapping on cube fires two "clicks". I logged them to console and they are identical, but the second fires ~one second after the first.
I havn't any other click handling in code. I found that touching screen somewhere else, not on cube, holding touch and moving it on cube and releasing then fires one click event. Only one.
I don't know why does it happen, but I want to have one click = one event.
I don't know if this is still helpful for you or not.
Here's a work around solution to prevent triggering click event twice on mobile.
https://glitch.com/edit/#!/profuse-forest-sword
The point is adding
cursor="rayOrigin: mouse; fuseTimeout: 0"

How to catch mouse left button on mouseover event?

I am trying to catch when user press left button on mouse while hovering over cells in a html table using vanilla javascript. The purpose is to paint a cell in black when user is clicking with mouse while dragging (drawing like in MsPaint, when you draw a line for example)
I added an "over" event listener on each td of my table and used buttons property to check if left button is pressed or not:
celle = document.getElementsByTagName("td");
for (i=0;i<celle.length;i++)
celle[i].addEventListener("mouseover", function(e){
if(e.buttons == 1 ){
e.target.style.backgroundColor="black";
}
})
This code works but not always and not perfectly. First it starts setting the background color of the next element, not the one on which I pressed the mouse. Moreover, sometimes it doesn't set any color at all (there is a small icon like "accessed denied" in Chrome's window). It appears to work quite randomly and unpredicatably.
I tried also with jQuery, but I found similar problems. Anyone can help me?
Thanks a lot
Split the problem into several parts. I would add a mousedown and mouseup eventlistener to the whole window and set a global state if you're currently drawing:
var drawState=false
window.addEventListener("mousedown",function(e){
if(e.button===1){
drawState = true;
}});
window.addEventListener("mouseup",function(e){
if(e.button===1){
drawState = false;
}});
You can improve the window listeners with some checks, if the mouse is over a cell.
After this you can add a mouseenter listener to all your cells. Mouseenter is only fired once you enter a cell and not on every move inside the element:
celle[i].addEventListener("mouseenter", function(e){
if(drawState){
e.target.style.backgroundColor="black";
}
})
Instead of tracking mouseover, track three events:
mousemove - to constantly get the mouse position
mousedown - to set the mouse state as currently clicked down
mouseup - to set the mouse state as currently released
It works this way:
handleMousemove constantly updates the mouse position and check mouse state
When the mouse is clicked down, handleMousedown is fired
handleMousedown set the state as 'down'
When handleMousemove sees that mouse state is 'down', it fires click event at the current mouse position
When the mouse is released, handleMouseup is fired
handleMouseup set the state as 'released' and everything returns to normal
Repeat
var mouseIsDown = false;
var mousePosition = { x:-1, y:-1 };
let handleMousemove = (event) => {
// get the mouse position
mousePosition.x = event.x;
mousePosition.y = event.y;
if(mouseIsDown) // if mouse state is currently down, fire click at mouse position
{
let elem = document.elementFromPoint(mousePosition.x, mousePosition.y);
// you can add some conditions before clicking
if(something)
{
elem.click();
}
}
};
let handleMousedown = (event) => {
mouseIsDown = true;
// set the mouse state as 'down'
};
let handleMouseup = (event) => {
mouseIsDown = false;
// set the mouse state as 'release'
};
document.addEventListener('mousemove', handleMousemove);
document.addEventListener('mousedown', handleMousedown);
document.addEventListener('mouseup', handleMouseup);
Working fiddle: https://jsfiddle.net/Black3800/9wvh8bzg/5/
Thanks to everybody for your kind answers. Proposed codes work almost ok. The only problem is that sometimes browser shows the NO SYMBOL cursor. Unfortunately I can't post an image but you can find it here:
NO Symbol
and the only way to keep on drawing is clicking outside the table and then clicking again inside.
This is my code:
var mouseIsDown = false;
var mousePosition = { x:-1, y:-1 };
let handleMousemove = (event) => {
// get the mouse position
mousePosition.x = event.x;
mousePosition.y = event.y;
if(mouseIsDown) // if mouse state is currently down, fire click at mouse position
{
let elem = document.elementFromPoint(mousePosition.x, mousePosition.y);
// you can add some conditions before clicking
if (event.buttons==1)
{
elem.click();
}
}
};
let handleMousedown = (event) => {
mouseIsDown = true;
// set the mouse state as 'down'
};
let handleMouseup = (event) => {
mouseIsDown = false;
// set the mouse state as 'release'
};
document.addEventListener('mousemove', handleMousemove);
document.addEventListener('mousedown', handleMousedown);
document.addEventListener('mouseup', handleMouseup);
celle = document.getElementsByTagName("td");
for (i=0;i<celle.length;i++)
celle[i].addEventListener("click", function(e){
e.target.style.backgroundColor="black";
}
)
Isn't it easier to just add a listener for "click" ? If the element is clicked it also over the cell.
celle[i].addEventListener("click", function(e){
e.target.style.backgroundColor="black";
}

Why event listener persists after manual removal

All,
I try to build a resizer UI like this:
My code is like:
<span class="grabber" draggable="false" #mousedown="grab"></span>
grab: function(e) {
var initX = e.screenX;
var mousemove = function(e) {
var offset = e.screenX - initX
initX = e.screenX;
}
var cancel = function(e) {
$(document).off("mousemove")
$(document).off("mouseup")
}
$(document).on("mousemove", mousemove)
$(document).on("mouseup", cancel)
mousemove = null;
cancel = null;
}
Basic idea is: I attach that grab event handler to mousedown, inside which I listen to mousemove until mouseup, then I remove those two event handlers from document.
I am pretty new to Chrome Performance tool, so I just simply record some drag of that resizer, then mouseup and drag again.
The result is confused, especially the number of listener goes up like crazy(but there seems no memory leak). I wonder where did I do wrong?
So what is happening here:
<span class="grabber" draggable="false" #mousedown="grab"></span>
every time mousedown happens vue runs grab
The safer thing to do in this case is attach the events directly to e.target also setting your handler function to null in the cancel function.
grab: function(e) {
var initX = e.screenX;
var target = e.target;
var mousemove = function(e) {
var offset = e.screenX - initX
initX = e.screenX;
}
var cancel = function(e) {
$(target).off("mousemove")
$(target).off("mouseup")
mousemove = null;
cancel = null;
}
$(target).on("mousemove", mousemove)
$(target).on("mouseup", cancel)
}
Use a flag variable instead of adding and removing the handler.
var mouseIsDown = false;
$(document).on("mousedown", function() {
mouseIsDown = true;
});
$(document).on("mouseup", function() {
mouseIsDown = false;
})
$(document).on("mousemove", function() {
if (mouseIsDown) {
// do what you want
}
});
The mousedown handler could be attached to specific elements that you can grab, rather than document.

JavaScript + Chrome - Event capture

I am trying to figure out how events are working. I have this piece of code:
document.captureEvents(Event.MOUSEMOVE);
document.onmousemove = mousePos;
document.onkeypress = keyPressed;
var keyStroke = 0;
var mouseX = 0;
var mouseY = 0;
function mousePos(e) {
console.log(e);
mouseX = e.pageX;
mouseY = e.pageY;
document.formex.mousex.value = mouseX;
document.formex.mousey.value = mouseY;
return true;
}
function keyPressed(e) {
console.log(e);
keyClicked = e.key;
document.formex.keypress.value = keyStroke;
return true;
}
What confuses me - inside of keyPressed function console.log(e) yields KeyboardEvent, inside of mousePos(e) it yields MouseEvent. I can't tell how the differentiation between these two 'e' happens?
What confuses me - inside of keyPressed function console.log(e) yields KeyboardEvent, inside of mousePos(e) it yields MouseEvent. I can't tell how the differentiation between these two 'e' happens?
The browser creates event objects and fires them at the document elements. When the event relates to the keyboard, the browser creates a KeyboardEvent. When it relates to the mouse, it fires a MouseEvent. If it relates to something else, it will create and fire a different kind of event.
The "differentiation" is based on what caused the event to be fired: A keyboard action or a mouse action.

How do I handle these events and why is my code not working?

I need to do the following. As soon as the user clicks on a div, i want to save the mouse coordinations while the user is moving the cursor over the div and is holding the left mouse button. When the user leaves the div or releases the left button, i want to stop recording the coordinates. I've got the following code:
$(document).ready(function() {
var coordhdl = new coordinateHandler();
$("#test").mousedown(function(e) {
$("#test").mousemove(function(ee) {
$("#test").mouseup(function(e) {
stopIt = true;
});
if(stopIt == false)
{
coordhdl.addCords(ee.pageX - this.offsetLeft, ee.pageY - this.offsetTop);
}
});
});
});
The problems with this code are:
It records coordinate even when the user only clicked the div without pressing the left button.
It doesn't stop recording the coordinates once it has been clicked.
I am new to Javascript/jQuery, so I don't know very much about it.
Something like this should work. It sets a flag to true/false when the mouse is pressed/released respectively. When the mouse moves, if the flag is set, the coordinates are added:
$(document).ready(function() {
var isDown = false,
coordhdl = new coordinateHandler();
$("#test").mousedown(function() {
isDown = true;
}).mouseup(function() {
isDown = false;
}).mousemove(function(e) {
if(isDown) {
coordhdl.addCords(ee.pageX - this.offsetLeft, ee.pageY - this.offsetTop);
}
});
});
Here's a demo of something similar in action (it simply writes the coordinates to a p element instead of using your coordinateHandler object).
Don't attach the event handlers inside the event handlers. On every mouse move you attach a new mouseup event handler. They don't get overridden, they get appended.
Use a "global" flag instead:
$(document).ready(function() {
var coordhdl = new coordinateHandler(),
recording = false;
$("#test").mousedown(function(e) {
recording = true;
}).mousemove(function(e) {
if(recording) {
coordhdl.addCords(e.pageX - this.offsetLeft, e.pageY - this.offsetTop);
}
}).mouseup(function(e) {
recording = false;
});
});
Every time there is a mousedown event, you add a mousemove handler, and every time the mouse moves, you add another mouseup handler. I can't see where the stopIt variable is declared so the scope of this variable may also be an issue. You don't need to nest the handlers, so try it this way.
$(document).ready(function() {
var coordhdl = new coordinateHandler();
var isRecording = false;
$("#test").mousedown(function(e) { isRecording = true })
.mouseup(function(e) { isRecording = false })
.mousemove(function(ee) {
if(isRecording)
{
coordhdl.addCords(ee.pageX - this.offsetLeft, ee.pageY - this.offsetTop);
}
});
});

Categories