-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Brian Chirls
committed
Nov 27, 2020
1 parent
48cadba
commit bee27e1
Showing
3 changed files
with
260 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
export default function DOMRenderer(control, options = {}) { | ||
const { device } = control; | ||
const { | ||
innerRadius = Math.max(24, device.radius / 4), | ||
parentElement = document.body | ||
} = options; | ||
|
||
const outer = document.createElement('div'); | ||
Object.assign(outer.style, { | ||
position: 'fixed', | ||
pointerEvents: 'none', | ||
borderRadius: 999e20 + 'px', | ||
|
||
transition: 'opacity 0.1s', | ||
|
||
//temp | ||
backgroundColor: 'rgba(128, 128, 128, 0.5)' | ||
}); | ||
|
||
const inner = document.createElement('div'); | ||
Object.assign(inner.style, { | ||
position: 'fixed', | ||
pointerEvents: 'none', | ||
borderRadius: 999e20 + 'px', | ||
|
||
transition: 'opacity 0.1s', | ||
|
||
//temp | ||
backgroundColor: 'rgba(128, 128, 128, 0.5)' | ||
}); | ||
outer.appendChild(inner); | ||
|
||
let rafId = 0; | ||
|
||
function render() { | ||
const [x, y] = control.read(); | ||
|
||
if (control.mode === 'static' || x || y) { | ||
const outerSize = device.radius * 2; | ||
const ox = device.x - device.radius; | ||
const oy = device.y - device.radius; | ||
|
||
outer.style.left = ox + 'px'; | ||
outer.style.top = oy + 'px'; | ||
outer.style.width = outer.style.height = outerSize + 'px'; | ||
outer.style.opacity = 1; | ||
|
||
const innerSize = innerRadius * 2; | ||
|
||
inner.style.left = device.x + x * device.radius - innerRadius + 'px'; | ||
inner.style.top = device.y - y * device.radius - innerRadius + 'px'; | ||
inner.style.width = inner.style.height = innerSize + 'px'; | ||
inner.style.opacity = 1; | ||
} else { | ||
outer.style.opacity = 0; | ||
inner.style.opacity = 0; | ||
} | ||
|
||
rafId = requestAnimationFrame(render); | ||
} | ||
|
||
rafId = requestAnimationFrame(render); | ||
|
||
this.destroy = () => { | ||
cancelAnimationFrame(rafId); | ||
|
||
if (outer.parentNode) { | ||
outer.parentNode.removeChild(outer); | ||
} | ||
}; | ||
|
||
parentElement.appendChild(outer); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,166 @@ | ||
import StickInputControl from '../../controls/StickInputControl'; | ||
|
||
export default function VirtualJoystick({ | ||
element = document.body, | ||
radius = 60, | ||
x = 0, | ||
y = 0, | ||
mode = 'dynamic', | ||
lockX = false, | ||
lockY = false, | ||
touch = true, | ||
pen = true, | ||
mouse = false, | ||
filter = null, | ||
touchActionStyle = true | ||
} = {}) { | ||
|
||
let startEvent = null; | ||
let lastEvent = null; | ||
let startX = 0; | ||
let startY = 0; | ||
let deltaX = 0; | ||
let deltaY = 0; | ||
|
||
const isStatic = mode === 'static'; | ||
const supported = mouse || pen || touch && navigator.maxTouchPoints > 0; | ||
|
||
const allowedPointerTypes = { | ||
pen, | ||
mouse, | ||
touch | ||
}; | ||
|
||
/* | ||
For now, each device only handles one pointer at a time | ||
*/ | ||
function pointerDown(evt) { | ||
if (!startEvent && | ||
allowedPointerTypes[evt.pointerType] && (!filter || filter(evt))) { | ||
|
||
if (isStatic) { | ||
const dx = evt.offsetX - x; | ||
const dy = evt.offsetY - y; | ||
if (Math.hypot(dx, dy) > radius) { | ||
return; | ||
} | ||
|
||
startX = x; | ||
startY = y; | ||
} else { | ||
startX = evt.offsetX; | ||
startY = evt.offsetY; | ||
} | ||
|
||
startEvent = evt; | ||
lastEvent = evt; | ||
} | ||
} | ||
|
||
function pointerMove(evt) { | ||
if (startEvent && startEvent.pointerId === evt.pointerId) { | ||
lastEvent = evt; | ||
|
||
// calculate and set x/y | ||
const dx = lockX ? 0 : evt.offsetX - startX; | ||
const dy = lockY ? 0 : evt.offsetY - startY; | ||
const length = Math.hypot(dx, dy); | ||
|
||
const divisor = Math.max(length, radius); | ||
deltaX = dx / divisor; | ||
deltaY = -dy / divisor; | ||
} | ||
} | ||
|
||
function pointerUp(evt) { | ||
if (startEvent && startEvent.pointerId === evt.pointerId) { | ||
lastEvent = evt; | ||
startEvent = null; | ||
} | ||
} | ||
|
||
const styleElement = touchActionStyle && element.style ? element : document.body; | ||
if (touchActionStyle) { | ||
styleElement.style.touchAction = 'none'; | ||
} | ||
|
||
this.getControl = (/*name,*/ options = {}) => { | ||
// if (typeof options === 'string') { | ||
// throw new Error('VirtualJoystick accepts options object'); | ||
// } | ||
const read = () => { | ||
return startEvent ? | ||
[deltaX, deltaY] : | ||
StickInputControl.defaultValue; | ||
}; | ||
|
||
return new StickInputControl(read, Object.assign( | ||
{}, | ||
options, | ||
{ device: this } | ||
)); | ||
}; | ||
|
||
this.destroy = () => { | ||
if (touchActionStyle) { | ||
styleElement.style.touchAction = ''; | ||
} | ||
|
||
element.removeEventListener('pointerdown', pointerDown); | ||
element.removeEventListener('pointermove', pointerMove); | ||
element.removeEventListener('pointerup', pointerUp); | ||
element.removeEventListener('pointercancel', pointerUp); | ||
}; | ||
|
||
Object.defineProperties(this, { | ||
pointerType: { | ||
get: () => lastEvent && lastEvent.pointerType || '' | ||
}, | ||
|
||
connected: { | ||
enumerable: false, | ||
configurable: false, | ||
writable: false, | ||
value: supported | ||
}, | ||
|
||
timestamp: { | ||
get: () => lastEvent && lastEvent.timeStamp || 0 | ||
}, | ||
|
||
element: { | ||
enumerable: false, | ||
configurable: false, | ||
writable: false, | ||
value: element | ||
}, | ||
|
||
x: { | ||
get: () => isStatic ? x : startX, | ||
set: val => { | ||
x = val; | ||
} | ||
}, | ||
|
||
y: { | ||
get: () => isStatic ? y : startY, | ||
set: val => { | ||
y = val; | ||
} | ||
}, | ||
|
||
radius: { | ||
get: () => radius, | ||
set: val => { | ||
radius = val; | ||
} | ||
} | ||
}); | ||
|
||
if (supported) { | ||
element.addEventListener('pointerdown', pointerDown); | ||
element.addEventListener('pointermove', pointerMove); | ||
element.addEventListener('pointerup', pointerUp); | ||
element.addEventListener('pointercancel', pointerUp); | ||
} | ||
} |