Background
I'm trying to achieve something, but it's really driving me crazy. So any help would be appreciated!
I've created a scene in Matter.js that will be placed in a container further down on the page. I want the visitor to be able to interact with the scene, dragging and dropping the bodies. But allowing interaction creates the problem where Matter.js prevents the user from scrolling whenever the mouse is over the canvas.
So to work around this, I'm using the following code:
mouseConstraint.mouse.element.removeEventListener("mousewheel", mouseConstraint.mouse.mousewheel);
mouseConstraint.mouse.element.removeEventListener("DOMMouseScroll", mouseConstraint.mouse.mousewheel);
This makes it possible for the user to scroll through the page and still being able to interact with the scene by clicking and dragging the bodies, as it's only the scroll event listeners that are being removed. At least on desktop.
The problem
However, on mobile, the touch event is the event that makes it possible for the user to scroll on the page, so that would require me to also remove the touchmove, touchstart and touchend event listeners from the mouse constraint in Matter.js. Like this:
mouseConstraint.mouse.element.removeEventListener('touchmove', mouseConstraint.mouse.mousemove);
mouseConstraint.mouse.element.removeEventListener('touchstart', mouseConstraint.mouse.mousedown);
mouseConstraint.mouse.element.removeEventListener('touchend', mouseConstraint.mouse.mouseup);
And here's where the problem occurs. If I remove the touch events, the user can scroll by the canvas, but the user can't interact with the scene as that requires the touch events to be activated.
So I'm wondering if there is any way to have the touch events added but only allow them to work on certain bodies in the scene? I've found that I can use mouseConstraint.body as a boolean in order to know if a body has been clicked/touched or not. Could this be used in some way with this as a base?:
Matter.Events.on(mouseConstraint, "mousedown", function(event) {
if (mouseConstraint.body) {
console.log("Body clicked!");
}
});
This is the solution that I came up with. It's not perfect, but it's better than nothing.
let touchStart;
mouseConstraint.mouse.element.addEventListener('touchstart', (event) => {
if (!mouseConstraint.body) {
touchStart = event;
}
});
mouseConstraint.mouse.element.addEventListener('touchend', (event) => {
if (!mouseConstraint.body) {
const startY = touchStart.changedTouches[0].clientY;
const endY = event.changedTouches[0].clientY;
const delta = Math.abs(startY - endY);
if (delta > 80) {
window.scrollTo(0, 600);
}
}
});
You listen for the touch start event and store that in a variable. Then in the touchend event you check if the swipe is large enough to constitute a scroll, then scroll to the content.
Related
I have animation with ScrollMagic library and I am also using GSAP. This is description of animation on scroll in steps. Every number is one scroll:
Add class overflow-hidden to body, to disable scrolling.
Move credit-cards
Move remove some images
Start doing transform: translate(x,y) rotateZ(zdeg)
Stop scrolling and make image that was translated sticky
So this works good with mouse on mousewheel event. The question is:
What is the best way to implement touch scroll with very same effect when user comes from iOS or Andorid. (When user comes to my website from android, iPhone, iPad etc.)
I know that there is touchmove event.
var image = document.getElementById('image-phone');
if(step == 1){
//do first step
}...
//mousewheel event
window.addEventListener('mousewheel', function (e) {
//this is implemented
});
//touchnmove event
window.addEventListener('touchmove', function (e) {
//should I use this event
});
I had implemented zoom in/out an image using mousewheel event which is successfully working. But the behavior is different when I do that with touchpad(using two fingers to zoom in/out). How to differentiate beetween mousewheel event and touchpad events.
The same wheel event unfortunately fires for both pinch and mouse wheel, so it doesn't seem possible at this time to use a different event.
However, I found that the mouse wheel tends to trigger the e.deltaY number in a larger way than pinching.
Thus, I have a listener like so:
const handleZoom = (e) => {
e.preventDefault();
// ignore mouse wheel events that have bigger steps
if (Math.abs(e.deltaY) > 25) {
return;
}
// do what you want to do with pinching here
}
document.addEventListener("wheel", handleZoom);
You can play with the 25 variable to suit your own needs. But for the most part, I noticed that pinch movements are usually much more subtle and have a delta closer to 0, whereas mouse wheel events usually trigger a larger scroll.
First, you have to be careful when designing more advanced touch interactions: when the user uses a mouse it will respond via a click event, but when the user touches the screen both touch and click events will occur. For a single click the order of events is:
1) touchstart
2) touchmove
3) touchend
4) mouseover
5) mousemove
6) mousedown
7) mouseup
8) click
This, of course, means that if you are processing touch events like touchstart, you need to make sure that you don’t process the corresponding mousedown and/or click event as well. If you can cancel the touch events (call preventDefault() inside the event handler), then no mouse events will get generated for touch.
Update:
You can identify touch or click like this:
`
$('#element-id').on('click touchend',function(e){
if(e.type=='click')
console.log('Mouse Click');
else
console.log('Touch');
});
`
I am trying to build a one touch game on HTML5 canvas. It's a running game made with the help of this tutorial:
http://blog.sklambert.com/html5-game-tutorial-game-ui-canvas-vs-dom/
I changed the existing controls from space bar to a mouse click. It works perfectly across all the platforms except Android devices mobile browsers.
In Android devices, the touch makes the user jump. If there is a long hold in the touch, the user keeps jumping even when the touch is released. This problem does not happen in iPhones or iPads or desktops.
Can I make a Javascript function where a mouse down for a certain number of seconds is cut ? Something like:
if(mousedown for 1sec)
mouseup;
Let me know if you can think of another approach.
You can use touch events rather than mouse for touch enabled devices. Refer: https://developer.mozilla.org/en-US/docs/Web/API/Touch_events/Using_Touch_Events
function is_touch_device() {
/* Function code taken from http://stackoverflow.com/a/4819886/3946520 */
return 'ontouchstart' in window // works on most browsers
|| navigator.maxTouchPoints; // works on IE10/11 and Surface
};
if(is_touch_device()) {
canvas.addEventListener('touchstart', handleTouchStart, false);
canvas.addEventListener('touchend', handleTouchEnd, false);
}
else {
// Bind Mouse Events
}
function handleTouchStart(e) {
// This code runs when user touches the canvas i.e. on touch start
}
function handleTouchEnd(e) {
// This code runs when user removes finger from canvas i.e. on touch end
}
Also note that there can be scenario where the user puts two or more fingers on the canvas. Each of them will fire 'touchstart' event. So you'll have to handle that.
You can refer to http://www.javascriptkit.com/javatutors/touchevents.shtml for a good tutorial on touch events.
I am currently having an issue in allowing vertical scrolling when event.preventdefault is enabled.
I am trying to add swipe functionality to my mobile page, I have tried may frameworks like hammer.js, swipe.js etc, and all of them require event.preventDefault enabled to detect left and right swipes.
When event.preventDefault is enabled, the swipes detect perfectly, however you lose the ability to vertical scroll when you are on that element. i.e you cannot move the screen up or down on a mobile device, when your finger starts on the swipe element.
I have tried building my own little script which works well, but again has the issue of vertical scrolling, that is an issue.
var el = document.getElementById('navigation');
el.ontouchstart = function(e){
e.preventDefault();
el.innerHTML = "touch start";
};
el.ontouchend = function(e){
el.innerHTML = "touch end";
};
el.ontouchmove = function(e){
el.innerHTML = "touch moved";
};
el.ontouchcancel = function(e){
el.innerHTML = "touch cancel";
};
Any ideas???
It's a common issue where you want the native browser behaviour to work alongside the interaction behaviour that people come to expect from a touchscreen device.
If you want to use a library you might need to hack it open as you WILL need to prevent the defaults to prevent the page from jumping all over the place when using touch events, but at other times you want to omit it as you want to prevent the page from remaining in a static position, obscuring other content.
Ideally you want add a clause to the handler that instructs them whether or not to prevent the default behaviour the browser executes upon receiving the event.
Swiping for instance, is a behaviour that should occur in a short time frame (if you are taking for instance one whole second in moving your finger from one area to the other instead of, let's say, 120 ms, you're not actually swiping, but dragging. Thinking about time frames may help you here, for instance:
var threshold = 150, interactionStart;
el.ontouchstart = function( e )
{
// store timestamp of interaction start
interactionStart = +new Date;
};
el.touchmove = function( e )
{
// get elapsed time in ms since start
var delta = +new Date - interactionStart;
if ( delta > threshold )
{
e.preventDefault();
}
else {
// super cool magic here
}
};
Whether 150 ms is the threshold you want depends on the action, as you see there is no fixed answer for your question as the actual implementation depends on what your application needs in terms of touch interactions.
You could also consider not blocking the events default when the user is scrolling more along the vertical axis (i.e. compare whether the delta position of the events Y offset (relative to the start Y offset) is larger than the events X offset (relative to the start X offset) to detect whether the users is moving left or right or up or down (for instance if you have a carousel that can swipe horizontally (where the default behaviour is blocked so the page won't move up/down during the horizontal scroll) but want the page to scroll vertically when the user is obviously dragging the page among the vertical axis).
The library jquery.touchSwipe solves that.
The library: https://github.com/mattbryson/TouchSwipe-Jquery-Plugin
An example page where swiping and scrolling are combined: http://labs.rampinteractive.co.uk/touchSwipe/demos/Page_scrolling.html
I have a little web gallery that I added swipe navigation to for mobile browsers. I did it with pretty simple touchstart/touchmove/touchend event tracking.
The problem is that when I try to pinch zoom in the browser window it fails if any finger starts in the element I added the touch event handlers to, I'm guessing from the calls to preventDefault.
Is there a way I can track the touch events for navigating my images without blocking the zoom in and out feature of the browser? I don't mind blocking single finger scrolling if it's over my element, but I want to allow the pinch zooming.
Here's the code:
function addDragHandlers(eventDivId) {
var startX, endX;
var slides = $('#'+eventDivId);
slides.bind('touchstart', function(e) {
e.preventDefault();
startX = e.pageX;
endX = startX;
});
slides.bind('touchmove', function(e) {
e.preventDefault();
endX = e.pageX;
});
slides.bind('touchend', function(e) {
e.preventDefault();
if ( endX - startX < 0) {
// go to next image
} else if ( endX - startX > 0) {
// go to previous image
} else {
// do click action
}
}
});
}
What you want is ongesturestart, ongesturestart move and end work the same as on touch but for more than one finger. From their you can add a listener for:
event.stopPropagation();
to prevent "preventDefault()" from propagating in.
I wanted this to work on Android so I didn't want to use gesture events, which I have read are only on iOS - though I have no android to test that claim.
I then looked at a bunch of javascript gesture libraries which try to make gesture support easy. Unfortunately none of them worked well enough for my purposes.
So I ended up using touch handlers on the body to track all the touches on the page and then a custom heuristic to determine whether to use the touch to navigate the gallery or to use it to scroll/pinch the page.
It's still not perfect. If you start by touching my gallery element and then touching outside it the pinch doesn't work.
As an aside, the "TouchSwipe" jquery library has an incredibly well-designed and flexible API, but with the configuration I needed, tracking only horizontal swipes on my element, it was too quirky under iOS6 and hasn't been updated for a few weeks. If you are looking into this sort of challenge and it's a few months from now I'd recommend checking the updates for that plugin.