avansel
Version:
Free OpenSource ThreeJS Javascript Virtual Tours viewer
302 lines (240 loc) • 9.93 kB
text/typescript
import { MathUtils } from 'three'
import Camera from '../Components/Camera'
import { normLng } from '../Components/utils'
import { controls } from '../config.json'
import { CameraPosition } from '../Types'
enum TouchType { Touch, Zoom }
export default class Controls{
camera: Camera
canvas: HTMLCanvasElement
tween: boolean
fovMax: number
fovMin: number
isInteracting: boolean
onTouchDist: number
onTouchFov: number
touchType: TouchType
onMouseDownMouseX: number
onMouseDownMouseY: number
lng: number
lngVector: number
onMouseDownLng: number
lat: number
latVector: number
onMouseDownLat: number
fov: number
fovVector: number
phi: number
theta: number
onTouchStartHandler: any
onClickHandler: any
onMouseDownHandler: any
onMouseWheelHandler: any
onTouchMoveHandler: any
onTouchUpHandler: any
onMouseMoveHandler: any
onMouseUpHandler: any
constructor(camera: Camera, canvas: HTMLCanvasElement) {
this.camera = camera;
this.canvas = canvas
this.tween = true
this.fovMax = controls.fovMax;
this.fovMin = controls.fovMin;
this.isInteracting = false
this.onMouseDownMouseX = 0
this.onMouseDownMouseY = 0
this.lng = 90
this.lngVector = 90
this.onMouseDownLng = 0
this.lat = 0
this.latVector = 0
this.onMouseDownLat = 0
this.phi = 0
this.theta = 0
this.fov = 70
this.fovVector = 70
this.init()
}
init(){
this.onTouchStartHandler = this.onTouchStart.bind(this)
this.onClickHandler = this.onClick.bind(this)
this.onMouseDownHandler = this.onMouseDown.bind(this)
this.onMouseWheelHandler = this.onMouseWheel.bind(this)
this.canvas.addEventListener('touchstart', this.onTouchStartHandler)
this.canvas.addEventListener( 'click', this.onClickHandler )
this.canvas.addEventListener( 'mousedown', this.onMouseDownHandler )
this.canvas.addEventListener( 'mousewheel', this.onMouseWheelHandler )
this.camera.lookAt(this.lat, this.lng)
}
setTween(tween: boolean){
this.tween = tween
}
position(): CameraPosition{
return { lat: this.lat, lng: normLng(this.lng), fov: this.fov }
}
tick(delta: number) {
if ( this.isInteracting === false ) {
//lng += 0.025;
}
this.lat = Math.max( - 90, Math.min( 89.9999999999, this.lat ) );
if(this.tween){
if(this.fovVector != this.fov){
let diff = (this.fov - this.fovVector) / 10
if(Math.abs(this.fovVector / diff) > 500000){
this.fov = this.fovVector
}else{
this.fov = this.fov - diff
}
this.onFovChanged()
}
let posChanged = false
if(this.latVector != this.lat){
let diff = (this.lat - this.latVector) / 10
if(Math.abs(this.fov / diff) > 100000){
this.lat = this.latVector
}else{
this.lat = this.lat - diff
}
posChanged = true
}
if(this.lngVector != this.lng){
let diff = (this.lng - this.lngVector) / 10
if(Math.abs(this.fov / diff) > 100000){
this.lng = this.lngVector
}else{
this.lng = this.lng - diff
}
posChanged = true
}
if(posChanged) this.onPosChanged();
}
}
// Touch events
onTouchStart(e: TouchEvent){
var touches = e.touches
if(touches.length == 1){
this.touchType = TouchType.Touch
}else if(touches.length == 2){
this.touchType = TouchType.Zoom
}
if(this.touchType == TouchType.Touch && e.type == 'touchstart'){
var touch = touches[0]
this.isInteracting = true
this.onMouseDownMouseX = touch.pageX
this.onMouseDownMouseY = touch.pageY
this.onMouseDownLng = this.lng
this.onMouseDownLat = this.lat
}
if(this.touchType == TouchType.Zoom){
this.onTouchDist = Math.hypot(
touches[0].pageX - touches[1].pageX,
touches[0].pageY - touches[1].pageY
)
this.onTouchFov = this.fov
}
this.onTouchMoveHandler = this.onTouchMove.bind(this)
this.onTouchUpHandler = this.onTouchUp.bind(this)
this.canvas.addEventListener( 'touchmove', this.onTouchMoveHandler )
this.canvas.addEventListener( 'touchend', this.onTouchUpHandler )
this.canvas.addEventListener( 'touchcancel', this.onTouchUpHandler )
e.preventDefault()
}
onTouchMove(e: TouchEvent){
var touches = e.touches
var newTouchType: TouchType
if(touches.length == 1){
newTouchType = TouchType.Touch
}else if(touches.length == 2){
newTouchType = TouchType.Zoom
}
if(this.touchType != newTouchType){
return null
}
if(this.touchType == TouchType.Touch && e.type == 'touchmove'){
var touch = touches[0]
this.lng = ( this.onMouseDownMouseX - touch.pageX ) * this.camera.get().fov * controls.moveFactor / this.canvas.clientWidth + this.onMouseDownLng;
this.lat = ( touch.pageY - this.onMouseDownMouseY ) * this.camera.get().fov * controls.moveFactor / this.canvas.clientWidth + this.onMouseDownLat;
this.latVector = this.lat
this.lngVector = this.lng
this.onPosChanged()
}
if(this.touchType == TouchType.Zoom && e.type == 'touchmove'){
var dist = Math.hypot(
touches[0].pageX - touches[1].pageX,
touches[0].pageY - touches[1].pageY
)
var diff = this.onTouchDist - dist
this.fov = MathUtils.clamp( this.onTouchFov + diff * this.onTouchFov / 500, this.fovMin, this.fovMax )
this.fovVector = this.fov
this.onFovChanged()
}
}
onTouchUp(e: TouchEvent){
this.isInteracting = false
this.canvas.removeEventListener( 'touchmove', this.onTouchMoveHandler )
this.canvas.removeEventListener( 'touchend', this.onTouchUpHandler )
this.canvas.removeEventListener( 'touchcancel', this.onTouchMoveHandler )
this.touchType = null
if(e.changedTouches.length == 1){
const touch = e.changedTouches[0]
if(touch.clientX == this.onMouseDownMouseX && touch.clientY == this.onMouseDownMouseY){
this.onTap(e)
}
}
}
onTap(e: TouchEvent){
console.log('onTap')
}
// mouse events
onMouseDown(e: MouseEvent){
this.isInteracting = true
this.onMouseDownMouseX = e.clientX
this.onMouseDownMouseY = e.clientY
this.onMouseDownLng = this.lng
this.onMouseDownLat = this.lat
this.onMouseMoveHandler = this.onMouseMove.bind(this)
this.onMouseUpHandler = this.onMouseUp.bind(this)
this.canvas.addEventListener( 'mousemove', this.onMouseMoveHandler )
this.canvas.addEventListener( 'mouseup', this.onMouseUpHandler )
this.canvas.addEventListener( 'mouseleave', this.onMouseUpHandler )
}
onMouseMove(e: MouseEvent){
if(this.tween){
this.lngVector = ( this.onMouseDownMouseX - e.clientX ) * this.camera.get().fov * controls.moveFactor / this.canvas.clientWidth + this.onMouseDownLng
this.latVector = ( e.clientY - this.onMouseDownMouseY ) * this.camera.get().fov * controls.moveFactor / this.canvas.clientWidth + this.onMouseDownLat
}else{
this.lng = ( this.onMouseDownMouseX - e.clientX ) * this.camera.get().fov * controls.moveFactor / this.canvas.clientWidth + this.onMouseDownLng;
this.lat = ( e.clientY - this.onMouseDownMouseY ) * this.camera.get().fov * controls.moveFactor / this.canvas.clientWidth + this.onMouseDownLat;
this.onPosChanged()
}
}
onMouseUp(e: MouseEvent){
this.isInteracting = false
this.canvas.removeEventListener( 'mousemove', this.onMouseMoveHandler )
this.canvas.removeEventListener( 'mouseup', this.onMouseUpHandler )
this.canvas.removeEventListener( 'mouseleave', this.onMouseUpHandler )
}
onMouseWheel(e: WheelEvent){
e.preventDefault()
e.stopPropagation()
if(this.tween){
this.fovVector = MathUtils.clamp(this.fovVector + e.deltaY * this.fovVector / 1000, this.fovMin, this.fovMax )
}else{
this.fov = MathUtils.clamp( this.fov + e.deltaY * this.fov / 1000, this.fovMin, this.fovMax )
this.onFovChanged()
}
}
onClick(e: PointerEvent){
if(e.clientX == this.onMouseDownMouseX && e.clientY == this.onMouseDownMouseY){
this.canvas.dispatchEvent(new CustomEvent('panoClick', {detail: e}))
}
}
onFovChanged(){
this.camera.setFov(this.fov)
this.canvas.dispatchEvent( new CustomEvent('fovChanged', {detail: { fov: this.fov }}) )
}
onPosChanged(){
this.camera.lookAt(this.lat, this.lng)
this.canvas.dispatchEvent(new CustomEvent('cameraMove', {detail: { lat: this.lat, lng: this.lng }}))
}
}