mapillary-js
Version:
WebGL JavaScript library for displaying street level imagery from mapillary.com
134 lines (113 loc) • 4.4 kB
text/typescript
import {merge as observableMerge, Observable, Subscription} from "rxjs";
import {filter, withLatestFrom, map} from "rxjs/operators";
import * as THREE from "three";
import {
Component,
IMouseConfiguration,
HandlerBase,
} from "../../Component";
import {
Transform,
ViewportCoords,
} from "../../Geo";
import {RenderCamera} from "../../Render";
import {
ICurrentState,
IFrame,
} from "../../State";
import {
Container,
IPinch,
Navigator,
} from "../../Viewer";
/**
* The `TouchZoomHandler` allows the user to zoom the viewer image by pinching on a touchscreen.
*
* @example
* ```
* var mouseComponent = viewer.getComponent("mouse");
*
* mouseComponent.touchZoom.disable();
* mouseComponent.touchZoom.enable();
*
* var isEnabled = mouseComponent.touchZoom.isEnabled;
* ```
*/
export class TouchZoomHandler extends HandlerBase<IMouseConfiguration> {
private _viewportCoords: ViewportCoords;
private _activeSubscription: Subscription;
private _preventDefaultSubscription: Subscription;
private _zoomSubscription: Subscription;
/** @ignore */
constructor(
component: Component<IMouseConfiguration>,
container: Container,
navigator: Navigator,
viewportCoords: ViewportCoords) {
super(component, container, navigator);
this._viewportCoords = viewportCoords;
}
protected _enable(): void {
this._preventDefaultSubscription = this._container.touchService.pinch$
.subscribe(
(pinch: IPinch): void => {
pinch.originalEvent.preventDefault();
});
let pinchStarted$: Observable<boolean> =
this._container.touchService.pinchStart$.pipe(
map(
(event: TouchEvent): boolean => {
return true;
}));
let pinchStopped$: Observable<boolean> =
this._container.touchService.pinchEnd$.pipe(
map(
(event: TouchEvent): boolean => {
return false;
}));
this._activeSubscription = observableMerge(
pinchStarted$,
pinchStopped$)
.subscribe(this._container.touchService.activate$);
this._zoomSubscription = this._container.touchService.pinch$.pipe(
withLatestFrom(this._navigator.stateService.currentState$),
filter(
(args: [IPinch, IFrame]): boolean => {
let state: ICurrentState = args[1].state;
return state.currentNode.fullPano || state.nodesAhead < 1;
}),
map(
(args: [IPinch, IFrame]): IPinch => {
return args[0];
}),
withLatestFrom(
this._container.renderService.renderCamera$,
this._navigator.stateService.currentTransform$))
.subscribe(
([pinch, render, transform]: [IPinch, RenderCamera, Transform]): void => {
let element: HTMLElement = this._container.element;
let [canvasX, canvasY]: number[] = this._viewportCoords.canvasPosition(pinch, element);
let unprojected: THREE.Vector3 =
this._viewportCoords.unprojectFromCanvas(
canvasX,
canvasY,
element,
render.perspective);
let reference: number[] = transform.projectBasic(unprojected.toArray());
const [canvasWidth, canvasHeight]: number[] = this._viewportCoords.containerToCanvas(element);
let zoom: number = 3 * pinch.distanceChange / Math.min(canvasWidth, canvasHeight);
this._navigator.stateService.zoomIn(zoom, reference);
});
}
protected _disable(): void {
this._activeSubscription.unsubscribe();
this._preventDefaultSubscription.unsubscribe();
this._zoomSubscription.unsubscribe();
this._preventDefaultSubscription = null;
this._zoomSubscription = null;
}
protected _getConfiguration(enable: boolean): IMouseConfiguration {
return { touchZoom: enable };
}
}
export default TouchZoomHandler;