I've created a slider. I have a touch event allow the user to slide using touch but the mouse event isn't working.
I'd like a mouse drag option as a fall back option. I thought a mouse event would work as standard.
I have the touch working but I can't get the mouse touch to work with it.
Here's the code. What am I doing wrong?
function swipedetect(el, callback){
var touchsurface = el,
swipedir,
startX,
startY,
distX,
distY,
threshold = 50, //required min distance traveled to be considered swipe
restraint = 100, // maximum distance allowed at the same time in perpendicular direction
allowedTime = 300, // maximum time allowed to travel that distance
elapsedTime,
startTime,
handleswipe = callback || function(swipedir){}
touchsurface.addEventListener('touchstart', function(e){
var touchobj = e.changedTouches[0]
swipedir = 'none'
dist = 0
startX = touchobj.pageX
startY = touchobj.pageY
startTime = new Date().getTime() // record time when finger first makes contact with surface
e.preventDefault()
}, false)
// Bind the functions...
el.onmousedown = function () {
_drag_init(this);
return false;
};
touchsurface.addEventListener('touchmove', function(e){
e.preventDefault() // prevent scrolling when inside DIV
}, false)
touchsurface.addEventListener('touchend', function(e){
var touchobj = e.changedTouches[0]
distX = touchobj.pageX - startX // get horizontal dist traveled by finger while in contact with surface
distY = touchobj.pageY - startY // get vertical dist traveled by finger while in contact with surface
elapsedTime = new Date().getTime() - startTime // get time elapsed
if (elapsedTime <= allowedTime){ // first condition for awipe met
if (Math.abs(distX) >= threshold && Math.abs(distY) <= restraint){ // 2nd condition for horizontal swipe met
swipedir = (distX < 0)? 'left' : 'right' // if dist traveled is negative, it indicates left swipe
}
else if (Math.abs(distY) >= threshold && Math.abs(distX) <= restraint){ // 2nd condition for vertical swipe met
swipedir = (distY < 0)? 'up' : 'down' // if dist traveled is negative, it indicates up swipe
}
}
handleswipe(swipedir)
e.preventDefault()
}, false)
}
//USAGE:
var input = Inputs.Input;
const el = input.getDOMElement();
swipedetect(el, function(swipedir){
if (swipedir =='left') {
// alert(swipedir);
Outputs.swipeLeft()
}
else if (swipedir === 'right') {
// alert(swipedir);
Outputs.swipeRight()
}
});
Outputs.Done ();
I'm having a problem with moving an object on the canvas but only on x or y-axis once at a time.
Idea:
The user can drag an object with CTRL / Shift pressed and then he's able to move an object on the x-axis or y-axis only. I move an object on an axis that I'm further away from starting position. This feature is present in most vector software (Corel, Inkscape, etc.).
On this video you can see what I mean:
https://www.youtube.com/watch?v=9AheCfh13Aw
To be honest - I don't know where to start. I guess I have to track the origin position of the object while dragging so I can check which axis should be locked while mouse movement.
Forked jsFiddle I'm using for developing:
https://jsfiddle.net/sores/1emj47q9/38/
Mouse movement event listener:
canvas.addEventListener('mousemove', function(e) {
if (myState.dragging) {
console.warn('mouseMove and dragging');
console.warn('object position: ', myState.selection);
var mouse = myState.getMouse(e);
console.warn('mouse: ', mouse);
// We don't want to drag the object by its top-left corner, we want to drag it
// from where we clicked. Thats why we saved the offset and use it here
// get the very first position of the object?
myState.selection.x = mouse.x - myState.dragoffx;
// y locked
// myState.selection.y = mouse.y - myState.dragoffy;
console.warn('new position: ', myState.selection);
myState.valid = false; // Something's dragging so we must redraw
}
}, true);
If anyone is familiar with such a thing I will be very grateful for any tips.
Thanks!
This answer is posted as code untested. The theory should point you in the right direction, though. I've commented the additions I've made to hopefully make it clearer but the basic rundown is;
Get the current mouse position
Move the mouse
Get the new mouse position
Compare the new mouse position against the old one
The biggest difference in positions determines the axis
When you've determined the direction (such as y), reset the position of the other axis (in this case, x) as it'll probably move slightly. I haven't included code for this.
// A reference to the previous mouse position
let initialMousePosition = {
x: 0,
y: 0
}
// A reference to locked axis that can be set or unset later
let dragLock: {
x: false,
y: false
}
// A reference to the control key
let ctrlPressed = false;
// Moved previousMousePosition logic in to on mouse down for increased reliability
canvas.addEventListener('mousedown', function(e){
var mouse = myState.getMouse(e);
initialMousePosition.x = mouse.x;
initialMousePosition.y = mouse.y;
})
canvas.addEventListener('mousemove', function(e) {
if (myState.dragging) {
var mouse = myState.getMouse(e);
// check for 0 positions to skip the first tick
if (initialMousePosition.x > 0 || initialMousePosition.y > 0) {
// compare previous mouse x & y positions to the current mouse positions
// assume the biggest difference is the direction of dragging
if (mouse.x - initialMousePosition.x > mouse.y - initialMousePosition.y) {
dragLock.y = true;
} else {
dragLock.x = true;
}
}
if (!dragLock.x || !ctrlPressed) myState.selection.x = mouse.x - myState.dragoffx;
if (!dragLock.y || !ctrlPressed) myState.selection.y = mouse.y - myState.dragoffy;
myState.valid = false; // Something's dragging so we must redraw
}
}, true);
//
canvas.addEventListener('keydown', function(e) {
if (event.which == "17")
ctrlPressed = true;
});
canvas.addEventListener('keyup', function(e) {
ctrlPressed = false;
})
this is a simple question.
Is there a way in jQuery or Javascript to determine the speed of a touchmove event?
I use css to grab the element and make it grabable, this works fine but if I move the finger faster, it's less likely I can move to a threshold distance but I did intend to turn page, so is there a way I can determine the speed of movement on the touch move event in javascript or jQuery, and I can adjust the threshold to a smaller value to compensate the speed?
var startX, endX, difference, threshold;
var startTime, endTime, timeDiff = 151;
$('#animate')
.bind("touchstart", function (e){
e.preventDefault();
var d = new Date();
startTime = d.getTime();
startX = e.originalEvent.touches[0].pageX; //starting point
})
.bind("touchmove", function (e){
e.preventDefault();
endX =e.originalEvent.changedTouches[0].pageX; //Get the information for finger
difference = startX - endX; //calculate the distance moved.
var moved = minusScreen - difference; //determine the affected css value.
$(this).css("left",moved); //this makes the element moves with my finger.
})
.bind("touchend", function (e) {
var date = new Date();
endTime = date.getTime();
threshold = Math.abs(difference);
timeDiff = endTime - startTime;
if ((threshold > (screenWidth * 0.4)) || (timeDiff < 150)) //make the animation move only when the finger moved more than 30% of the page.
{
if (endX > startX) turnLeft();
else if (endX == startX) {} // havent decide what to do yet
else turnRight();
} else {
$(this).animate({"left": minusScreen}, 100);
}
startX=0; //set the value back to initial.
endX=0; //set the value back to initial.});
});
thank's for your great answer. the above is modified code. worked great!!!
get the time on the touchstart and again on touchend like this
startTime = new Date().getTime() and endTime = new Date().getTime()
then calculate var speed = abs(endX-startX)/(endTime-startTime) this is now your overall touchmove speed in px/ms
Although this is an old question, it seems that there is a "timeStamp" in touch event object, so it might be simpler and faster to simply use :
startTime = e.timeStamp();
in place of :
var d = new Date();
startTime = d.getTime();
Same thing for var endTime
I've written the following script with the simple purpose of scrolling to the right when the user hovers over the right side of the screen and scrolling to the left when the user hovers over the left side of the screen. It works fine except that if you leave the mouse in the same spot for too long, then scrolling will stop before reaching the end. It begins scrolling again if you subsequently move the mouse. I can't understand why this is happening, since the code initiates an infinite timed loop which checks mouse position and scrolls accordingly. Its as if the mouse position stops being reported if the mouse is inactive for too long. Any ideas?
var mouseX = 0;
var scrollX = 0;
var timer;
$(document).ready(function() {
// Record the mouse position if the mouse is moved
$(document).mousemove(function(e) {
mouseX = e.pageX;
});
// Record the scroll position if the page is scrolled
$(document).scroll(function() {
scrollX = $(window).scrollLeft();
});
// Initiate the scrolling loop
scroll();
});
function scroll() {
// If the user is hovering over the right side of the window
if ((mouseX - scrollX) > 0.75*$(window).width()) {
scrollX += 1;
$(window).scrollLeft(scrollX);
}
// If the user is hovering over the left side of the window
if ((mouseX - scrollX) < (0.25*$(window).width())) {
scrollX -= 1;
$(window).scrollLeft(scrollX);
}
// Repeat in 5 ms
timer = window.setTimeout('scroll()', 5);
}
I don't know exactly what's wrong with your code, but why don't you use jQuery's animation?
It's more reliable than writing your own.
//inside $(document).ready():
var which = 0;
$('body').mousemove(function(e) {
var w_width = $(window).innerWidth();
var prc = (e.pageX - $(window).scrollLeft())/w_width;
var next_which = prc < 0.25 ? -1 : (prc > 0.75 ? 1 : 0);
if (next_which == which)
return;
which = next_which;
$('html,body').stop(true);
if (which != 0)
$('html,body').animate({scrollLeft: (which > 0 ? $(document).innerWidth()-w_width : 0)}, 2000);
}).mouseleave(function() {
$('html,body').stop(true);
which = 0;
});
See fiddle
jQuery's mousemove() event fails to fire when e.pageX > $(window).width() (or thereabouts). Looks like a jQuery bug to me. That could be impeding your progress!
You can easily see the problem on the first page here: http://m.vancouverislandlife.com/
Scroll down (slide up) and allow the content to leave the page, and it doesn't bounce back and is lost forever. However, on pages whose content does overflow the page and is therefore supposed to be scrollable, the scrolling works correctly (see Accomodations > b&b's and scroll down for an example of this).
I noticed that on my computer, the scrolling on the first page is always stuck at -899px. I can't find anybody else who's experienced this problem and no matter what I try, I just can't fix it! Help!
(It's not exactly urgent, however, as the target audience of iPhones and iPod Touches aren't affected by this since they have so little screen room.)
Okay, new problem. To solve the iScroll issue, I just created a custom script. However, it's not working correctly on the actual device. On desktop browsers, it works just fine. On mobile, it occasionally jumps back to the top and won't recognize some touches. This is probably because of the way I cancelled the default event and had to resort to a bit of a hack. How can I fix this? (Yup - simple problem for a +500 bounty. Not bad, huh?)
Here's the script, and the website is at the usual place:
function Scroller(content) {
function range(variable, min, max) {
if(variable < min) return min > max ? max : min;
if(variable > max) return max;
return variable;
}
function getFirstElementChild(element) {
element = element.firstChild;
while(element && element.nodeType !== 1) {
element = element.nextSibling;
}
return element;
}
var isScrolling = false;
var mouseY = 0;
var cScroll = 0;
var momentum = 0;
if("createTouch" in document) {
content.addEventListener('touchstart', function(evt) {
isScrolling = true;
mouseY = evt.pageY;
evt.preventDefault();
}, false);
content.addEventListener('touchmove', function(evt) {
if(isScrolling) {
evt = evt.touches[0];
var dY = evt.pageY - mouseY;
mouseY = evt.pageY;
cScroll += dY;
momentum = range(momentum + dY * Scroller.ACCELERATION, -Scroller.MAX_MOMENTUM, Scroller.MAX_MOMENTUM);
var firstElementChild = getFirstElementChild(content);
content.style.WebkitTransform = 'translateY(' + range(cScroll, -(firstElementChild.scrollHeight - content.offsetHeight), 0).toString() + 'px)';
}
}, false);
window.addEventListener('touchend', function(evt) {
isScrolling = false;
}, false);
} else {
content.addEventListener('mousedown', function(evt) {
isScrolling = true;
mouseY = evt.pageY;
}, false);
content.addEventListener('mousemove', function(evt) {
if(isScrolling) {
var dY = evt.pageY - mouseY;
mouseY = evt.pageY;
cScroll += dY;
momentum = range(momentum + dY * Scroller.ACCELERATION, -Scroller.MAX_MOMENTUM, Scroller.MAX_MOMENTUM);
var firstElementChild = getFirstElementChild(content);
content.style.WebkitTransform = 'translateY(' + range(cScroll, -(firstElementChild.scrollHeight - content.offsetHeight), 0).toString() + 'px)';
}
}, false);
window.addEventListener('mouseup', function(evt) {
isScrolling = false;
}, false);
}
function scrollToTop() {
cScroll = 0;
content.style.WebkitTransform = '';
}
function performAnimations() {
if(!isScrolling) {
var firstElementChild = getFirstElementChild(content);
cScroll = range(cScroll + momentum, -(firstElementChild.scrollHeight - content.offsetHeight), 0);
content.style.WebkitTransform = 'translateY(' + range(cScroll, -(firstElementChild.scrollHeight - content.offsetHeight), 0).toString() + 'px)';
momentum *= Scroller.FRICTION;
}
}
return {
scrollToTop: scrollToTop,
animationId: setInterval(performAnimations, 33)
}
}
Scroller.MAX_MOMENTUM = 100;
Scroller.ACCELERATION = 1;
Scroller.FRICTION = 0.8;
I think Andrew was on the right track with regards to setting the height of the #wrapper div. As he pointed out that,
that.maxScrollY = that.wrapperH - that.scrollerH;
Normally, this would work. But now that you've changed your #content to position: fixed, the wrapper element is no longer "wrapping" your content, thus that.wrapperH has a value of 0, things break.
Disclaimer: I did not go through the entire script so I may be wrong here
When manually setting a height to #wrapper, say 500px, it becomes,
that.maxScrollY = 500 - that.scrollerH;
The folly here is that when there's a lot of content and the window is small, that.scrollerH is relatively close in value to 500, say 700px. The difference of the two would be 200px, so you can only scroll 200 pixels, thus giving the appearance that it is frozen. This boils down to how you set that maxScrollY value.
Solution (for Chrome browser at least):
Since #wrapper effectively contains no content, we cannot use it in the calculations. Now we are left with the only thing that we can reliably get these dimensions from, #content. In this particular case, it appears that using the content element's scrollHeight yield what we want. This is most likely the one that has the expected behavior,
that.maxScrollY = that.scrollerH - that.scroller.scrollHeight;
scrollerH is the offsetHeight, which is roughly the height of what you see in the window. scroller.scrollHeight is the height that's considered scrollable. When the content does not exceed the length of the page, they are roughly equivalent to one another. That means no scroll. When there are a lot of content, the difference of these two values is the amount of scroll you need.
There is still a minor bug, and this looks like it's already there. When you have a lot of content, the last few elements are covered up by the bar when scrolled to the bottom. To fix this, you can set an offset such as,
that.maxScrollY = that.scrollerH - that.scroller.scrollHeight - 75;
The number 75 arbitrary. It's probably best if it's the height of the bar itself with 2 or 3 pixels for a bit of padding. Good luck!
Edit:
I forgot to mention last night, but here are the two sample pages that I used in trying to debug this problem.
Long page
Short page
This may be a CSS issue. In your stylesheet (mobile.css line 22), try removing position:fixed from #content.
That should allow the document to scroll normally (vertical scrollbar on a computer, "slideable" on a mobile browser).
Elements with position:fixed exit the normal flow of the document, their positioning is relative to the browser window. This is probably why you're having issues with scrolling. Fixed positioning is generally for elements which should always remain in the same place, even when the page is scrolled (ie. a notification bar "pinned" at the top of a page).
No definite solution, but more a direction I'd go for:
#wrapper and #content's overflow:hidden paired #content's postion:fixed and seem to be the cause of the issue.
If position: fixed is removed from #content, scrolling is possible but the "blank" divs are wrongly layered (tested in Firefox 5).
Your wrapper div seems to have a height of 0. So all the calculations are negative, setting it's height to the window height will correct the scroll issue. When I manually set the wrappers height via firebug and chromes debug bar the scroll functions as it should.
You #content div seems to have its size change on resize, probably a better idea to have the #wrapper div have its size change and then have #content inherit the size.
[Edit]
You don't believe me so codez, From iscroll-lite.js
refresh: function () {
var that = this,
offset;
that.wrapperW = that.wrapper.clientWidth;
that.wrapperH = that.wrapper.clientHeight;
that.scrollerW = that.scroller.offsetWidth;
that.scrollerH = that.scroller.offsetHeight;
that.maxScrollX = that.wrapperW - that.scrollerW;
that.maxScrollY = that.wrapperH - that.scrollerH;
In your page that translates to,
that.wrapperH = 0;
that.maxScrollY = -that.scrollerH
When a scroll finishes, this code gets called.
var that = this,
resetX = that.x >= 0 ? 0 : that.x < that.maxScrollX ? that.maxScrollX : that.x,
resetY = that.y >= 0 || that.maxScrollY > 0 ? 0 : that.y < that.maxScrollY ? that.maxScrollY : that.y;
...
that.scrollTo(resetX, resetY, time || 0);
See that that.maxScrollY > 0 ? ? If maxScrollY is negative then scrolling up will never bounce back.
I ended up just making my own, small script to handle the scrolling:
// A custom scroller
function range(variable, min, max) {
if(variable < min) return min > max ? max : min;
if(variable > max) return max;
return variable;
}
var isScrolling = false;
var mouseY = 0;
var cScroll = 0;
if("createTouch" in document) {
// TODO: Add for mobile browsers
} else {
content.addEventListener('mousedown', function(evt) {
isScrolling = true;
mouseY = evt.pageY;
}, false);
content.addEventListener('mousemove', function(evt) {
if(isScrolling) {
var dY = evt.pageY - mouseY;
mouseY = evt.pageY;
cScroll += dY;
var firstElementChild = content.getElementsByTagName("*")[0];
content.style.WebkitTransform = 'translateY(' + range(cScroll, -(firstElementChild.scrollHeight - content.offsetHeight), 0).toString() + 'px)';
}
}, false);
window.addEventListener('mouseup', function(evt) {
isScrolling = false;
}, false);
}
and modifying a few other parts. It does save a lot of download time, I suppose, also.
I'm still going to accept answers and award the bounty in 5 days, though.
Changed question warrants a new answer. I took a look at the code and I saw that you calculated the momentum on each step of the "move" function. This does not make sense because the momentum is used after the move has ended. What this meant was to capture the mouse position at the beginning, and then calculate the difference at the end. So I added two new variables,
var startTime;
var startY;
Inside the start event (mousedown/touchstart), I added,
startY = evt.pageY;
startTime = evt.timeStamp || Date.now();
Then I have the following for my end handler,
var duration = (evt.timeStamp || Date.now()) - startTime;
if (duration < 300) {
var dY = evt.pageY - startY;
momentum = range(momentum + dY * Scroller.ACCELERATION, -Scroller.MAX_MOMENTUM, Scroller.MAX_MOMENTUM);
} else {
momentum = 0;
}
I also removed the momentum calculation from inside of mousemove/touchmove. Doing this removed the jumping around behavior that I was seeing on my iPhone. I am seeing other unwanted behaviors as well (the whole window "scrolls"), but I'm guessing that you've been working to get rid of those so I didn't attempt.
Good luck. Here's a coded up page that I duplicated for my testing. I also took the liberty to refactor the code for this section to remove some duplicated code. It's under mobile3.js if you want to look at it.