in src/viewer/TouchService.ts [58:289]
constructor(canvasContainer: HTMLElement, domContainer: HTMLElement) {
const subs = this._subscriptions;
this._activeSubject$ = new BehaviorSubject<boolean>(false);
this._active$ = this._activeSubject$.pipe(
distinctUntilChanged(),
publishReplay(1),
refCount());
subs.push(observableFromEvent<TouchEvent>(domContainer, "touchmove")
.subscribe(
(event: TouchEvent): void => {
event.preventDefault();
}));
this._touchStart$ = observableFromEvent<TouchEvent>(canvasContainer, "touchstart");
this._touchMove$ = observableFromEvent<TouchEvent>(canvasContainer, "touchmove");
this._touchEnd$ = observableFromEvent<TouchEvent>(canvasContainer, "touchend");
this._touchCancel$ = observableFromEvent<TouchEvent>(canvasContainer, "touchcancel");
const tapStart$: Observable<TouchEvent> = this._touchStart$.pipe(
filter(
(te: TouchEvent): boolean => {
return te.touches.length === 1 && te.targetTouches.length === 1;
}),
share());
this._doubleTap$ = tapStart$.pipe(
bufferWhen(
(): Observable<number | TouchEvent> => {
return tapStart$.pipe(
first(),
switchMap(
(): Observable<number | TouchEvent> => {
return observableMerge(
observableTimer(300),
tapStart$).pipe(
take(1));
}));
}),
filter(
(events: TouchEvent[]): boolean => {
return events.length === 2;
}),
map(
(events: TouchEvent[]): TouchEvent => {
return events[events.length - 1];
}),
share());
subs.push(this._doubleTap$
.subscribe(
(event: TouchEvent): void => {
event.preventDefault();
}));
this._singleTouchMove$ = this._touchMove$.pipe(
filter(
(te: TouchEvent): boolean => {
return te.touches.length === 1 && te.targetTouches.length === 1;
}),
share());
let singleTouchStart$: Observable<TouchEvent> = observableMerge(
this._touchStart$,
this._touchEnd$,
this._touchCancel$).pipe(
filter(
(te: TouchEvent): boolean => {
return te.touches.length === 1 && te.targetTouches.length === 1;
}));
let multipleTouchStart$: Observable<TouchEvent> = observableMerge(
this._touchStart$,
this._touchEnd$,
this._touchCancel$).pipe(
filter(
(te: TouchEvent): boolean => {
return te.touches.length >= 1;
}));
let touchStop$: Observable<TouchEvent> = observableMerge(
this._touchEnd$,
this._touchCancel$).pipe(
filter(
(te: TouchEvent): boolean => {
return te.touches.length === 0;
}));
this._singleTouchDragStart$ = singleTouchStart$.pipe(
mergeMap(
(): Observable<TouchEvent> => {
return this._singleTouchMove$.pipe(
takeUntil(
observableMerge(
touchStop$,
multipleTouchStart$)),
take(1));
}));
this._singleTouchDragEnd$ = singleTouchStart$.pipe(
mergeMap(
(): Observable<TouchEvent> => {
return observableMerge(
touchStop$,
multipleTouchStart$).pipe(
first());
}));
this._singleTouchDrag$ = singleTouchStart$.pipe(
switchMap(
(): Observable<TouchEvent> => {
return this._singleTouchMove$.pipe(
skip(1),
takeUntil(
observableMerge(
multipleTouchStart$,
touchStop$)));
}));
let touchesChanged$: Observable<TouchEvent> = observableMerge(
this._touchStart$,
this._touchEnd$,
this._touchCancel$);
this._pinchStart$ = touchesChanged$.pipe(
filter(
(te: TouchEvent): boolean => {
return te.touches.length === 2 && te.targetTouches.length === 2;
}));
this._pinchEnd$ = touchesChanged$.pipe(
filter(
(te: TouchEvent): boolean => {
return te.touches.length !== 2 || te.targetTouches.length !== 2;
}));
this._pinchOperation$ = new Subject<PinchOperation>();
this._pinch$ = this._pinchOperation$.pipe(
scan(
(pinch: TouchPinch, operation: PinchOperation): TouchPinch => {
return operation(pinch);
},
{
changeX: 0,
changeY: 0,
clientX: 0,
clientY: 0,
distance: 0,
distanceChange: 0,
distanceX: 0,
distanceY: 0,
originalEvent: null,
pageX: 0,
pageY: 0,
screenX: 0,
screenY: 0,
touch1: null,
touch2: null,
}));
const pinchSubscription = this._touchMove$.pipe(
filter(
(te: TouchEvent): boolean => {
return te.touches.length === 2 && te.targetTouches.length === 2;
}),
map(
(te: TouchEvent): PinchOperation => {
return (previous: TouchPinch): TouchPinch => {
let touch1: Touch = te.touches[0];
let touch2: Touch = te.touches[1];
let minX: number = Math.min(touch1.clientX, touch2.clientX);
let maxX: number = Math.max(touch1.clientX, touch2.clientX);
let minY: number = Math.min(touch1.clientY, touch2.clientY);
let maxY: number = Math.max(touch1.clientY, touch2.clientY);
let centerClientX: number = minX + (maxX - minX) / 2;
let centerClientY: number = minY + (maxY - minY) / 2;
let centerPageX: number = centerClientX + touch1.pageX - touch1.clientX;
let centerPageY: number = centerClientY + touch1.pageY - touch1.clientY;
let centerScreenX: number = centerClientX + touch1.screenX - touch1.clientX;
let centerScreenY: number = centerClientY + touch1.screenY - touch1.clientY;
let distanceX: number = Math.abs(touch1.clientX - touch2.clientX);
let distanceY: number = Math.abs(touch1.clientY - touch2.clientY);
let distance: number = Math.sqrt(distanceX * distanceX + distanceY * distanceY);
let distanceChange: number = distance - previous.distance;
let changeX: number = distanceX - previous.distanceX;
let changeY: number = distanceY - previous.distanceY;
let current: TouchPinch = {
changeX: changeX,
changeY: changeY,
clientX: centerClientX,
clientY: centerClientY,
distance: distance,
distanceChange: distanceChange,
distanceX: distanceX,
distanceY: distanceY,
originalEvent: te,
pageX: centerPageX,
pageY: centerPageY,
screenX: centerScreenX,
screenY: centerScreenY,
touch1: touch1,
touch2: touch2,
};
return current;
};
}))
.subscribe(this._pinchOperation$);
subs.push(pinchSubscription);
this._pinchChange$ = this._pinchStart$.pipe(
switchMap(
(): Observable<TouchPinch> => {
return this._pinch$.pipe(
skip(1),
takeUntil(this._pinchEnd$));
}));
}