相机控制
1、平移,pan.js:
import * as THREE from 'three'
export default class Pan {
constructor ( param) {
const { camera, dampingFactor, target, domWidth, domHeight, enableDamping } = param
this . param = param
this . panOffset = new THREE. Vector3 ( ) ;
this . panStart = new THREE. Vector2 ( ) ;
this . panEnd = new THREE. Vector2 ( ) ;
this . panDelta = new THREE. Vector2 ( ) ;
this . panSpeed = 1
}
setPanStart ( x, y) {
this . panStart. set ( x, y)
}
setPanEnd ( x, y) {
this . panEnd. set ( x, y)
this . panDelta. subVectors ( this . panEnd, this . panStart) . multiplyScalar ( this . panSpeed)
this . setPanStart ( x, y)
this . pan ( this . panDelta. x, this . panDelta. y) ;
this . update ( )
}
panLeft ( distance, objectMatrix) {
var v = new THREE. Vector3 ( ) ;
v. setFromMatrixColumn ( objectMatrix, 0 ) ;
v. multiplyScalar ( - distance) ;
this . panOffset. add ( v) ;
}
panUp ( distance, objectMatrix) {
var v = new THREE. Vector3 ( ) ;
v. setFromMatrixColumn ( objectMatrix, 1 ) ;
v. multiplyScalar ( distance) ;
this . panOffset. add ( v) ;
}
pan ( deltaX, deltaY) {
const offset = new THREE. Vector3 ( )
const { camera, target, domWidth, domHeight } = this . param
var position = camera. position;
offset. copy ( position) . sub ( target) ;
var targetDistance = offset. length ( ) ;
targetDistance *= Math. tan ( ( camera. fov / 2 ) * Math. PI / 180.0 ) ;
this . panLeft ( 2 * deltaX * targetDistance / domWidth, camera. matrix) ;
this . panUp ( 2 * deltaY * targetDistance / domHeight, camera. matrix) ;
}
update ( ) {
const { camera, target } = this . param
camera. position. add ( this . panOffset)
target. add ( this . panOffset) ;
this . panOffset. set ( 0 , 0 , 0 )
}
}
2、缩放Dolly.js:
import * as THREE from 'three'
export default class Dolly {
constructor ( param) {
this . param = param
this . zoomSpeed = 1
this . scale = 1
}
handle ( deltaY) {
if ( deltaY> 0 ) {
this . dollyOut ( this . getZoomScale ( ) )
} else {
this . dollyIn ( this . getZoomScale ( ) )
}
this . update ( )
}
dollyOut ( dollyScale) {
this . scale /= dollyScale
}
dollyIn ( dollyScale) {
this . scale *= dollyScale
}
getZoomScale ( ) {
return Math. pow ( 0.95 , this . zoomSpeed) ;
}
update ( ) {
let { target } = this . param
let spherical = new THREE. Spherical ( ) ;
const offset = new THREE. Vector3 ( ) ;
var position = this . param. camera. position
offset. copy ( position) . sub ( target) ;
spherical. setFromVector3 ( offset) ;
spherical. radius *= this . scale
this . scale = 1
offset. setFromSpherical ( spherical) ;
position. copy ( target) . add ( offset) ;
}
}
3、旋转rotation.js
import * as THREE from 'three'
export default class Rotation {
constructor ( param) {
const { camera, dampingFactor, target, domWidth, domHeight, enableDamping } = param
this . param = param
this . spherical = new THREE. Spherical ( ) ;
this . sphericalDelta = new THREE. Spherical ( ) ;
this . rotateStart = new THREE. Vector2 ( ) ;
this . rotateEnd = new THREE. Vector2 ( ) ;
this . rotateDelta = new THREE. Vector2 ( ) ;
this . rotateSpeed = 1.0
this . quat = new THREE. Quaternion ( ) . setFromUnitVectors ( this . param. camera. up, new THREE. Vector3 ( 0 , 1 , 0 ) ) ;
this . quatInverse = this . quat. clone ( ) . invert ( ) ;
}
setRotateEnd ( x, y) {
this . rotateEnd. set ( x, y)
this . rotateDelta. subVectors ( this . rotateEnd, this . rotateStart) . multiplyScalar ( this . rotateSpeed) ;
this . rotateLeft ( 2 * Math. PI * this . rotateDelta. x / this . param. domWidth) ;
this . rotateUp ( 2 * Math. PI * this . rotateDelta. y / this . param. domHeight) ;
this . rotateStart. copy ( this . rotateEnd) ;
this . update ( )
}
setRotateStart ( x, y) {
this . rotateStart. set ( x, y)
}
rotateLeft ( angle) {
this . sphericalDelta. theta -= angle;
}
rotateUp ( angle) {
this . sphericalDelta. phi -= angle;
}
update ( ) {
let { target, dampingFactor, camera, enableDamping } = this . param
enableDamping = false
const offset = new THREE. Vector3 ( ) ;
var position = this . param. camera. position
offset. copy ( position) . sub ( target) ;
this . spherical. setFromVector3 ( offset) ;
if ( enableDamping) {
this . spherical. theta += this . sphericalDelta. theta * this . dampingFactor;
this . spherical. phi += this . sphericalDelta. phi * dampingFactor;
} else {
this . spherical. theta += this . sphericalDelta. theta;
this . spherical. phi += this . sphericalDelta. phi;
}
offset. setFromSpherical ( this . spherical) ;
position. copy ( target) . add ( offset) ;
camera. lookAt ( target) ;
if ( enableDamping) {
this . sphericalDelta. theta *= ( 1 - dampingFactor) ;
this . sphericalDelta. phi *= ( 1 - dampingFactor) ;
} else {
this . sphericalDelta. set ( 0 , 0 , 0 ) ;
}
}
}
4、使用
import * as THREE from 'three'
import Rotation from './Rotation.js'
import Pan from './Pan.js'
import Dolly from './Dolly.js'
export default class myOrbitControls {
constructor ( camera, domElement) {
this . camera = camera
this . domElement = domElement
this . target = new THREE. Vector3 ( 0 , 0 , 0 )
this . pan = new Pan ( {
camera: this . camera,
target: this . target,
domWidth: this . domElement. width,
domHeight: this . domElement. height,
} )
this . dolly = new Dolly ( {
camera: this . camera,
target: this . target,
domWidth: this . domElement. width,
domHeight: this . domElement. height,
} )
this . rotation = new Rotation ( {
camera: this . camera,
enableDamping: false ,
dampingFactor: 0.05 ,
target: this . target,
domWidth: this . domElement. width,
domHeight: this . domElement. height,
} )
this . mouseState = null
this . mouseAction = null
this . onPointerDown = this . onPointerDown. bind ( this )
this . onPointerMove = this . onPointerMove. bind ( this )
this . onPointerUp = this . onPointerUp. bind ( this )
this . onWheel = this . onWheel. bind ( this )
this . initEvent ( )
}
initEvent ( ) {
this . domElement. addEventListener ( 'pointerdown' , this . onPointerDown) ;
this . domElement. ownerDocument. addEventListener ( 'pointerup' , this . onPointerUp) ;
this . domElement. ownerDocument. addEventListener ( 'pointermove' , this . onPointerMove) ;
this . domElement. addEventListener ( 'contextmenu' , this . onContextMenu) ;
this . domElement. addEventListener ( 'wheel' , this . onWheel) ;
}
onWheel ( event) {
const { deltaY } = event
this . dolly. handle ( deltaY)
}
onContextMenu ( event) {
event. preventDefault ( ) ;
}
onPointerMove ( event) {
if ( this . mouseState != "down" ) return
const mouse = { x: event. clientX, y: event. clientY }
switch ( this . mouseAction) {
case THREE . MOUSE . ROTATE :
this . rotation. setRotateEnd ( mouse. x, mouse. y)
break ;
case THREE . MOUSE . PAN :
this . pan. setPanEnd ( mouse. x, mouse. y)
break
}
}
onPointerUp ( ) {
this . mouseState = "up"
}
onPointerDown ( event) {
this . mouseState = "down"
event. preventDefault ( ) ;
const mouse = { x: event. clientX, y: event. clientY }
switch ( event. button) {
case 0 :
this . mouseAction = THREE . MOUSE . ROTATE ;
this . rotation. setRotateStart ( mouse. x, mouse. y)
break ;
case 2 :
this . mouseAction = THREE . MOUSE . PAN ;
this . pan. setPanStart ( mouse. x, mouse. y)
break ;
default :
this . mouseAction = - 1 ;
}
}
}