我正在使用以下指令在Angular应用中向左滑动和向右滑动手势。
import { Directive, ElementRef, EventEmitter, NgZone, OnDestroy, OnInit, Output, Input } from '@angular/core';
import { fromEvent, Observable } from 'rxjs';
import { elementAt, filter, map, switchMap, take, takeUntil, takeWhile, tap } from 'rxjs/operators';
import { SwipeCoordinates, SwipeDirection, SwipeEvent } from './interfaces';
@Directive({
selector: '[swipe]'
})
export class SwipeDirective implements OnInit, OnDestroy {
@Input() swipe: boolean;
@Output() swipeLeft: EventEmitter<true> = new EventEmitter<true>();
@Output() swipeRight: EventEmitter<true> = new EventEmitter<true>();
//property used to unsubscribe from all subscriptions on destroy event
private alive = true;
private horizontalMovesSub: any;
constructor(
private elementRef: ElementRef,
private zone: NgZone
) { }
ngOnInit() {
if (this.swipe) {
const domElement = this.elementRef.nativeElement;
const touchStarts: Observable<SwipeCoordinates> = fromEvent(domElement, 'touchstart').pipe(map(this.touchEventToCoordinate));
const touchMoves: Observable<SwipeCoordinates> = fromEvent(domElement, 'touchmove').pipe(map(this.touchEventToCoordinate));
const touchEnds: Observable<SwipeCoordinates> = fromEvent(domElement, 'touchend').pipe(map(this.touchEventToCoordinate));
//Move starts with direction: Pair the move start events with the 3rd subsequent move event, but only if no touch end event happens in between.
const moveStartsWithDirection = touchStarts.pipe(
switchMap((dragStartEvent: SwipeCoordinates) => touchMoves.pipe(
elementAt(3),
map((dragEvent: SwipeCoordinates) => {
const intialDeltaX = dragEvent.x - dragStartEvent.x;
const initialDeltaY = dragEvent.y - dragStartEvent.y;
//this.swipe(dragEvent, 'start');
return { x: dragStartEvent.x, y: dragStartEvent.y, intialDeltaX, initialDeltaY };
})
)));
//Horizontal move starts: Keep only those move start events where the 3rd subsequent move event is rather horizontal than vertical
const horizontalMoveStarts = moveStartsWithDirection.pipe(
filter(dragStartEvent => Math.abs(dragStartEvent.intialDeltaX) >= Math.abs(dragStartEvent.initialDeltaY))
);
//Take the moves until touch ends On move end emit swipe end event to parent element
const movesUntilEnds = (dragStartEvent: any, direction: SwipeDirection) => touchMoves.pipe(
map(dragEvent => this.getSwipeDistance(dragStartEvent, dragEvent)),
takeUntil(touchEnds.pipe(
take(1),
map(dragEndEvent => this.getSwipeDistance(dragStartEvent, dragEndEvent)),
tap((coordinates: SwipeCoordinates) => this.emitSwipeEndEvent(direction, coordinates))
)));
//const verticalMoves = verticalMoveStarts.pipe(
// switchMap(dragStartEvent => movesUntilEnds(dragStartEvent, 'y'))
//);
const horizontalMoves = horizontalMoveStarts.pipe(
switchMap(dragStartEvent => movesUntilEnds(dragStartEvent, 'x'))
);
//Run swipe subscriptions outside zone for better performance On move emit swipe move event to parent element
this.zone.runOutsideAngular(() => {
this.horizontalMovesSub = horizontalMoves.pipe(
takeWhile(() => this.alive)
).subscribe(() => { });
});
}
}
//Format touch event to coordinates object that is easier to read
public touchEventToCoordinate(touchEvent: TouchEvent): SwipeCoordinates {
return {
x: touchEvent.changedTouches[0].clientX,
y: touchEvent.changedTouches[0].clientY
};
}
private getSwipeDistance(dragStartEvent, dragEvent): SwipeCoordinates {
return {
x: dragEvent.x - dragStartEvent.x,
y: dragEvent.y - dragStartEvent.y
};
}
//Emits swipe event
private emitSwipeEndEvent(direction: SwipeDirection, coordinates: SwipeCoordinates) {
if (direction == 'x') {
let longEnough = Math.abs(coordinates.x) > 50;
if (longEnough) {
if (coordinates.x < 0) {
this.swipeLeft.emit(true);
} else {
this.swipeRight.emit(true);
}
}
}
}
//Set alive property to false to unsubscribe from all subscriptions
ngOnDestroy() {
this.alive = false;
if (this.horizontalMovesSub) {
this.horizontalMovesSub.unsubscribe();
}
}
}
然后在这样的组件中使用
<div [swipe]="canNavigate" (swipeLeft)="navigate()" (swipeRight)="navigate(true)">
<my-onpush-component>
</my-onpush-component>
</div>
问题是当在Swipe指令中我在runOutsideAngular中使用Swipe订阅时,my-onpush-component
上的更改检测未在swipe事件上运行。即使删除ChangeDetectionStrategy.OnPush
,my-onpush组件也不会更新。
this.zone.runOutsideAngular(() => {
this.horizontalMovesSub = horizontalMoves.pipe(
takeWhile(() => this.alive)
).subscribe(() => { });
});
但是如果我从Swipe指令中删除this.zone.runOutsideAngular(() => {
,一切正常。
有人可以指导解决此问题吗?