threejs-camera-controller-pan-zoom-unified-pointer
Version:
A controller to pan and zoom a camera via a mouse or touch controls
177 lines (154 loc) • 4.2 kB
JavaScript
var Signal = require('signals').Signal;
var clamp = require('clamp');
function Controller(opts) {
var pointers = opts.pointers;
var onMouseWheelSignal = opts.mouseWheel ? opts.mouseWheel.onWheelSignal : null;
var _panning = false;
var _pinchEnabled = true;
var _singleFingerPanEnabled = opts.singleFingerPanEnabled !== undefined ? opts.singleFingerPanEnabled : true;
var _wheelEnabled = true;
var panSignal = new Signal();
var zoomSignal = new Signal();
var mousePosition = [0, 0];
var activePointers = [];
var positions = [];
for (var i = 0; i < 100; i++) {
positions.push(0, 0);
}
function onPointerDown(x, y, id) {
if(activePointers.indexOf(id) !== -1) return;
if(id === 0 && activePointers.length < 2) {
mousePosition[0] = x;
mousePosition[1] = y;
}
if(activePointers.length >= 2) return;
positions[id*2] = x;
positions[id*2+1] = y;
_panning = true;
activePointers.push(id);
}
function onPointerMove(x, y) {
mousePosition[0] = x;
mousePosition[1] = y;
}
function len(x, y) {
return Math.sqrt(x*x+y*y);
}
var indexAx, indexAy, indexBx, indexBy;
function onPointerDrag(x, y, id) {
if(id === 0 && activePointers.length < 2) {
mousePosition[0] = x;
mousePosition[1] = y;
}
if(activePointers.indexOf(id) === -1) return;
indexAx = id*2;
indexAy = indexAx+1;
var activeFingers = activePointers.length;
if (!_pinchEnabled) {
activeFingers = 1; // only allow panning
}
switch(activeFingers) {
case 1:
if(_singleFingerPanEnabled) {
panSignal.dispatch(
positions[id*2] - x,
positions[id*2+1] - y
);
}
break;
case 2:
indexBx = (id*2+2)%4;
indexBy = indexBx+1;
panSignal.dispatch(
(positions[indexAx] - x) * 0.5,
(positions[indexAy] - y) * 0.5
);
var zoom = len(
positions[indexAx] - positions[indexBx],
positions[indexAy] - positions[indexBy]
) / len(
x - positions[indexBx],
y - positions[indexBy]
);
zoomSignal.dispatch(
(positions[0] + positions[2]) * 0.5,
(positions[1] + positions[3]) * 0.5,
zoom
);
break;
}
positions[indexAx] = x;
positions[indexAy] = y;
}
function onMouseOut(x, y) {
onPointerUp(x, y, pointers.mouseID);
}
function onPointerUp(x, y, id) {
positions[id*2] = x;
positions[id*2+1] = y;
var index = activePointers.indexOf(id);
if(index !== -1) activePointers.splice(index);
_panning = false;
}
function onMouseWheelZoom(deltaX, deltaY, event) {
if (!_wheelEnabled) return;
zoomSignal.dispatch(
mousePosition[0],
mousePosition[1],
clamp((3000 + deltaY) / 3000, 0.8, 1.2),
true
);
}
var active = false;
function setState(state) {
if(state === active) return;
active = state;
var op = state ? 'add' : 'remove';
pointers.onPointerDownSignal[op](onPointerDown);
pointers.onPointerMoveSignal[op](onPointerMove);
pointers.onPointerDragSignal[op](onPointerDrag);
pointers.onPointerUpSignal[op](onPointerUp);
pointers.mouse.onOutSignal[op](onMouseOut);
onMouseWheelSignal[op](onMouseWheelZoom);
if(!state) {
_panning = false;
while(activePointers.length > 0) activePointers.splice(0, 1);
}
}
function isPanning() {
return _panning;
}
function setPinchEnabled(bool) {
_pinchEnabled = bool;
}
function isPinchEnabled() {
return _pinchEnabled;
}
function setSingleFingerPanEnabled(bool) {
_singleFingerPanEnabled = bool;
}
function isSingleFingerPanEnabled() {
return _singleFingerPanEnabled;
}
function setWheelEnabled(bool) {
_wheelEnabled = bool;
}
function isWheelEnabled() {
return _wheelEnabled
}
this.panSignal = panSignal;
this.zoomSignal = zoomSignal;
this.panSignal = panSignal;
this.zoomSignal = zoomSignal;
this.setState = setState;
this.onPointerDown = onPointerDown;
this.isPanning = isPanning;
this.setPinchEnabled = setPinchEnabled;
this.isPinchEnabled = isPinchEnabled;
this.setSingleFingerPanEnabled = setSingleFingerPanEnabled;
this.isSingleFingerPanEnabled = isSingleFingerPanEnabled;
this.setWheelEnabled = setWheelEnabled;
this.isWheelEnabled = isWheelEnabled;
this.activePointers = activePointers;
}
module.exports = Controller;