diff --git a/src/webxr/ARButton.js b/src/webxr/ARButton.ts similarity index 69% rename from src/webxr/ARButton.js rename to src/webxr/ARButton.ts index 0cef7de7..36b61dc4 100644 --- a/src/webxr/ARButton.js +++ b/src/webxr/ARButton.ts @@ -1,28 +1,37 @@ +import { WebGLRenderer, XRSession, XRSessionInit } from 'three' + +export interface ARButtonSessionInit extends XRSessionInit { + domOverlay?: { + root: HTMLDivElement + } +} + class ARButton { - static createButton(renderer, sessionInit = {}) { + static createButton( + renderer: WebGLRenderer, + sessionInit: ARButtonSessionInit = {}, + ): HTMLButtonElement | HTMLAnchorElement { const button = document.createElement('button') function showStartAR(/*device*/) { if (sessionInit.domOverlay === undefined) { - var overlay = document.createElement('div') + const overlay = document.createElement('div') overlay.style.display = 'none' document.body.appendChild(overlay) - var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg') - svg.setAttribute('width', 38) - svg.setAttribute('height', 38) + const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg') + svg.setAttribute('width', '38') + svg.setAttribute('height', '38') svg.style.position = 'absolute' svg.style.right = '20px' svg.style.top = '20px' - svg.addEventListener('click', function () { - currentSession.end() - }) + svg.addEventListener('click', () => currentSession?.end()) overlay.appendChild(svg) - var path = document.createElementNS('http://www.w3.org/2000/svg', 'path') + const path = document.createElementNS('http://www.w3.org/2000/svg', 'path') path.setAttribute('d', 'M 12,12 L 28,28 M 28,12 12,28') path.setAttribute('stroke', '#fff') - path.setAttribute('stroke-width', 2) + path.setAttribute('stroke-width', '2') svg.appendChild(path) if (sessionInit.optionalFeatures === undefined) { @@ -35,9 +44,9 @@ class ARButton { // - let currentSession = null + let currentSession: XRSession | null - async function onSessionStarted(session) { + async function onSessionStarted(session: XRSession) { session.addEventListener('end', onSessionEnded) renderer.xr.setReferenceSpaceType('local') @@ -45,16 +54,16 @@ class ARButton { await renderer.xr.setSession(session) button.textContent = 'STOP AR' - sessionInit.domOverlay.root.style.display = '' + sessionInit.domOverlay!.root.style.display = '' currentSession = session } function onSessionEnded(/*event*/) { - currentSession.removeEventListener('end', onSessionEnded) + currentSession?.removeEventListener('end', onSessionEnded) button.textContent = 'START AR' - sessionInit.domOverlay.root.style.display = 'none' + sessionInit.domOverlay!.root.style.display = 'none' currentSession = null } @@ -79,6 +88,9 @@ class ARButton { button.onclick = function () { if (currentSession === null) { + // @ts-expect-error waiting for TypeScript lib.dom.d.ts update + // https://developer.mozilla.org/en-US/docs/Web/API/Navigator/xr + // https://github.com/Microsoft/TypeScript/blob/master/lib/lib.dom.d.ts navigator.xr.requestSession('immersive-ar', sessionInit).then(onSessionStarted) } else { currentSession.end() @@ -101,11 +113,10 @@ class ARButton { function showARNotSupported() { disableButton() - button.textContent = 'AR NOT SUPPORTED' } - function stylizeElement(element) { + function stylizeElement(element: HTMLElement) { element.style.position = 'absolute' element.style.bottom = '20px' element.style.padding = '12px 6px' @@ -126,9 +137,12 @@ class ARButton { stylizeElement(button) + // @ts-expect-error waiting for TypeScript lib.dom.d.ts update + // https://developer.mozilla.org/en-US/docs/Web/API/Navigator/xr + // https://github.com/Microsoft/TypeScript/blob/master/lib/lib.dom.d.ts navigator.xr .isSessionSupported('immersive-ar') - .then(function (supported) { + .then((supported: boolean) => { supported ? showStartAR() : showARNotSupported() }) .catch(showARNotSupported) diff --git a/src/webxr/VRButton.js b/src/webxr/VRButton.ts similarity index 76% rename from src/webxr/VRButton.js rename to src/webxr/VRButton.ts index 501ba4d4..31f3091d 100644 --- a/src/webxr/VRButton.js +++ b/src/webxr/VRButton.ts @@ -1,30 +1,22 @@ -class VRButton { - static createButton(renderer, options) { - if (options) { - console.error( - 'THREE.VRButton: The "options" parameter has been removed. Please set the reference space type via renderer.xr.setReferenceSpaceType() instead.', - ) - } +import { WebGLRenderer, XRSession } from 'three' +class VRButton { + static createButton(renderer: WebGLRenderer): HTMLButtonElement | HTMLAnchorElement { const button = document.createElement('button') function showEnterVR(/*device*/) { - let currentSession = null + let currentSession: XRSession | null - async function onSessionStarted(session) { + async function onSessionStarted(session: XRSession) { session.addEventListener('end', onSessionEnded) - await renderer.xr.setSession(session) button.textContent = 'EXIT VR' - currentSession = session } function onSessionEnded(/*event*/) { - currentSession.removeEventListener('end', onSessionEnded) - + currentSession?.removeEventListener('end', onSessionEnded) button.textContent = 'ENTER VR' - currentSession = null } @@ -38,16 +30,13 @@ class VRButton { button.textContent = 'ENTER VR' - button.onmouseenter = function () { - button.style.opacity = '1.0' - } - - button.onmouseleave = function () { - button.style.opacity = '0.5' - } + button.onmouseenter = () => (button.style.opacity = '1.0') + button.onmouseleave = () => (button.style.opacity = '0.5') - button.onclick = function () { - if (currentSession === null) { + button.onclick = () => { + if (currentSession) { + currentSession.end() + } else { // WebXR's requestReferenceSpace only works if the corresponding feature // was requested at session creation time. For simplicity, just ask for // the interesting ones as optional features, but be aware that the @@ -58,14 +47,14 @@ class VRButton { const sessionInit = { optionalFeatures: ['local-floor', 'bounded-floor', 'hand-tracking'], } - navigator.xr.requestSession('immersive-vr', sessionInit).then(onSessionStarted) - } else { - currentSession.end() + + const xr: THREE.XR = (navigator as any).xr + xr.requestSession('immersive-vr', sessionInit).then(onSessionStarted) } } } - function disableButton() { + function disableButton(): void { button.style.display = '' button.style.cursor = 'auto' @@ -80,11 +69,10 @@ class VRButton { function showWebXRNotFound() { disableButton() - button.textContent = 'VR NOT SUPPORTED' } - function stylizeElement(element) { + function stylizeElement(element: HTMLElement): void { element.style.position = 'absolute' element.style.bottom = '20px' element.style.padding = '12px 6px' @@ -105,7 +93,8 @@ class VRButton { stylizeElement(button) - navigator.xr.isSessionSupported('immersive-vr').then(function (supported) { + const xr: THREE.XR = (navigator as any).xr + xr.isSessionSupported('immersive-vr').then((supported) => { supported ? showEnterVR() : showWebXRNotFound() })