OpenGL 与 3D第 4 / 4 章
输入事件状态机
统一鼠标、触摸、滚轮和键盘事件,并管理控制器状态切换。
input-state
function onPointerDown(event) {
if (scope.enabled === false) return
if (pointers.length === 0) {
scope.domElement.setPointerCapture(event.pointerId)
scope.domElement.addEventListener('pointermove', onPointerMove)
scope.domElement.addEventListener('pointerup', onPointerUp)
}
//
if (isTrackingPointer(event)) return
//
addPointer(event)
if (event.pointerType === 'touch') {
} else {
onMouseDown(event)
}
}
function onPointerMove(event) {
dollyState.changePos = true
if (scope.enabled === false) return
if (event.pointerType === 'touch') {
} else {
onMouseMove(event)
}
}
function onPointerUp(event) {
removePointer(event)
switch (pointers.length) {
case 0:
scope.domElement.releasePointerCapture(event.pointerId)
scope.domElement.removeEventListener('pointermove', onPointerMove)
scope.domElement.removeEventListener('pointerup', onPointerUp)
scope.dispatchEvent(_endEvent)
state = STATE.NONE
break
case 1:
break
}
}
function onMouseDown(event) {
let mouseAction
dollyState.changePos = true
switch (event.button) {
case 0:
mouseAction = scope.mouseButtons.LEFT
break
case 1:
mouseAction = scope.mouseButtons.MIDDLE
break
case 2:
mouseAction = scope.mouseButtons.RIGHT
break
default:
mouseAction = -1
}
switch (mouseAction) {
case MOUSE.DOLLY:
if (scope.enableZoom === false) return
handleMouseDownDolly(event)
state = STATE.DOLLY
break
case MOUSE.ROTATE:
if (event.ctrlKey || event.metaKey || event.shiftKey) {
if (scope.enablePan === false) return
handleMouseDownPan(event)
state = STATE.PAN
} else {
if (scope.enableRotate === false) return
handleMouseDownRotate(event)
state = STATE.ROTATE
}
break
case MOUSE.PAN:
if (event.ctrlKey || event.metaKey || event.shiftKey) {
if (scope.enableRotate === false) return
handleMouseDownRotate(event)
state = STATE.ROTATE
} else {
if (scope.enablePan === false) return
handleMouseDownPan(event)
state = STATE.PAN
}
break
default:
state = STATE.NONE
}
if (state !== STATE.NONE) {
scope.dispatchEvent(_startEvent)
}
}
function onMouseMove(event) {
switch (state) {
case STATE.ROTATE:
if (scope.enableRotate === false) return
handleMouseMoveRotate(event)
break
case STATE.DOLLY:
if (scope.enableZoom === false) return
handleMouseMoveDolly(event)
break
case STATE.PAN:
if (scope.enablePan === false) return
handleMouseMovePan(event)
break
}
}
function onMouseWheel(event) {
if (scope.enabled === false || scope.enableZoom === false || state !== STATE.NONE) return
event.preventDefault()
scope.dispatchEvent(_startEvent)
handleMouseWheel(customWheelEvent(event))
// handleMouseWheel(event)
scope.dispatchEvent(_endEvent)
}
function handleMouseMove(event){
dollyState.changePos = true
}
function customWheelEvent(event) {
const mode = event.deltaMode
// minimal wheel event altered to meet delta-zoom demand
const newEvent = {
clientX: event.clientX,
clientY: event.clientY,
deltaY: event.deltaY,
}
// switch (mode) {
// case 1: // LINE_MODE
// newEvent.deltaY *= 16
// break
// case 2: // PAGE_MODE
// newEvent.deltaY *= 100
// break
// }
// detect if event was triggered by pinching
if (event.ctrlKey && !controlActive) {
newEvent.deltaY *= 10
}
return newEvent
}
function interceptControlDown(event) {
if (event.key === 'Control') {
controlActive = true
const document = scope.domElement.getRootNode() // offscreen canvas compatibility
document.addEventListener('keyup', interceptControlUp, { passive: true, capture: true })
}
}
function interceptControlUp(event) {
if (event.key === 'Control') {
controlActive = false
const document = scope.domElement.getRootNode() // offscreen canvas compatibility
document.removeEventListener('keyup', interceptControlUp, { passive: true, capture: true })
}
}
function onKeyDown(event) {
if (scope.enabled === false || scope.enablePan === false || event.target.nodeName == 'INPUT') return
handleKeyDown(event)
}
function onContextMenu(event) {
if (scope.enabled === false) return
event.preventDefault()
}
function addPointer(event) {
pointers.push(event.pointerId)
}
function removePointer(event) {
delete pointerPositions[event.pointerId]
for (let i = 0; i < pointers.length; i++) {
if (pointers[i] == event.pointerId) {
pointers.splice(i, 1)
return
}
}
}
function isTrackingPointer(event) {
for (let i = 0; i < pointers.length; i++) {
if (pointers[i] == event.pointerId) return true
}
return false
}
//
scope.domElement.addEventListener('contextmenu', onContextMenu)
scope.domElement.addEventListener('mousemove', handleMouseMove)
scope.domElement.addEventListener('pointerdown', onPointerDown)
scope.domElement.addEventListener('pointercancel', onPointerUp)
scope.domElement.addEventListener('wheel', onMouseWheel, { passive: false })
const document = scope.domElement.getRootNode() // offscreen canvas compatibility
document.addEventListener('keydown', interceptControlDown, { passive: true, capture: true })
// force an update at start
this.update()
}
}
export default OrbitControls