In my angular 2 app I have a carousel element which has the swipe-right and swipe-left functionality implemented.
The issue is that if I'm mobile (or emulate mobile in Chrome) and I try to scroll above or below that element by first clicking on my element and dragging I cannot scroll my page. In mobile it's 100% of the width so it's going to be a common user scenario. If I remove the swipe-right and swipe-left functionality it works as expected.
I read somewhere that this requires changing the css to include touch-action: pan-y !important.
This works in the desktop chrome emulator but not for chrome on an actual mobile device (iPhone 6).
Is there any better work around?
Is there any additional css I need to make scrolling function as normal on all browsers?
Having the same issue on iOS Safari. DOM elements that are use hammerJs swipe methods do not respond to horizontal scrolling.
Same here. When we enable any custom option with this code the scroll is disabled.
mc.get('pinch').set({ enable : true });
mc.get('swipe').set({ direction : Hammer.DIRECTION_ALL });
This is because you have to polyfill touch-action, you can override which touch-action style is in use by supplying one. http://hammerjs.github.io/touch-action/
@runspired hi, where do i get the polyfills? try the link but the polyfills link is broken. And how actually use the polyfills? I cant really understand just from the link.
This may be helpful if anyone dig here:
In module.ts I've added:
import {
HammerGestureConfig,
HAMMER_GESTURE_CONFIG,
} from '@angular/platform-browser';
export class MyHammerConfig extends HammerGestureConfig {
buildHammer(element: HTMLElement) {
let mc = new Hammer(element, {
touchAction: "auto",
});
return mc;
}
}
// THEN IN PROVIDERS:
{
// hammer instantion with custom config
provide: HAMMER_GESTURE_CONFIG,
useClass: MyHammerConfig ,
},
I don't guarantee this is perfect config, but helped in my case and may be good start for you.
@KKrisu Can you configure it for just tap somehow?
Like this:
[style.touch-action]="'pan-y'"
@KKrisu
`import { HammerGestureConfig, HAMMER_GESTURE_CONFIG } from '@angular/platform-browser';
declare var Hammer: any;
export class MyHammerConfig extends HammerGestureConfig {
buildHammer(element: HTMLElement) {
let mc = new Hammer(element, {
touchAction: "pan-y",
});
return mc;
}
}`
declare var Hammer: any; was missing in your code
@bhnbhushan
Or preferably install @types/hammerjs.
I was able to fix this problem by combining the comment from @bhnbhushan and @KKrisu
instead of touchAction: "auto", i used touchAction: "pan-y".
````
import {
HammerGestureConfig,
HAMMER_GESTURE_CONFIG,
} from '@angular/platform-browser';
declare var Hammer: any;
export class MyHammerConfig extends HammerGestureConfig {
buildHammer(element: HTMLElement) {
let mc = new Hammer(element, {
touchAction: "pan-y"
});
return mc;
}
}
// THEN IN PROVIDERS:
{
// hammer instantion with custom config
provide: HAMMER_GESTURE_CONFIG,
useClass: MyHammerConfig ,
},
````
I have a list of rows which are swipeable left and right. the whole list can be scrolled.
With the answer by jbhue scrolling works for me while the rows have (swipeleft) / (swiperight) handles on them, swipeleft and right are triggered correctly, but swipeup (on a different view) doesn't work anymore.
("auto" as touchAction leads to even more weird behaviour in my context at least)
Any ideas to get both working?
@jbhue Thanks for the code example
I had the same problem, touchAction: "auto" only fixed that issue for me: mc = new Hammer(el, {touchAction: "auto"})
[style.touch-action]="'pan-y'" is not fully supported currently in all browsers, so it won't work in all cases (e.g. an iOS embedded Webkit view)touchAction: 'auto' unless you're really aware of what you're doingThe approaches recommended above of simply returning something like new Hammer(element, { touchAction: "pan-y" }); result in EVERY object Hammerjs get's bound to having this touchAction option (angular calls buildHammer for every element that listens to hammer events).
I achieved per-DOM element configuration via the following method:
// hammer-config.ts
import { HammerGestureConfig } from '@angular/platform-browser';
declare var Hammer: any;
export class HammerConfig extends HammerGestureConfig {
buildHammer(element: HTMLElement) {
let options = {};
if (element.attributes['data-mc-options']) {
try {
let parseOptions = JSON.parse(element.attributes['data-mc-options'].nodeValue);
options = parseOptions;
} catch(err) {
console.error('An error occurred when attempting to parse Hammer.js options: ', err);
}
}
const mc = new Hammer(element, options);
// keep default angular config
mc.get('pinch').set({enable: true});
mc.get('rotate').set({enable: true});
// retain support for angular overrides object
for (const eventName in this.overrides) {
mc.get(eventName).set(this.overrides[eventName]);
}
return mc;
}
}
Then, wherever you're listening for hammer.js events you can do the following:
<div
(swipeleft)="swipeClose()"
(swiperight)="swipeOpen()"
data-mc-options="{ \"touchAction\": \"pan-y\" }"
>
...
</div>
You must add it to your provider config as all the other examples indicate. Also note the escaping of the "'s, which is necessary for JSON.parse() to parse correctly. You could also accomplish this via data-mc-options='{ "touchAction": "pan-y" }' if you prefer.
It's worth noting that the newer versions of angular look to have an options property on the HammerGesturesConfig class, but I still don't see a way to configure hammer per element.
Hope this helps someone!
@jspizziri Your custom config worked great for me. The only problem I had with it is that Angular really didn't like the escaped quotes in the options attribute and failed to parse my template.
compiler.js:485 Uncaught Error: Template parse errors:
Unexpected character "EOF" (Do you have an unescaped "{" in your template? Use "{{ '{' }}") to escape it.) ("
I just removed the escapes and surrounded the attribute value with single instead of double quotes and Angular was happy. data-mc-options='{"touchAction": "pan-y"}' I'm on Angular 5.2.6.
Fo me, the buildHammer is not executed. Angular 6
This is my code:
`...
import { HammerGestureConfig, HAMMER_GESTURE_CONFIG } from '@angular/platform-browser';
declare var Hammer: any;
@Injectable()
export class CustomHammerConfig extends HammerGestureConfig {
buildHammer(element: HTMLElement) {
console.log('Here I am');
const mc = new Hammer(element, {
touchAction: 'pan-y'
});
return mc;
}
}
@NgModule({
...
providers: [
{
...
HostService,
{
provide: HAMMER_GESTURE_CONFIG,
useClass: CustomHammerConfig
}
],
bootstrap: [AppComponent]
})
export class AppModule { }
`
Nothing console.log about "Here I am" isn't logged. Thanks for any idea
@jspizziri Your custom config will throw error with @HostListener on document element.
It should be limited on element node only.
~
if (element.nodeType === Node.ELEMENT_NODE && element.hasAttribute('data-mc-options') ) {
// ...
}
~
I was able to solve my issue using CSS alone.
My issue was that I had an AngularJS element with swipeleft and swiperight. When I tried to scroll, scrolling was prevented.
This was on Android, Samsung Galaxy S9 (a very modern phone at the time of this comment.)
HTML
<myAngularElement hm-swipeleft="model.previousScreen()" hm-swiperight="model.previousScreen()"></myAngularElement>
CSS
// remember, this is an element. don't accidentally put a . or an # in.
myAngularElement {
display: block; // I needed this to make my element touchable
touch-action: pan-y !important;
}
If you have a different circumstance, you might be able to find help on this page:
http://hammerjs.github.io/touch-action/
I was able to solve my issue using CSS alone.
My issue was that I had an AngularJS element with swipeleft and swiperight. When I tried to scroll, scrolling was prevented.
This was on Android, Samsung Galaxy S9 (a very modern phone at the time of this comment.)
HTML
<myAngularElement hm-swipeleft="model.previousScreen()" hm-swiperight="model.previousScreen()"></myAngularElement>CSS
// remember, this is an element. don't accidentally put a . or an # in. myAngularElement { display: block; // I needed this to make my element touchable touch-action: pan-y !important; }If you have a different circumstance, you might be able to find help on this page:
http://hammerjs.github.io/touch-action/
This will not work with Safari and iOS Safari
https://caniuse.com/#search=touch-action
I had this problem, specially with iOS, couldn't find any other solution than implement some separate trigger from this part using (touchstart)="touchStart($event)" and (touchend)="touchEnd($event)"
touchStart(event: TouchEvent) {
this.touchTracker['start'] = this.getSwipeInfo(event);
this.touchTracker['bottom'] = this.isScrollBottom;
this.touchTracker['top'] = this.isScrollTop;
this.touchTracker['canceled'] = false;
}
touchEnd(event: TouchEvent) {
this.touchTracker['end'] = this.getSwipeInfo(event);
const y = this.touchTracker.start.y - this.touchTracker.end.y;
const x = this.touchTracker.start.x - this.touchTracker.end.x;
const time = this.touchTracker.end.time - this.touchTracker.start.time;
const isVertical = Math.abs(x) > Math.abs(y);
if (isVertical || Math.abs(y) < 40 || this.touchTracker.canceled || time > 400) {
return;
}
if (
(y > 0 && this.touchTracker.bottom) ||
(y < 0 && this.touchTracker.top)
) {
this.onSwipe(y > 0);
}
}
private getSwipeInfo(event: TouchEvent) {
const touchLenth = event.changedTouches.length;
const touchEvent = event.changedTouches[touchLenth - 1];
return {
x: touchEvent.pageX,
y: touchEvent.pageY,
time: Date.now(),
};
}
This way it works on Android and iOS (scroll and swipe).
This may be helpful if anyone dig here:
In module.ts I've added:import { HammerGestureConfig, HAMMER_GESTURE_CONFIG, } from '@angular/platform-browser'; export class MyHammerConfig extends HammerGestureConfig { buildHammer(element: HTMLElement) { let mc = new Hammer(element, { touchAction: "auto", }); return mc; } } // THEN IN PROVIDERS: { // hammer instantion with custom config provide: HAMMER_GESTURE_CONFIG, useClass: MyHammerConfig , },I don't guarantee this is perfect config, but helped in my case and may be good start for you.
That what I was looking for.
Following as I've also run into this issue having <div class="scroll-content bg-light" (swipeleft)="nextDay()" (swiperight)="previousDay()"> on my div prevents it from scrolling on mobile devices.
Use this style in the dom element css in using the Hammer event:
touch-action: pan-y !important;
You will see that vertical scrolling works.
for ios, touch-action: pan-y !important; not recognize
overrides = {
'swipe': {direction: Hammer.DIRECTION_HORIZONTAL},
'pinch': { enable: false },
'rotate': { enable: false }
}
disable pinch and rotate
As @zyhzx123e pointed out, touch-action is not available in iOS 12, only in iOS 13. Using Ionic/Angular what solved for me in both iOS 12 and 13 is this:
app.module.ts
export class MyHammerConfig extends HammerGestureConfig {
overrides = <any> {
pinch: { enable: false },
rotate: { enable: false },
swipe: {
direction: 6
},
// pan: {
// direction: 6
// }
}
}
@NgModule({
entryComponents: [],
declarations: [AppComponent],
imports: [
...
],
providers: [
...
{
provide: HAMMER_GESTURE_CONFIG,
useClass: MyHammerConfig
}
],
bootstrap: [AppComponent]
})
export class AppModule {
}
Most helpful comment
I was able to fix this problem by combining the comment from @bhnbhushan and @KKrisu
instead of
touchAction: "auto",i usedtouchAction: "pan-y".````
import {
HammerGestureConfig,
HAMMER_GESTURE_CONFIG,
} from '@angular/platform-browser';
declare var Hammer: any;
export class MyHammerConfig extends HammerGestureConfig {
buildHammer(element: HTMLElement) {
let mc = new Hammer(element, {
touchAction: "pan-y"
});
return mc;
}
}
// THEN IN PROVIDERS:
{
// hammer instantion with custom config
provide: HAMMER_GESTURE_CONFIG,
useClass: MyHammerConfig ,
},
````