Actual Behavior:
What is the issue? *
What is the expected behavior?
CodePen Demo which shows your issue:
http://codepen.io/twolfe/pen/pNQBrbDetails:
This works on chrome desktop, but not chrome for Android version 55.0.2883.91. Open the codepen, http://codepen.io/twolfe/full/pNQBrb/, on an Android phone with the latest Chrome update, or with Chrome DevTools device mode, to see the issue. This issue is showing up on my Pixel XL.Angular Versions: *
Angular Version:
1.5.5Angular Material Version:
1.1.1Additional Information:
Browser Type: *
Chrome for AndroidBrowser Version: *
55.0.2883.91OS: *
Android 7.1.1Stack Traces:
This can also be repeated in the Chrome DevTools Device Mode
I encountered the same issue on my hybrid Cordova app. Downgrading the Android System Web view from the play store is a workaround for hybrid apps which use web views for now.
I hope the issue will be resolved soon.
Hi Team, its driving my users crazy. Need some quick fix
@LohithAnand, how to downgrade the Android System Web view from the play store?
@imromec Locate the Android System Webview in play store and click on the uninstall button. This will rollback to the version of Webview shipped with the OS.
Same problem on main site demo in the Chrome DevTools Device Mode: https://material.angularjs.org/latest/demo/swipe
+1
+1
Any workarounds for now?
let me mention that issue also appears on Firefox when testing in device mode with touch enabled. it's kind of a normal to high priority issue IMO but there is no attention to it yet...
Update:
I just had a review on code and found that angular material uses initCustomEvent()
to make these native events which is deprecated currently, I'm not sure if that's the reason... just shared my findings.
I'm currently building a web app which makes use of sliders extensively. Since I discovered this issue I can't find sleep. Development has slowed down as a result of this feature not working correctly especially in mobile. It work fine on desktop though.
If you are building hybrid app using cordova, then this issue can be resolved by installing cordova crosswalk plugin.
Seems that in Chrome Device Mode, in the $mdGestures provider, the touch pointercancel event with properties x and y as 0 is triggered on swipe end. In regular mode the mouse pointerup event is triggered with the x and y properties set to the location where mouse click ended. I believe the issue could have something to do with this.
+1 ... really need a fix for this
Could the issue be here?
function getEventPoint(ev) {
ev = ev.originalEvent || ev; // support jQuery events
return (ev.touches && ev.touches[0]) ||
(ev.changedTouches && ev.changedTouches[0]) ||
ev;
}
ev.touches and ev.changedTouches are always undefined so ev is always returned.
I'm not sure how ev.touches are supposed to work, but it seems that this is looking for the original event to return. That is compared in updatePointerState() with the current event and direction calculated.
Any ideas on how to get around this?
I believe the issue might be in the missing pageX and pageY properties on the last returned touch event (pointercancel), because the swipe direction gets always calculated towards the upper left corner here:
function updatePointerState(ev, pointer) {
var point = getEventPoint(ev);
var x = pointer.x = point.pageX; // point.pageX == 0
var y = pointer.y = point.pageY; // point.pageY == 0
pointer.distanceX = x - pointer.startX;
pointer.distanceY = y - pointer.startY;
pointer.distance = Math.sqrt(
pointer.distanceX * pointer.distanceX + pointer.distanceY * pointer.distanceY
);
pointer.directionX = pointer.distanceX > 0 ? 'right' : pointer.distanceX < 0 ? 'left' : '';
pointer.directionY = pointer.distanceY > 0 ? 'down' : pointer.distanceY < 0 ? 'up' : '';
pointer.duration = +Date.now() - pointer.startTime;
pointer.velocityX = pointer.distanceX / pointer.duration;
pointer.velocityY = pointer.distanceY / pointer.duration;
}
As this broke with a Chrome update I believe they changed the browser so that either the pointercancel event data changed or it's the wrong event type to begin with (are we expecting pointerup for example?)
RoniPa, yep, I think you're right. There is pointer.startX, what we need is the finishing point.
This is also messing up scrolling in my sidenav. It triggers the nav to close.
This is a tricky one indeed. The very last event brings no mouse position info and the direction ends up always 'left'. Other touch options are getting a conflict, ng-swipe doesn't seem to trigger, ng-mouseup or down not working for touch move(for a fast manual solution).
Isn't this a Chrome error, messing up this event?
Just wanted to add a +1 here.
+1. This breaks the md-swipe-content in tabs as well. Also the demo is broken and only swipe left works.
+1 Looking forward to see this fixed soon.
If your development slowed down with this bug like ours, a quick workaround would be using https://github.com/adzialocha/angular-swipe and changing md-swipe-* to ng-swipe-* until this is fixed.
Confirmed that angular-swipe does work.
Just ran into this after upgrading to Chrome Version 55.0.2883.87 on Ubuntu 16.04. It appears to be an issue with Chromium, not Angular Material. Swipe gestures still function fine in Firefox. I've submitted an error report to the Chrome team.
Here's a link to the issue on the Chromium Bug Tracker: https://bugs.chromium.org/p/chromium/issues/detail?id=678426
@imjoeco maybe this is related to something in their js engine, but if the problem is with chrome or webview then why other libs out there detecting gestures correctly? ok maybe an api issue... but you can see that it's not working in Firefox in device (responsive) mode too. but thanks for reporting the issue there too... maybe someone can find out the reason... here or there ;)
@mohammadrafigh I think it's because material angular is using the 'pointercancel' event to detect the last position. A simple fix may just be to use another event. I want to have a look through some other swipe libs to see what they use, when I have some time
+1 I am facing this issue with my Cordova application. Looking forward to see some fix soon.
@mohammadrafigh, I'm definitely not saying there isn't at least one work-around, I understand other libraries still work. My thinking is just that, unless angular material is using some deprecated browser api, it's essentially a bug with chrome, so in the effort of a standards compliant web and not doing more work than I need to, my starting point is to report the chrome bug to the chromium team, despite potential workarounds for the problem. To be clear, I'm not sure exactly the source of the issue, but as it predictably happened on browser update, I'm going to make sure the simplest explanation isn't the answer before trying to fix what not be broken.
I think I found a workaround for this. The issue seems to be that Chrome launches a pointercancel event when ever some browser default action is triggered. You can control this with the touch-action CSS property. By disabling the conflicting default actions (for example, if you only need horizontal scrolling and have a md-swipe-right set the property as touch-action:pan-y
) I was able to prevent the unwanted pointercancel events and get the md-swipe actions working correctly. This doesn't however solve the issue entirely, but it seems to be more of a problem of how Chrome 55 has implemented the new PointerEvent API. This article gave me some insight.
If the library is wished to be compatible with this, some changes might be needed to the way Angular Material groups events. I was also able to get this to work by ignoring the PointerEvents and reading only the old Mouse and Touch APIs, but that's probably not a very sustainable solution in the long run.
Except it seems that unwanted swipe-left actions are still triggered, so pointercancel events should probably be ignored handled separately in the gestureEnd function (perhaps with touchcancel) as to my understanding the intended purpose for this event is the possibility to roll back without triggering actions?
Hi Team, This issue is causing problems in other swipe functionalities too. I have a map in my hybrid mobile application. In which, Zoom in/Zoom out is not working. Need some quick fix. Thanks
@clshortfuse and @RoniPa: This is not a bug in Chrome, looks like Angular needs a little fix here for PointerEvents. Simply add a style touch-action: none
for the elements that will be swiped. I confirmed that it fixes the problem in Chrome.
Edge also acts strangely on Surface with the demo, and works perfectly with the suggested fix.
Here is an explanation for this behavior: We are seeing a pointercancel
event here because the browser is taking over the control to handle the gestures here, as specified in the PointerEvent spec. To suppress the browser action (and let Angular handle it), we need to specify using touch-action
which browser-actions are allowed. In my solution above, I specified that browser shouldn't do _anything_ for gestures starting on the swiped elements. This could be more granular than this, e.g. touch-action: pan-x
would let browser do only horizontal panning/scrolling.
@mustaqahmed To clarify, are you saying there isn't a bug with Chrome or this isn't a Chrome-exclusive bug?
Exactly: Chrome 55 shipped PointerEvent support, which exposed a hidden bug.
Angular seems to have a few other bugs around this. E.g. md-slider is working neither on Android nor on Windows touch. This is true for Edge too.
The same fix works in both cases. I will comment on those issues.
@mustaqahmed Yep, I think you're right. I created a fork of my own where I handle pointercancel and touchcancel separately from other 'ending' events. When combined with touch-action CSS property it seems to remove issues at least with Chrome. I haven't had time to test it out extensively though.
Pointer Events
Pointing at things on the web used to be simple. You had a mouse, you moved it around, sometimes you pushed buttons, and that was it. But this, doesn’t work so well on here.
Touch events are good, but to support touch and mouse, you had to support two event models:
elem.addEventListener('mousemove', mouseMoveEvent);
elem.addEventListener('touchmove', touchMoveEvent);
Chrome now enables unified input handling by dispatching PointerEvents:
elem.addEventListener('pointermove', pointerMoveEvent);
Pointer events unify the pointer input model for the browser, bringing touch, pens, and mice together into a single set of events. They’re supported in IE11, Edge, Chrome, Opera and partially supported in Firefox.
Check out Pointing the Way Forward for more details.
Test 1: Is angular-material handling Chrome pointermove?
answer:
Yes
I did a simple test with console.log, and it works fine!
Test 2: How angular material treats swipe?
answer:
That's not a fail but a calc error. On mobile or chrome inspection swipe left and swipe right are both recognized by 'left', when i close inspection the lib treats it right.
There are two ways:
pointerup pointercancel
In our conditions, chrome always call 'pointercancel', I suspect that distanceX negative can be a cause.
"With pointer events, whenever a default action like scroll or zoom is triggered, you’ll get a pointercancel event, to let you know that the browser has taken control of the pointer."
https://developers.google.com/web/updates/2016/10/pointer-events
The solution is more simple that it seems:
Browser take control of elements when it want to, but you can say what element you don't want give to browser handler.
div{
touch-action : pan-y;
}
It worked for me.
My conclusion is that Chrome is right, because if you dont explicit what you want to control totally, browser need to take the master control. Directives are always slaves, in my opnion.
http://codepen.io/anon/pen/JEryRP
On my APP, i'd make a horizontal grid list, like Deezer does, and before Chrome 55, the handle of this list was calling swipe event too. It was a big problem to solve.
Material team may put it on md-swipe directives on next version.
div.md-swipe-left *, div.md-swipe-right *{
touch-action : pan-y;
}
div.md-swipe-up *, div.md-swipe-down *{
touch-action : pan-x;
}
https://developers.google.com/web/updates/2016/10/pointer-events
@waldandrade thanks for your tips. I'm sorry but please update your conments instead of commenting multiple times this will spam others. Again thanks for your efforts.
@mohammadrafigh it was my evolution step by step, i'm sorry about spam.
ADDED: Found the issue while I was creating a CodePen. I had the directive md-swipe-content on md-tabs. Removed it and the tabs stopped switching on scroll.
I have something that is similar but can't fix. I have a view with 2 tabs. When I scroll the left area it activates the tab to the right. Structure is:
mb-tabs | mb-tab | md-tab-body | md-content | md-list | (using a virtual repeat) md-list-item ...
I tried inserting the touch-action property but the tab keeps switching on the slightest scroll.
This only happens on Chrome for Android -- works fine on Chrome for IOS
@EladBezalel Is this solved now?
@DevVersion swipe examples on website shows that Swipe is fixed generally but something is wrong still with tabs, md-swipe-content still has the issue.
Same result as @mohammadrafigh.
But strangely, if md-tabs is contained within a md-bottom-sheet, then swipe works fine.
I updated do 13 with high hopes but tabs are still sliding to the right. I should not though that I am really glad that this problem is being looked into!
With pointer events, whenever zoom is triggered, It seems pointercancel event is triggered always. I have a map in my app which is not working(zoom in / zoom out) after chrome 55 update.
It is because browsers need to crontrol scroll. Try to turn off browser
control. I think there are more complex ways but that is the best way i
found.
Em 02/02/2017 09:40, "iamroof" notifications@github.com escreveu:
With pointer events, whenever zoom is triggered, It seems pointercancel
event is triggered always. I have a map in my app which is not working(zoom
in / zoom out) after chrome 55 update.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/angular/material/issues/10145#issuecomment-276946933,
or mute the thread
https://github.com/notifications/unsubscribe-auth/ALSmmZMgUnule_seO6DcntWWXuv9BDx7ks5rYc62gaJpZM4LOmaK
.
Can this issue be solved? This is an important feature in Material Design and they don't even bother fixing it, when hundreds of not thousands of people are relying on the framework to run their business? This is ridiculous.
Come on guy's, this is a big problem for lots of users.
Visitors are currently unable to scroll down pages, and cannot access content.
@waldandrade this isnt working. Tried turning off browser scroll control.
The ngTouch module, built in to Angular doesn't have this problem. I added that module to my application and replaced md-swipe-left
with ng-swipe-left
; vertical scrolling still works. All is well again.
Try to scroll
ok now I see scroll is broken inside pages with tabs too, new damn issue.
this PR should fix this issue completely (issues with swipe, tabs and scrolling). I hope this works for everyone.
@mohammadrafigh i'm looking into it
@mohammadrafigh @EladBezalel
The PR is definitely in the right direction. However, the issue isn't really fixed.
Excluding the pointercancel event in the check for 'end-event' will not work due to the following reason.
FROM MDN
"The pointercancel event is fired when the browser concludes the pointer will no longer be able to generate events. For example, when a touchscreen action was triggered that was not disallowed by the touch-action CSS property."
Chrome fires both pointerdown and touchstart initially. However, it is not guaranteed that the "pointerup" event will always be fired at the end. Infact, the pointercancel event maybe fired, resulting in the touch-move cycle being resetted (I dont know how to put this in better words, but my point is that the next tap action will be a pointerup/touchstart).This might happen due to a slow swipe or other reasons which I cannot figure-out. But just disabling the 'touch-action' css value does not guarantee that pointercancel will be supressed.
But interestingly, a touch-end is always fired at the end, causing me to believe that the POINTER API in itself is not reliable to detect precision swipes based on pointerdown-pointerup cycles, as a pointercancel can be fired at anytime.
I have tested extensively on chrome, both in Android as well as Responsive Mode on a Mac.
A pointercancel event can be fired anytime.
The whole issue is due to 'pointercancel' event. Specifically due to this function:
function typesMatch(ev, pointer) {
return ev && pointer && ev.type.charAt(0) === pointer.type;
}
The tab-change on scroll down occurs due to a pointerdown-pointercancel cycle returning true. Same for right swipe going to the next tab. Hence, a pointerdown-pointercancel cycle is treated like a pointerdown-pointerup cycle, which is the root cause of the issue.
THE SOLUTION THAT WORKS FOR ME
Drop listening to pointer events. For all practical purposes, the mouse and touch events suffice. Safari has no support for it.
The browser support for pointer events is almost not very amazing.
So all those who are struggling with tabs switching on scroll, or the swipe being in the right direction only, here's what you can do:
Change the following:
// Listen to all events to cover all platforms.
var START_EVENTS = 'mousedown touchstart pointerdown';
var MOVE_EVENTS = 'mousemove touchmove pointermove';
var END_EVENTS = 'mouseup mouseleave touchend touchcancel pointerup pointercancel';
To:
// Listen to all events to cover majority of platforms.
var START_EVENTS = 'mousedown touchstar';
var MOVE_EVENTS = 'mousemove touchmoev';
var END_EVENTS = 'mouseup mouseleave touchend touchcancel';
Ironically, the purpose of pointer events is to provide a unified api for the mouse/touch events.However, we are listening to all these events, along with pointer events. Further, the handler are attached to the Document element !
Also, as per MDN:
Touch-action property may be applied to all elements except: non-replaced inline elements, table rows, row groups, table columns, and column groups
So, we cannot prevent panning,zooming,etc in these. So the solution with touch-action:pan-x/y is not all-inclusive.
TL;DR
Stop listening to pointer-events and your app will work just fine.
I request to kindly let me know in what Browser/Platform, will the touch/mouse events not be sufficient, as I am not listening to them in my production app. So it would be great to know where have I broken swipe gesture by doing this.
Thanks.
@theLufenk A while back when this problem first appeared after Chrome enabled the new pointer api I created a fork because of this issue where i separate the handling of cancel events. Does this kinda work towards a solution without needing to ignore the entire pointer api, which doesn't seem like a lasting fix?
EDIT: As mentioned previously, this solution would naturally require the touch-action CSS definitions for components with touch actions to remove the problem.
@RoniPa
Completely agree with you. This is in no way a lasting fix. However, I am pretty sure about the amount of headache it must be giving to the folks. So I have offered a temporary fix. I am unable to find out a input type (pen/stylus ?) which doesn't result in a touch/mouse event being fired. I request you to kindly let me know of I device which can be used to do a swipe action (from an abstract user's perspective, not technically ) and yet not produce a touch/mouse event. I am really interested in solving this problem but simply couldn't see a scenario where my fix will fail.
Also, going through the w3c spec reveals:
Examples of scenarios in which a user agent might determine that a pointer is unlikely to continue to produce events include:
1) A device's screen orientation is changed while a pointer is active.
2) The user inputs a greater number of simultaneous pointers than is supported by the device.
3) The user agent interprets the input as accidental (for example, the hardware supports palm rejection).
4) The user agent interprets the input as a pan or zoom gesture.
I would like to draw your attention to points number 2 and 3. They signify that the pointer events get further dependant on the device hardware
A important point to consider is that the pointer events API's main purpose was to provide a unified interface for accessing pointer interactions. Which directly relates to (as mentioned in the w3c draft) removing the need for developers to attach separate listeners for touch/mouse events, which end up affecting performance
So one of the reason why pointerevents api was introduced is to reduce the cycles consumed by touch and mouse events used in same script. This is an important takeway for us because:
1) I am not sure how accurate I am, but unless the dispatch of touch/mouse/pointer events is done, the browser does not take it's default action for zoom/pan etc. In essence, by introducing another event listener for pointer events, we are further aggravating the issue which the pointer events api seeks to resolve.
2) Event listeners do affect both performance and memory. And here, the listeners are on the document. Please add a console log inside the three functions which handle start,move and end in gesture.js and see the console while moving you mouse or swipe. In non-mobile environments , the pointerevents listeners are essentially doubling the overhead, since they are never exclusive and fired along with either mouse or touch events.
Hence, IMHO using pointer events would only make sense when we can use on('pointerdown') in place of on('touchstart mousemove pointerdown') as there is no apparent reasons or advantages of use pointerevents. There purpose is to enable us to listen to only 1 event. And ironically, we are using 3. So instead of reducing from 2 to 1, we are going from 2 to 3.
I hope my point is clear enough
To the maintainers: Please see it makes sense to remove the pointerevents, so that the users can still serve the framework from CDNs instead of having to include it locally just for a change encompassing 5 words. Also, if you guys can point out why they were included in the first place, it would be great !
Thank you
@theLufenk I can see your point and agree that for now that might actually be the solution that makes the most sense, as touch and mouse events aren't probably going to be deprecated any time soon in major browsers.
@RoniPa
Sadly, IE11+ do not use touchevents but use pointerApi !!
So i'm checking against the latest version and it seems to be working :
http://codepen.io/EladBezalel/pen/ZeodPy
@EladBezalel md-swipe is working but as you see it's not working with tabs specially when the content of tabs has scrolls.
My tabs scroll still not working
This issue is kind of funny.
Lot of annoyed people.
I actually didn't notice this at first and it caused our entire user profile system to be unscrollable on mobile.
Yikes.
will be solved by @mohammadrafigh in #10455
Any update when it will be resolved?
Using ngTouch instead seems to fire events as desired. Thanks @trevorhreed
The fix in 8c1ce1b breaks pinch zoom due to the touch-action: pan-x/y
applied to elements.
It's particularly bad with md-tabs
as even if you don't have md-swipe-content
set to true it will still have md-swipe-left/right
attributes.
Annoyingly, pinch zoom works with shift+click in Chrome dev tools touch emulation so you have to test on an actual Android device to recreate the bug.
Fixed in https://github.com/angular/material/pull/10455 which was merged in 1.1.5
.
Most helpful comment
This can also be repeated in the Chrome DevTools Device Mode