UNPKG

kaleidoscopejs

Version:
189 lines (166 loc) 6.55 kB
import THREE from 'threejs360'; import utils from './utils' let easeOutBack = k => { let s = 1.70158; return --k * k * ((s + 1) * k + s) + 1; }; export default class Controls { constructor(options) { Object.assign(this, options); this.el = this.renderer.el; this.theta = this.initialYaw * Math.PI / 180; this.phi = 0; this.velo = 0.02; this.rotateStart = new THREE.Vector2(); this.rotateEnd = new THREE.Vector2(); this.rotateDelta = new THREE.Vector2(); this.orientation = new THREE.Quaternion(); this.euler = new THREE.Euler(); this.momentum = false; this.isUserInteracting = false; this.addDraggableStyle(); this.onMouseMove = this.onMouseMove.bind(this); this.onMouseDown = this.onMouseDown.bind(this); this.onMouseUp = this.onMouseUp.bind(this); this.onTouchStart = e => this.onMouseDown({clientX: e.touches[0].pageX, clientY: e.touches[0].pageY}); this.onTouchMove = e => this.onMouseMove({clientX: e.touches[0].pageX, clientY: e.touches[0].pageY}); this.onTouchEnd = _ => this.onMouseUp(); this.onDeviceMotion = this.onDeviceMotion.bind(this); this.onMessage = this.onMessage.bind(this); this.bindEvents(); } bindEvents() { this.el.addEventListener('mouseleave', this.onMouseUp); this.el.addEventListener('mousemove', this.onMouseMove); this.el.addEventListener('mousedown', this.onMouseDown); this.el.addEventListener('mouseup', this.onMouseUp); this.el.addEventListener('touchstart', this.onTouchStart); this.el.addEventListener('touchmove', this.onTouchMove); this.el.addEventListener('touchend', this.onTouchEnd); if (!this.isInIframe()) window.addEventListener('devicemotion', this.onDeviceMotion); window.addEventListener('message', this.onMessage); } centralize() { let endTheta = this.initialYaw * Math.PI / 180; let duration = 750; let startTheta = this.theta; let startPhi = this.phi; let start = Date.now(); let animate = () => { let progress = Date.now() - start; let elapsed = progress / duration; elapsed = elapsed > 1 ? 1 : elapsed; if (progress >= duration) { return cancelAnimationFrame(id); } this.theta = startTheta + (endTheta - startTheta) * easeOutBack(elapsed); this.phi = startPhi + (0 - startPhi) * easeOutBack(elapsed); return requestAnimationFrame(animate); }; let id = animate(); } isInIframe() { try { return window.self !== window.top;    } catch (e) { return true;    } } destroy() { this.el.removeEventListener('mouseleave', this.onMouseUp); this.el.removeEventListener('mousemove', this.onMouseMove); this.el.removeEventListener('mousedown', this.onMouseDown); this.el.removeEventListener('mouseup', this.onMouseUp); this.el.removeEventListener('touchstart', this.onTouchStart); this.el.removeEventListener('touchmove', this.onTouchMove); this.el.removeEventListener('touchend', this.onTouchEnd); window.removeEventListener('devicemotion', this.onDeviceMotion); window.removeEventListener('message', this.onMessage); } getCurrentStyle() { return `height: ${parseInt(this.el.style.height, 10)}px; width: ${parseInt(this.el.style.width, 10)}px; user-select: none; -webkit-user-select: none; -webkit-touch-callout: none; -webkit-tap-highlight-color: rgba(0,0,0,0);`; } addDraggingStyle() { this.el.setAttribute('style', `${this.getCurrentStyle()} cursor: -webkit-grabbing; cursor: -moz-grabbing; cursor: grabbing;`); } addDraggableStyle() { this.el.setAttribute('style', `${this.getCurrentStyle()} cursor: -webkit-grab; cursor: -moz-grab; cursor: grab;`); } onMessage(event) { let {orientation, portrait, rotationRate} = event.data; if (!rotationRate) return; this.onDeviceMotion({orientation, portrait, rotationRate}); } onDeviceMotion(event) { let portrait = event.portrait !== undefined ? event.portrait : window.matchMedia("(orientation: portrait)").matches; let orientation; if (event.orientation !== undefined) { orientation = event.orientation; } else if (window.orientation !== undefined) { orientation = window.orientation; } else { orientation = -90; } let alpha = THREE.Math.degToRad(event.rotationRate.alpha); let beta = THREE.Math.degToRad(event.rotationRate.beta); if (portrait) { this.phi = this.verticalPanning ? this.phi + alpha * this.velo : this.phi; this.theta = this.theta - beta * this.velo * -1; } else { if (this.verticalPanning) { this.phi = orientation === -90 ? this.phi + beta * this.velo : this.phi - beta * this.velo; } this.theta = orientation === -90 ? this.theta - alpha * this.velo : this.theta + alpha * this.velo; } this.adjustPhi(); } onMouseMove(event) { if (!this.isUserInteracting) { return; } this.rotateEnd.set(event.clientX, event.clientY); this.rotateDelta.subVectors(this.rotateEnd, this.rotateStart); this.rotateStart.copy(this.rotateEnd); this.phi = this.verticalPanning ? this.phi + 2 * Math.PI * this.rotateDelta.y / this.renderer.height * 0.3 : this.phi; this.theta += 2 * Math.PI * this.rotateDelta.x / this.renderer.width * 0.5; this.adjustPhi(); } adjustPhi() { // Prevent looking too far up or down. this.phi = THREE.Math.clamp(this.phi, -Math.PI / 1.95, Math.PI / 1.95); } onMouseDown(event) { this.addDraggingStyle(); this.rotateStart.set(event.clientX, event.clientY); this.isUserInteracting = true; this.momentum = false; this.onDragStart && this.onDragStart(); } inertia() { if (!this.momentum) return; this.rotateDelta.y *= 0.90; this.rotateDelta.x *= 0.90; this.theta += 0.005 * this.rotateDelta.x; this.phi = this.verticalPanning ? this.phi + 0.005 * this.rotateDelta.y : this.phi; this.adjustPhi(); } onMouseUp() { this.isUserInteracting && this.onDragStop && this.onDragStop(); this.addDraggableStyle(); this.isUserInteracting = false; this.momentum = true; this.inertia(); } update() { if ((this.phi === this.previousPhi) && (this.theta === this.previousTheta)) return false; this.previousPhi = this.phi; this.previousTheta = this.theta; this.euler.set(this.phi, this.theta, 0, 'YXZ'); this.orientation.setFromEuler(this.euler); this.camera.quaternion.copy(this.orientation); this.inertia(); return true; } }