|
| 1 | +--- |
| 2 | +title: iframe Reality Layers |
| 3 | +type: api |
| 4 | +layout: docs |
| 5 | +order: 5 |
| 6 | +parent_section: api |
| 7 | +--- |
| 8 | + |
| 9 | +Exokit implements normal [`<iframe>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe) functionality with the ability to do volumetric manipulation. |
| 10 | + |
| 11 | + |
| 12 | +## Create an iframe |
| 13 | +`const iframe = document.createElement('iframe');` |
| 14 | + |
| 15 | + |
| 16 | +- `iframe.src` |
| 17 | + - The URL or file path. |
| 18 | +- `iframe.positon` |
| 19 | + - The position vector as an array. |
| 20 | +- `iframe.scale` |
| 21 | + - The scale vector as an array. |
| 22 | +- `iframe.orientation` |
| 23 | + - The orientation quaternion as an array. |
| 24 | +- `iframe.d` |
| 25 | + - The dimensions of the iframe. `2` gives you DOM-to-texture. `3` gives you reality layers. |
| 26 | + |
| 27 | +then the `iframe` needs to be put onto the session layers: |
| 28 | +`display.session.layers.push(iframe);` |
| 29 | + |
| 30 | +## iframe Reality Layers example |
| 31 | +This section walks through the Reality Layers Exokit example. The `menumesh` refers to the GUI used in the example. See the full [realitytabs.html](https://github.com/exokitxr/exokit/blob/master/examples/realitytabs.html) example on GitHub. |
| 32 | + |
| 33 | + |
| 34 | + |
| 35 | + |
| 36 | +### Create the iframe |
| 37 | +```js |
| 38 | +const iframe = document.createElement('iframe'); |
| 39 | +iframe.onconsole = (jsString, scriptUrl, startLine) => { |
| 40 | + console.log('parent got console', {jsString, scriptUrl, startLine}); |
| 41 | +}; |
| 42 | +iframe.onload = function() { |
| 43 | + const contentDocument = (() => { |
| 44 | + try { |
| 45 | + if (this.contentDocument) { // this potentially throws |
| 46 | + return this.contentDocument; |
| 47 | + } else { |
| 48 | + return null; |
| 49 | + } |
| 50 | + } catch(err) { |
| 51 | + console.warn(err.stack); |
| 52 | + return null; |
| 53 | + } |
| 54 | + })(); |
| 55 | + if (contentDocument) { |
| 56 | + _drawOk(); |
| 57 | + // scene.background = null; |
| 58 | + } else { |
| 59 | + _drawFail(); |
| 60 | + _closeTab(tab); |
| 61 | + if (focusedTab === tab) { |
| 62 | + focusedTab = rig.menuMesh.urlMesh; |
| 63 | + } |
| 64 | + rig.menuMesh.urlMesh.updateText(); |
| 65 | + _updateRigLists(); |
| 66 | + } |
| 67 | +}; |
| 68 | +iframe.d = d; |
| 69 | +iframe.src = u; |
| 70 | +/* iframe.addEventListener('destroy', () => { |
| 71 | + // scene.background = _makeBackground(); |
| 72 | +}); */ |
| 73 | +const tab = _addTab(iframe, position, orientation, scale, d, local, id); |
| 74 | +}; |
| 75 | +const _addTab = (iframe, position = new THREE.Vector3(), orientation = new THREE.Quaternion(), scale, d = 3, local = true, id = tabId++) => { |
| 76 | +if (scale === undefined) { |
| 77 | + scale = new THREE.Vector3(1, d === 3 ? 1 : window.innerHeight/window.innerWidth, 1); |
| 78 | + if (d !== 3) { |
| 79 | + scale.multiplyScalar(0.4); |
| 80 | + } |
| 81 | +} |
| 82 | +iframe.position = position.toArray(); |
| 83 | +iframe.orientation = orientation.toArray(); |
| 84 | +iframe.scale = scale.toArray(); |
| 85 | +document.body.appendChild(iframe); |
| 86 | +const tab = { |
| 87 | + url: iframe.src, |
| 88 | + id, |
| 89 | + iframe, |
| 90 | +}; |
| 91 | +tabs.push(tab); |
| 92 | +layers.push(iframe); |
| 93 | +focusedTab = tab; |
| 94 | +rig.menuMesh.urlMesh.updateText(); |
| 95 | +rig.menuMesh.listMesh.updateList(); |
| 96 | +return tab; |
| 97 | +}; |
| 98 | +``` |
| 99 | + |
| 100 | +### Closing a tab |
| 101 | +```js |
| 102 | +const _closeTab = tab => { |
| 103 | +const {id, iframe} = tab; |
| 104 | +if (iframe.destroy) { |
| 105 | + iframe.destroy(); |
| 106 | +} |
| 107 | +document.body.removeChild(iframe); |
| 108 | +tabs.splice(tabs.indexOf(tab), 1); |
| 109 | +layers.splice(layers.indexOf(iframe), 1); |
| 110 | +if (serverConnectedUrl) { |
| 111 | + const objectMesh = xrmp.getObjectMeshes().find(objectMesh => objectMesh.object.id === id); |
| 112 | + xrmp.removeObjectMesh(objectMesh); |
| 113 | +} |
| 114 | +}; |
| 115 | +``` |
| 116 | + |
| 117 | +### Close all tabs |
| 118 | +```js |
| 119 | +const _closeAllTabs = () => { // XXX trigger this when switching servers |
| 120 | +for (let i = 0; i < tabs.length; i++) { |
| 121 | + const {iframe} = tab; |
| 122 | + if (iframe.destroy) { |
| 123 | + iframe.destroy(); |
| 124 | + } |
| 125 | + document.body.removeChild(iframe); |
| 126 | +} |
| 127 | +tabs.length = 0; |
| 128 | +layers.length = 0; |
| 129 | +}; |
| 130 | +``` |
| 131 | + |
| 132 | +### Send keys to iframe |
| 133 | + |
| 134 | +```js |
| 135 | +window.addEventListener('keydown', e => { |
| 136 | + if (window.document.pointerLockElement) { |
| 137 | + switch (e.which) { |
| 138 | + case 87: { // W |
| 139 | + keys.up = true; |
| 140 | + /* if (!window.document.pointerLockElement) { |
| 141 | + renderer.domElement.requestPointerLock(); |
| 142 | + } */ |
| 143 | + break; |
| 144 | + } |
| 145 | + case 83: { // S |
| 146 | + keys.down = true; |
| 147 | + /* if (!window.document.pointerLockElement) { |
| 148 | + renderer.domElement.requestPointerLock(); |
| 149 | + } */ |
| 150 | + break; |
| 151 | + } |
| 152 | + case 65: { // A |
| 153 | + keys.left = true; |
| 154 | + /* if (!window.document.pointerLockElement) { |
| 155 | + renderer.domElement.requestPointerLock(); |
| 156 | + } */ |
| 157 | + break; |
| 158 | + } |
| 159 | + case 68: { // D |
| 160 | + keys.right = true; |
| 161 | + /* if (!window.document.pointerLockElement) { |
| 162 | + renderer.domElement.requestPointerLock(); |
| 163 | + } */ |
| 164 | + break; |
| 165 | + } |
| 166 | + case 69: { // E |
| 167 | + fakeDisplay.gamepads[1].buttons[2].pressed = true; |
| 168 | + break; |
| 169 | + } |
| 170 | + case 32: { // space |
| 171 | + keys.space = true; |
| 172 | + break; |
| 173 | + } |
| 174 | + case 17: { // ctrl |
| 175 | + keys.ctrl = true; |
| 176 | + break; |
| 177 | + } |
| 178 | + } |
| 179 | + } else { |
| 180 | + if (focusedTab) { |
| 181 | + if (focusedTab === rig.menuMesh.urlMesh) { |
| 182 | + rig.menuMesh.urlMesh.handleKey(e.keyCode, e.shiftKey); |
| 183 | + } else if (focusedTab.iframe) { |
| 184 | + focusedTab.iframe.sendKeyDown(e.which, { |
| 185 | + shiftKey: e.shiftKey, |
| 186 | + ctrlKey: e.ctrlKey, |
| 187 | + altKey: e.altKey, |
| 188 | + }); |
| 189 | + } |
| 190 | + } |
| 191 | + } |
| 192 | +}); |
| 193 | +window.addEventListener('keyup', e => { |
| 194 | + if (window.document.pointerLockElement) { |
| 195 | + switch (e.which) { |
| 196 | + case 87: { // W |
| 197 | + keys.up = false; |
| 198 | + break; |
| 199 | + } |
| 200 | + case 83: { // S |
| 201 | + keys.down = false; |
| 202 | + break; |
| 203 | + } |
| 204 | + case 65: { // A |
| 205 | + keys.left = false; |
| 206 | + break; |
| 207 | + } |
| 208 | + case 68: { // D |
| 209 | + keys.right = false; |
| 210 | + break; |
| 211 | + } |
| 212 | + case 69: { // E |
| 213 | + fakeDisplay.gamepads[1].buttons[2].pressed = false; |
| 214 | + break; |
| 215 | + } |
| 216 | + case 32: { // space |
| 217 | + keys.space = false; |
| 218 | + break; |
| 219 | + } |
| 220 | + case 17: { // ctrl |
| 221 | + keys.ctrl = false; |
| 222 | + break; |
| 223 | + } |
| 224 | + } |
| 225 | + } else { |
| 226 | + if (focusedTab && focusedTab.iframe) { |
| 227 | + focusedTab.iframe.sendKeyUp(e.which, { |
| 228 | + shiftKey: e.shiftKey, |
| 229 | + ctrlKey: e.ctrlKey, |
| 230 | + altKey: e.altKey, |
| 231 | + }); |
| 232 | + } |
| 233 | + } |
| 234 | +}); |
| 235 | +window.addEventListener('keypress', e => { |
| 236 | + if (!window.document.pointerLockElement) { |
| 237 | + if (focusedTab) { |
| 238 | + /* if (focusedTab === rig.menuMesh.urlMesh) { |
| 239 | + rig.menuMesh.urlMesh.handleKey(e.keyCode, e.shiftKey); |
| 240 | + } else */if (focusedTab.iframe) { |
| 241 | + focusedTab.iframe.sendKeyPress(e.which, { |
| 242 | + shiftKey: e.shiftKey, |
| 243 | + ctrlKey: e.ctrlKey, |
| 244 | + altKey: e.altKey, |
| 245 | + }); |
| 246 | + } |
| 247 | + } |
| 248 | + } |
| 249 | +}); |
| 250 | +``` |
0 commit comments