I want to be able to click on a canvas, move it to the map, drop it on the map. After dropping it on the map, they can select the canvas on the map and move it again. The process is repeated. I have trouble with the ignoreMouseMove variable, it does not reset to false and is always true
Here is link to the demo: https://pokemon-map-electro2k.c9users.io/index.html
var moveCanvas = function ($canvas, e) {
$(".map ul li." + $canvas).offset({
left: e.pageX - 30,
top: e.pageY - 30
});
};
// When user first click on canvas
var onmousemove = function ($canvas) {
var ignoreMouseMove = false;
// Make canvas follow cursor in the map area
$(".map").mousemove(function (e) {
if (ignoreMouseMove) return; // event handling mousemove is "disabled"
moveCanvas($canvas, e);
}).click(function () {
// "re-enable" mousemove
ignoreMouseMove = true;
// When canvas is click on again within the map area, make canvas follow cursor
$(".map ul li").click(function () {
$(".map").mousemove(function (e) {
if (!ignoreMouseMove) return;
moveCanvas($canvas, e);
}).click(function () {
// Click function does not work anymore. ignoreMouseMove can't be reset. It is always true
ignoreMouseMove = false;
})
});
});
};
Instead of using .off(), use a boolean variable which indicates whether you want to ignore the mousemove event or not, but keeping the mousemove event handler in place:
var onmousemove = function ($canvas) {
var ignoreMouseMove = false;
// Make canvas follow cursor in the map area
$(".map").mousemove(function (e) {
if (ignoreMouseMove) return; // event handling is "disabled"
$(".map ul li." + $canvas).offset({
left: e.pageX,
top: e.pageY
});
}).click(function () {
ignoreMouseMove = true;
$(".map ul li").click(function () {
// "re-enable" mousemove
ignoreMouseMove = false;
});
});
};
Related
I want to execute function when a mouse move ends inside mouseDown Observer. But onComplete function doesn't execute, when i letdown my mouse. Any suggestions?
var split = $('.drag');
var parent = $('.Container');
var mouseDowns = Rx.Observable.fromEvent(split, "mousedown");
var parentMouseMoves = Rx.Observable.fromEvent(parent, "mousemove");
var parentMouseUps = Rx.Observable.fromEvent(parent, "mouseup");
var drags = mouseDowns.flatMap(function(e){
return parentMouseMoves.takeUntil(parentMouseUps);
});
drags.subscribe(
function(e) {
var $containerWidth = $('.Container').width();
var clientX = $containerWidth - e.clientX;
if (clientX >= 50 && e.clientX >= 50) {
$('.left').css('right', clientX);
$('.right').css('width', clientX);
}
},
function(error) {
console.log(error);
},
function() {
console.log('finished');
});
jsbin.com url
If you want to detect drag event end, a.k.a. drop, then something like this should do the trick:
var drop = mouseDowns.selectMany(
Rx.Observable.concat([
parentMouseMoves.take(1).ignoreElements(),
parentMouseUps.take(1)
])
);
drop.subscribe(function(e) {
console.log('finished');
});
If the outer-most function is suppose to return a value each time a drag completes, you need to convert the drag completion into an "onNext" event for the outer-most observable.
I want to drag an image according to mouse movement. The problem is whenever the drag starts, the image's top and left positions suddenly change with the mouse position.
When it starts it should not suddenly move to the mouse position. I want it to feel like normal dragging, but the position is changing whenever I start moving the mouse.
var mousedown = false;
var p;
var x=0,y=0;
var flag1 = 0;
$("#blue3").mousedown(function(){
if(mousedown) {
mousedown = false;
console.log("mouse down is true");
} else {
mousedown = true;
console.log("mouse down is true");
}
});
$("#blue3").mouseup(function(){
if(mousedown) {
mousedown = true;
console.log("mouse down is false");
} else {
flag1 = 1;
x = p.left;
y = p.top;
mousedown = false;
console.log("mouse down is false");
}
});
$("#blue3").mousemove(function(event) {
p = $("#blue3").position();
if(mousedown) {
console.log("mouse left" + event.pageX);
console.log("image left" + p.left);
console.log("mouse top" + event.pageY);
console.log("ima top" + p.top);
$("#blue3").css({"left" : event.pageX, "top" : event.pageY });
$("#blue").css({"left" : event.pageX, "top" : event.pageY});
}
});
there! i have a problem for getting #drag element moving smoothly.
i look at this article : http://www.html5rocks.com/en/tutorials/speed/animations/#debouncing-mouse-events
it said that : "the problem with mousemove event when moving element was mousemove event fired too much
so, i try to used their method : using requestAnimationFrame + boolean checking.
look at this fiddle for live action : https://jsfiddle.net/5f181w9t/
HTML :
<div id="drag">this is draggable</div>
CSS :
#drag {width:100px; height:50px; background-color:red; transform:translate3d(0, 0, 0); }
JS :
var el = document.getElementById("drag"),
startPosition = 0, // start position mousedown event
currentPosition = 0, // count current translateX value
distancePosition = 0, // count distance between "down" & "move" event
isMouseDown = false; // check if mouse is down or not
function mouseDown(e) {
e.preventDefault(); // reset default behavior
isMouseDown = true;
startPosition = e.pageX; // get position X
currentPosition = getTranslateX(); // get current translateX value
requestAnimationFrame(update); // request 60fps animation
}
function mouseMove(e) {
e.preventDefault();
distancePosition = (e.pageX - startPosition) + currentPosition; // count it!
}
function mouseUp(e) {
e.preventDefault();
isMouseDown = false; // reset mouse is down boolean
}
function getTranslateX() {
var translateX = parseInt(getComputedStyle(el, null).getPropertyValue("transform").split(",")[4]);
return translateX; // get translateX value
}
function update() {
if (isMouseDown) { // check if mouse is down
requestAnimationFrame(update); // request 60 fps animation
}
el.style.transform = "translate3d(" + distancePosition + "px, 0, 0)";
// move it!
}
el.addEventListener("mousedown", mouseDown);
document.addEventListener("mousemove", mouseMove);
document.addEventListener("mouseup", mouseUp);
is this the correct way to accompolished it?
what's wrong with my code?
thanks
The problem is that you are using requestAnimationFrame() in the mouseDown event listener. You should do all you updates in the mouseMove event listener because you want to update your display when the mouse moves not when mouse clicks. Accordingly you should update all your variables under the isMouseDown conditional in the update function. i would suggest correcting the code as follows.
HTML
<div id="drag">this is draggable</div>
CSS
#drag {
width:100px;
height:50px;
background-color:red;
transform:translateX(0);
}
JS
var el = drag,
startPosition = 0, // start position mousedown event
currentPosition = 0, // count current translateX value
distancePosition = 0, // count distance between "down" & "move" event
isMouseDown = false, // check if mouse is down or not
needForRAF = true; // to prevent redundant rAF calls
function mouseDown(e) {
e.preventDefault(); // reset default behavior
isMouseDown = true;
currentPosition = getTranslateX(); // get current translateX value
startPosition = e.clientX; // get position X
}
function mouseMove(e) {
e.preventDefault();
distancePosition = (e.clientX - startPosition) + currentPosition; // count it!
if (needForRAF && isMouseDown) {
needForRAF = false; // no need to call rAF up until next frame
requestAnimationFrame(update); // request 60fps animation
};
}
function mouseUp(e) {
e.preventDefault();
isMouseDown = false; // reset mouse is down boolean
}
function getTranslateX() {
var translateX = parseInt(getComputedStyle(el, null).getPropertyValue("transform").split(",")[4]);
return translateX; // get translateX value
}
function update() {
needForRAF = true; // rAF now consumes the movement instruction so a new one can come
el.style.transform = "translateX(" + distancePosition + "px)";// move it!
}
el.addEventListener("mousedown", mouseDown);
document.addEventListener("mousemove", mouseMove);
document.addEventListener("mouseup", mouseUp);
check it up here
Your code should already work fine. However, here's another way to do it:
You need to make sure you only let one requestAnimationFrame call go through per frame, otherwise update() will be called multiple times at the next repaint, which can cause lag and decrease your fps. To do this, you want to save the requested frame and on each mousemove event check if there's already a frame lined up. If there is, you'll want to use cancelAnimationFrame to cancel it and make a new request. This way update() is only called as often as the browser is able to render changes (I.E. 60fps in most browsers).
function mouseDown(e) {
e.preventDefault(); // cancel default behavior
isMouseDown = true;
startPosition = e.pageX; // get position X
currentPosition = getTranslateX(); // get current translateX value
}
var lastUpdateCall=null;
function mouseMove(e){
if(isMouseDown){ //check if mousedown here, so there aren't any unnecessary animation frame requests when the user isn't dragging
e.preventDefault(); // You probably only want to preventDefault when the user is actually dragging
if(lastUpdateCall) cancelAnimationFrame(lastUpdateCall); //if an animation frame was already requested after last repaint, cancel it in favour of the newer event
lastUpdateCall=requestAnimationFrame(function(){ //save the requested frame so we can check next time if one was already requested
distancePosition = (e.clientX - startPosition) + currentPosition; // Do the distance calculation inside the animation frame request also, so the browser doesn't have to do it more often than necessary
update(); //all the function that handles the request
lastUpdateCall=null; // Since this frame didn't get cancelled, the lastUpdateCall should be reset so new frames can be called.
});
}
}
function update(){
el.style.transform = "translateX(" + distancePosition + "px)";// move it!
}
You could also just not call requestAnimationFrame again if lastUpdateCall isn't null, but that would mean you'd have to calculate the distance outside the animation frame call every time the event fires, or the animation would lag behind your mouse by up to 20ms. Either method is fine, I suppose.
I am trying to change the length of two bars (div) by mouse dragging (extending one example in eloquetjavascript book chapter 14, which involves changing the length of one bar by dragging the mouse.) The intended behavior is clicking on any bar, moving the mouse when holding the left mouse key would change the length of that bar.
Here is my implementation (also available on JSfiddle)
<script>
var lastX; // Tracks the last observed mouse X position
var rect1 = document.getElementById("bar1");
var rect2 = document.getElementById("bar2");
rect1.addEventListener("mousedown", function(){watchmousedown(rect1)});
rect2.addEventListener("mousedown", function(){watchmousedown(rect2)});
function watchmousedown(rec) {
if (event.which == 1) {
lastX = event.pageX;
addEventListener("mousemove",function(){moved(rec)});
event.preventDefault(); // Prevent selection
} else {
removeEventListener("mousedown", watchmousedown)}
}
function moved(rec) {
if (event.which != 1) {
removeEventListener("mousemove", moved);
} else {
var dist = event.pageX - lastX;
var newWidth = Math.max(10, rec.offsetWidth + dist);
rec.style.width = newWidth + "px";
lastX = event.pageX;
}
}
</script>
The problem is I can only change the length of the bar where the first mouse click event happened. I assume I didn't handle the mousedown event correctly (probably need a reset some how).
I am new to javascript, help on programming style is also appreciated.
Thanks!
Add rec. to addEventListener("mousemove", function () { so that the event listener is bound to the rec you clicked on instead of to the window.
function watchmousedown(rec) {
if (event.which == 1) {
lastX = event.pageX;
rec.addEventListener("mousemove", function () {
moved(rec)
});
event.preventDefault(); // Prevent selection
} else {
rec.removeEventListener("mousedown", watchmousedown)
}
}
Edit: I there are some event handlers not being cleaned up properly. I don't know if this would be my final code, but this is closer to how I would do it:
var lastX; // Tracks the last observed mouse X position
var rect1 = document.getElementById("bar1");
var rect2 = document.getElementById("bar2");
var moveRect1 = function () {
console.log(arguments);
moved(rect1)
};
var moveRect2 = function() {
console.log(arguments);
moved(rect2);
}
var watchRect1 = function () {
console.log(arguments);
watchmousedown(moveRect1)
};
var watchRect2 = function () {
console.log(arguments);
watchmousedown(moveRect2)
};
rect1.addEventListener("mousedown", watchRect1);
rect2.addEventListener("mousedown", watchRect2);
window.addEventListener("mouseup", function() {
removeEventListener("mousemove", moveRect1);
removeEventListener("mousemove", moveRect2);
});
function watchmousedown(moverec) {
if (event.which == 1) {
lastX = event.pageX;
addEventListener("mousemove", moverec);
event.preventDefault(); // Prevent selection
}
}
function moved(rec) {
if (event.which == 1) {
var dist = event.pageX - lastX;
var newWidth = Math.max(10, rec.offsetWidth + dist);
rec.style.width = newWidth + "px";
lastX = event.pageX;
}
}
Edit: removed a line that didn't do anything
I am curious if binding an event to an object multiple times cause problems by stacking multiple event listeners on top of each other or do they override each other? For example if I have an attachHandlers function
function _attachHandlers() {
// Slider hover
var isDown = false;
$('.ui-slider-handle').mousedown(function () {
// Create tool tip
isDown = true;
var tt = $(document.createElement('span')).addClass('sk-tooltip');
var handle = $(this);
var left = parseInt(handle.css('left'));
var val = _convertSliderValue(Math.round(left / 5 / 14.2857));
tt.text(val);
handle.append(tt);
var newLeft = (handle.outerWidth() - tt.outerWidth()) / 2;
tt.css({
'left': newLeft
});
$(document).mousemove(function (event) {
var left = parseInt(handle.css('left'));
var val = _convertSliderValue(Math.round(left / 5 / 14.2857));
tt.text(val);
var newLeft = (handle.outerWidth() - tt.outerWidth()) / 2;
tt.css({
'left': newLeft
});
});
});
$(document).mouseup(function () {
if (isDown) {
$(document).unbind('mousemove');
$(this).find('.sk-tooltip').remove()
isDown = false;
}
});
}
I need to re-attach these handlers at some point in my code and therefore I was just going to call _attachHandlers() to re-bind them, however, I will also be re-binding the document listener for the mouseup event every time this happens. Therefore, is it alright to do this and will the event handler just get overwritten everytime or do I have to unbind the handler first before it can be re-bound?