I need to detect the mouse pressed event in jQuery. I see this question and it's answer. It is the exact one I needed, because here the action is happen only if the mouse is pressed and moved. It is not a click event, it is combined mouse press and move event.
I don't understand how to implement it. Currently I rotate the div using the following code, but this code is based on the mousedown event:
var rotation = 0;
jQuery.fn.rotate = function(degrees) {
$(this).css({'transform' : 'rotate('+ degrees +'deg)'});
};
$('.resizeButton').on("mousedown", function(e) {
var startX = e.pageX;
$(document).mousemove(function(e){
rotation = startX - e.pageX;
$('.test').rotate(rotation);
});
});
$('.resizeButton').on("mouseup", function(){
$(document).unbind( "mousemove" );
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="test">Hello
<button class="resizeButton"> </button>
</div>
Please help to convert it to mouse press and move event, so that the user can easily adjust the rotation by pressing that button and rotate to any direction and release the mouse press. When the user releases the mouse press and then rotation needs to set on that particular mouse press release point.
There are some issues in your code.
First of all, the "mouseup" event handler doesn't get fired because your mouse isn't over the checkbox when you release the mouse. You have to bind the event to the document for example.
Then, instead of binding and unbinding event handlers, it's probably easier to store the state of the mouse, and rotate only if the mouse is pressed.
var rotation = 0;
var rotating = false;
var startX = 0;
jQuery.fn.rotate = function(degrees) {
$(this).css({'transform' : 'rotate('+ degrees +'deg)'});
};
$(document).mousemove(function(e){
if (!rotating) return;
rotation = startX - e.clientX;
$('.test').rotate(rotation);
});
$(document).on("mouseup", function(){
rotating = false;
});
$('.resizeButton').on("mousedown", function(e) {
rotating = true;
startX = e.clientX;
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="test">Hello
<button class="resizeButton"> </button></div>
To have the button follow the mouse pointer during rotation, you would probably need to use some sin and cos functions to determine the angle of rotation, and you'd probably need to adjust the radius too.
Related
I have a fixed window on my screen that I would like the user to be able to drag around the screen. While I have set this up to work for mouse inputs, I am having more difficulty getting this to work for touch screens.
So I have a basic box like so:
<div id="smallavatar" class="smallbox">
</div>
Which is then immediately followed by this JS (note that the first half is for the click events)
var divOverlay = document.getElementById ("smallavatar");
var isDown = false;
divOverlay.addEventListener('mousedown', function(e) {
isDown = true;
}, true);
document.addEventListener('mouseup', function() {
isDown = false;
}, true);
document.addEventListener('mousemove', function(event) {
event.preventDefault();
if (isDown) {
var deltaX = event.movementX;
var deltaY = event.movementY;
var rect = divOverlay.getBoundingClientRect();
divOverlay.style.left = rect.x + deltaX + 'px';
divOverlay.style.top = rect.y + deltaY + 'px';
}
}, true);
divOverlay.addEventListener('touchstart', e => {
clientX = e.touches[0].screenX;
clientY = e.touches[0].screenY;
});
divOverlay.addEventListener('touchmove', e => {
let x = divOverlay.style.left;
let y = divOverlay.style.top;
// Compute the change in X and Y coordinates.
// The first touch point in the changedTouches
// list is the touch point that was just removed from the surface.
deltaX = e.changedTouches[0].clientX - clientX;
deltaY = e.changedTouches[0].clientY - clientY;
divOverlay.style.left = divOverlay.style.left + deltaX;
divOverlay.style.top = divOverlay.style.top + deltaY;
});
Finally the following CSS is being used:
.smallbox{position:fixed; bottom:70px; right:0; width:360px; height:400px; z-index:20;}
Now while the touchmove event does work, the problem is that the box seems to fly off the screen very quickly. The idea is supposed to be that I'm trying to get the position the div was using before the touch event started, then work out how much movement has occured in the touchmove event and then from that update the left and top values with the new position it should be in.
Is there anything wrong that would explain why this isn't working as expected?
You have one major flaw in your code that causes the unexpected result:
At touchstart you store clientX and clientY, and than during touchmove you keep referencing those two values at deltaX = e.changedTouches[0].clientX - clientX; (and Y).
But while e.changedTouches[0].clientX keeps updating, clientX never gets updated and always keeps its initial start-value.
So for the next frame/iteration, deltaX = e.changedTouches[0].clientX - clientX; will actually be the delta-x of that last frame, plus the delta-x of the frame before, and the one before, and before, and so on till the very first frame.
So your divOverlay will be moving exponentially, and be out of sight in a fraction of a sec.
What the best choice is for getting the current position of divOverlay, depends a little on your situation, although I think in most cases, using pageX/pageY is probably your safest bet.
The other options are clientX/clientY and screenX/screenY, see this and this article for the differences between the three.
In any case, I wouldn't recommend using them interchangeably during one calculation, unless it's absolutely necessary.
See the live snippet below for the solution:
var divOverlay = document.getElementById("smallavatar");
/*MOUSE-LISTENERS---------------------------------------------*/
var isDown=false;
divOverlay.addEventListener('mousedown',function(){isDown=true;},true);
document.addEventListener('mouseup',function(){isDown=false;},true);
document.addEventListener('mousemove',function(e) {
e.preventDefault();
if (isDown) {
divOverlay.style.left = divOverlay.offsetLeft + e.movementX + 'px';
divOverlay.style.top = divOverlay.offsetTop + e.movementY + 'px';
}
},true);
/*TOUCH-LISTENERS---------------------------------------------*/
var startX=0, startY=0;
divOverlay.addEventListener('touchstart',function(e) {
startX = e.changedTouches[0].pageX;
startY = e.changedTouches[0].pageY;
});
divOverlay.addEventListener('touchmove',function(e) {
e.preventDefault();
var deltaX = e.changedTouches[0].pageX - startX;
var deltaY = e.changedTouches[0].pageY - startY;
divOverlay.style.left = divOverlay.offsetLeft + deltaX + 'px';
divOverlay.style.top = divOverlay.offsetTop + deltaY + 'px';
//reset start-position for next frame/iteration
startX = e.changedTouches[0].pageX;
startY = e.changedTouches[0].pageY;
});
.smallbox{position:fixed; bottom:70px; right:0; width:36px; height:40px; z-index:20; background:red;}
<div id="smallavatar" class="smallbox"></div>
jsfiddle: https://jsfiddle.net/2hdczkwn/4/
To address your major flaw, I added startX = e.changedTouches[0].pageX; and startY = e.changedTouches[0].pageY; to the end of your touchmove listener, resetting the start-position for each new frame/iteration to the end-position of the last frame/iteration.
I also added e.preventDefault(); to your touchmove listener, because otherwise any scrollable page would also start scrolling while you were dragging the divOverlay.
I converted your arrow functions into traditional ones, like your mouse listeners.
I added var startX=0, startY=0; outside the functions to make sure they are global vars.
I replaced var rect = divOverlay.getBoundingClientRect(); and rect.x and rect.y inside your mousemove listener, and divOverlay.style.left and divOverlay.style.top inside your touchmove listener, with divOverlay.offsetLeft and divOverlay.offsetTop.
This is actually quite important, since divOverlay.style.left returns a px value, e.g. 57px. When this value is used in a calculation, like 57px + 12, it will not result in 69 or 69px but instead in NaN.
I can't help but notice that your mouse and touch listeners are written in a different coding style, mouse uses traditional JS, while touch is written in the new ES6, using let and arrow functions.
As if you copied the touch listeners from someone else and didn't change it to match the rest of your code.
While it is fine to use either style and to use other people's code (as long as you have permission), I would discourage using them both in the same project, and it is never a good idea to simply copy someone else's code without rewriting it to fit your own style.
This will make sure your whole project retains its current style of coding, and has the added advantage of you actually understanding the code you're using.
I know that with jquery I can select an element and invoke a click. However, what I am looking to do is simply invoke a click on where ever the current cursor is.
For example something like this:
jQuery().click();
I think this might help:
How to simulate a click by using x,y coordinates in JavaScript?
maybe something like:
$(document).click(function(e) {
var x = e.clientX;
var y = e.clientY;
document.elementFromPoint(x, y).click();
})
Getting the user's mouse position can only be done via an event. Let's say your mouse is broken and it will track, but won't click, you could trigger a 'fake' click at the current position through other means:
var x, y;
$(document).mousemove(function(e) {
x = e.clientX;
y = e.clientY;
})
$(document).keyup(function(e) {
// if the key pressed was return, click at the current cursor position
if (e.keyCode === 13) {
document.elementFromPoint(x, y).click();
}
})
$(document).keyup(function(e) {
// if the key pressed was space, click 50px below the current cursor position
if (e.keyCode === 32) {
document.elementFromPoint(x, y + 50).click();
}
})
tested and working, now you can click with your keyboard!
Note that document.elementFromPoint(x, y) looks for elements at the coordinates relative to the viewport. So you'll be using e.clientX & e.clientY, not e.pageX & e.pageY.
I have a Javascript that scrolls an UL from left to right depending on where the mouse is positioned over it: A demo can be seen HERE (site still under construction) I would like it to work with touchscreen devices also. Whereby touching and "dragging" ones finger would scroll the UL in a similar manner, tapping on the list would then "click" on an image.
How easy/hard would that be to modify the JS:
$(function(){
$(window).load(function(){
var $gal = $("#gallerylist.top"),
galW = $gal.outerWidth(true),
galSW = $gal[0].scrollWidth,
wDiff = (galSW/galW)-1, /// widths difference ratio
mPadd = 200, // Mousemove Padding
damp = 20, // Mousemove response softness
mX = 0, // Real mouse position
mX2 = 0, // Modified mouse position
posX = 0,
mmAA = galW-(mPadd*2), // The mousemove available area
mmAAr = galW/mmAA; /// get available mousemove fidderence ratio
$gal.mousemove(function(e) {
mX = e.pageX - $(this).parent().offset().left - this.offsetLeft;
mX2 = Math.min( Math.max(0, mX-mPadd), mmAA ) * mmAAr;
});
setInterval(function(){
posX += (mX2 - posX) / damp; /// zenos paradox equation "catching delay"
$gal.scrollLeft(posX*wDiff);
}, 10);
});
});
There are touch events similar to mouse events:
$gal.touchstart(function(e) {});
$gal.touchend(function(e) {});
$gal.touchmove(function(e) {});
Usually you don't need to implement touch events for 'click'... they are usually already in sync.
For the mousemove event, it may be more desirable from the user's perspective to have this functionality disabled on a touch device. If the page needs to scroll, this could interfere with that interaction. An alternative may be to have some animation on a window scroll event.
I've got a home-made slider made from jQuery UI's draggable() function:
$("#petrolGauge .fuelBar .slider").draggable({
containment: "parent",
axis: "x",
drag:function(){
updValues();
},
start:function(){
$(this).css("background-color","#666");
},
stop:function(){
//checkForm();
$(this).css("background-color","#AAA");
}
});
This is for the following markup:
<div id="petrolGauge">
<input id="endPet" name="endPet" type="hidden" value="0">
How much fuel was left in the tank when you were finished? (Use the slider) <b>(~<span class="petLeft">0</span>%)</b>
<span class="mandatory">*</span><br />
<div class="fuelBar">
<div title="Drag" class="slider"></div>
</div>
This works a treat, when I click on the slider. But I'd like it so that when I click the fuel bar (the slider's parent) the slider not only starts dragging but also jumps to the cursor. I've achieved it by doing this:
$("#petrolGauge .fuelBar").on("mousedown",function(e){
slider = $("#petrolGauge .fuelBar .slider");
left = e.pageX-($(this).offset().left)-(slider.width()/2);
updValues();
slider.css("left",left).trigger(e);
});
Two problems with this:
Firstly, when clicking on the parent I get a couple of second's delay before the slider starts to drag? I've tried and tested this in Chrome and IE and both do it. Secondly if the cursor is less than half of the slider's width away from the edge of the parent, the slider will move to the outside of the parent. Wouldn't be hard to fix this with a couple of checking, but was wondering if there was another way? I'm suprised that draggable() doesn't have any parameters for this to be honest. I didn't want to use slider() if I could help it but if it's the only way, then it's the only way.
Here's a fiddle to work with.
The reason you get the delay is because you use .trigger() inside the .on() event which creates a big loop. As a result the loop slows down the moving process.
$("#petrolGauge .fuelBar").click(function (e) { // use click instead of mousedown
slider = $("#petrolGauge .fuelBar .slider");
left = e.pageX - ($(this).offset().left) - (slider.width() / 2);
if(left > 570) { left = 570; } else if(left < 0) { left = 0; }
// it looks like a draggable bug due to the manual position change, so use a small check
slider.css("left", left); // change the position first
updValues(); // then calculate and update the div
// no need to trigger the event a second time because it will loop until jQuery exceeds it's trigger limit.
});
Here's an updated FIDDLE
Updated answer
To make .slider move accordingly to the mouse movement when not directly dragged, bind a mousemove event to the mousedown and unbind it when mouseup. Then in .mousemove() you change the position of .slider.
var move = function (e) {
left = e.pageX - ($('#petrolGauge .fuelBar').offset().left) - (slider.width() / 2);
if (left > 570) {
left = 570;
} else if (left < 0) {
left = 0;
}
slider.css("left", left);
updValues();
};
var slider = $("#petrolGauge .fuelBar .slider");
$("#petrolGauge .fuelBar").mousedown(function (e) {
e.preventDefault();
left = e.pageX - ($(this).offset().left) - (slider.width() / 2);
if (left > 570) {
left = 570;
} else if (left < 0) {
left = 0;
}
slider.css("left", left)
$(this).bind('mousemove', move);
updValues();
}).mouseup(function () {
$(this).unbind('mousemove');
});
I have a div with a javascript touchmove event listener that scrolls the image inside the div horizontally on iOS6 Mobile Safari. I'd like allow vertical scrolling of the page to bubble up to the browser but when this occurs, jQuery.animate no longer works.
I've posted a simplified version of the code that demonstrates the problem at
https://gist.github.com/4047733
The steps I take to recreate the problem are:
Swipe picture left/right and notice how it animates back to the left edge
Touch the picture and scroll the page up/down
Repeat left/right swipe and notice the picture does NOT animate back to the left edge. It appears jQuery animate fails after touchmove occurs without e.preventDefault
Here is the javascript inside jQuery document ready from the gist link above
var el = document.getElementById("swipebox"),
$slider = $('#swipebox').find('img'),
startX, startY, dx, dy,
startLeft,
animateH = false,
animateV = false;
var onTouchStart = function(e) {
startLeft = parseInt($slider.css('left'), 10) || 0;
startX = e.touches[0].pageX;
startY = e.touches[0].pageY;
};
var onTouchMove = function(e) {
dx = e.touches[0].pageX - startX;
dy = e.touches[0].pageY - startY;
if (
animateH ||
(!animateV && Math.abs(dx) > 5)
) {
// prevent default, we are scrolling horizontally,
animateH = true;
$slider.stop().css({'left': startLeft+dx*2});
e.preventDefault();
} else if (Math.abs(dy) > 5) {
// do NOT prevent default, we are scrolling the page vertically
animateV = true;
} else {
// direction of scroll is undetermined at this time
// we've moved less than 5px in any direction
e.preventDefault();
}
return false;
};
var onTouchEnd = function(e) {
$slider.stop().animate({'left': 0}); // animate image back to left
e.preventDefault();
animateH = false;
animateV = false;
};
var onTouchCancel = function(e) {
console.log('onTouchCancel');
};
el.addEventListener('touchstart', onTouchStart, false);
el.addEventListener('touchmove', onTouchMove, false);
el.addEventListener('touchend', onTouchEnd, false);
el.addEventListener("touchcancel", onTouchCancel, false);
Any insight would be greatly appreciated.
This is bug in iOS6. jQuery animate timers fail when scrolling window in iOS6.
Currently there are few workarounds on this:
Create your own timer functions like someone did: https://gist.github.com/3755461
Use CSS3 transition instead of jQuery.animate. This is preffered way - css3 transitions doesn't have such problem. You can use this jquery plugin http://ricostacruz.com/jquery.transit/ to easily manipulate CSS transitions in JavaScript.