Skip to content

Commit 1950086

Browse files
committed
feat: add WebXR UI components with theme integration and controller support
1 parent b2510e7 commit 1950086

File tree

7 files changed

+1724
-0
lines changed

7 files changed

+1724
-0
lines changed

webxr/README.md

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,64 @@ scene.add(myObject);
160160
controllerManager.registerInteractable(myObject);
161161
```
162162

163+
### Using Web Components for UI
164+
165+
The WebXR experience includes a set of Web Components for creating UI elements in VR:
166+
167+
#### VR UI Panel
168+
169+
The `<vr-ui-panel>` component creates a panel that can be positioned in 3D space, attached to controllers, or set to follow the user's gaze:
170+
171+
```html
172+
<!-- Create a panel that follows the user's gaze -->
173+
<vr-ui-panel title="Settings" follow-gaze>
174+
<!-- Panel content goes here -->
175+
</vr-ui-panel>
176+
177+
<!-- Create a panel attached to a controller -->
178+
<vr-ui-panel title="Controller Menu" controller-attached controller-index="0">
179+
<!-- Panel content goes here -->
180+
</vr-ui-panel>
181+
182+
<!-- Create a panel fixed in the world -->
183+
<vr-ui-panel title="World Panel" position="0,1.6,-1" rotation="0,0,0">
184+
<!-- Panel content goes here -->
185+
</vr-ui-panel>
186+
```
187+
188+
You can also create panels programmatically using the VRUIManager:
189+
190+
```javascript
191+
// Create a VR UI Manager
192+
const uiManager = new VRUIManager({
193+
scene: scene,
194+
camera: camera,
195+
controllers: controllers
196+
});
197+
198+
// Create a settings panel
199+
const settingsPanel = uiManager.createSettingsPanel({
200+
title: 'VR Settings',
201+
position: { x: 0, y: 1.6, z: -1 },
202+
followGaze: true
203+
});
204+
205+
// Add UI elements to the panel
206+
settingsPanel.addButton('Reset Position', () => {
207+
// Reset the user's position
208+
});
209+
210+
settingsPanel.addSlider(0, 100, 50, (value) => {
211+
// Handle slider change
212+
}, 'Volume');
213+
214+
settingsPanel.addToggle(true, (checked) => {
215+
// Handle toggle change
216+
}, 'Enable Teleportation');
217+
```
218+
219+
See the [UI Components Example](./examples/ui-components-example.html) for a complete demonstration.
220+
163221
### Adding Sounds
164222

165223
To add spatial audio:
@@ -205,6 +263,24 @@ import { integrateWebXR } from './webxr/hono-integration.js';
205263
integrateWebXR(app);
206264
```
207265

266+
### Theme Integration
267+
268+
The WebXR UI components use the main application's theme.css file to ensure a consistent look and feel. The UI components automatically adapt to theme changes, including dark mode support.
269+
270+
```css
271+
/* WebXR UI components use the main application's theme variables */
272+
@import url('/public/css/theme.css');
273+
274+
.vr-panel {
275+
background-color: var(--card-background);
276+
color: var(--text-primary);
277+
border: 2px solid var(--primary-color);
278+
/* ... */
279+
}
280+
```
281+
282+
This ensures that the WebXR experience looks and feels like a natural extension of the main application, with consistent colors, typography, and styling.
283+
208284
### Other Integration Options
209285

210286
Alternative integration options include:
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6+
<title>WebXR UI Components Example</title>
7+
<!-- Import the main application's theme -->
8+
<link rel="stylesheet" href="/public/css/theme.css">
9+
<style>
10+
body {
11+
margin: 0;
12+
padding: 0;
13+
overflow: hidden;
14+
font-family: var(--font-family);
15+
}
16+
17+
#info {
18+
position: absolute;
19+
top: 10px;
20+
width: 100%;
21+
text-align: center;
22+
color: var(--text-primary);
23+
background-color: var(--card-background);
24+
padding: 10px;
25+
z-index: 100;
26+
border-bottom: 2px solid var(--primary-color);
27+
}
28+
29+
#info a {
30+
color: var(--primary-color);
31+
}
32+
33+
#loading-overlay {
34+
position: fixed;
35+
top: 0;
36+
left: 0;
37+
width: 100%;
38+
height: 100%;
39+
background-color: var(--background-color);
40+
display: flex;
41+
flex-direction: column;
42+
align-items: center;
43+
justify-content: center;
44+
z-index: 1000;
45+
color: var(--text-primary);
46+
transition: opacity 0.5s ease;
47+
}
48+
49+
#loading-progress {
50+
width: 300px;
51+
height: 20px;
52+
background-color: var(--surface-variant);
53+
border-radius: var(--border-radius-full);
54+
margin-top: 20px;
55+
overflow: hidden;
56+
}
57+
58+
#progress-bar {
59+
height: 100%;
60+
width: 0%;
61+
background-color: var(--primary-color);
62+
transition: width 0.3s ease;
63+
}
64+
65+
.hidden {
66+
opacity: 0;
67+
pointer-events: none;
68+
}
69+
70+
#theme-toggle {
71+
position: absolute;
72+
top: 10px;
73+
right: 10px;
74+
z-index: 101;
75+
background-color: var(--surface-color);
76+
border: 1px solid var(--border-color);
77+
color: var(--text-primary);
78+
padding: 8px 12px;
79+
border-radius: var(--border-radius-md);
80+
cursor: pointer;
81+
font-family: var(--font-family);
82+
transition: all var(--transition-normal);
83+
}
84+
85+
#theme-toggle:hover {
86+
background-color: var(--surface-variant);
87+
}
88+
</style>
89+
</head>
90+
<body>
91+
<div id="loading-overlay">
92+
<h2>Loading WebXR Experience</h2>
93+
<div id="loading-progress">
94+
<div id="progress-bar"></div>
95+
</div>
96+
</div>
97+
98+
<button id="theme-toggle">Toggle Dark Mode</button>
99+
100+
<div id="info">
101+
<h1>WebXR UI Components Example</h1>
102+
<p>This example demonstrates how to use Web Components in a WebXR application.</p>
103+
<p>Click the 'Enter VR' button to experience in VR.</p>
104+
</div>
105+
106+
<script type="importmap">
107+
{
108+
"imports": {
109+
"three": "https://unpkg.com/[email protected]/build/three.module.js",
110+
"three/examples/jsm/webxr/VRButton.js": "https://unpkg.com/[email protected]/examples/jsm/webxr/VRButton.js",
111+
"three/examples/jsm/webxr/XRControllerModelFactory.js": "https://unpkg.com/[email protected]/examples/jsm/webxr/XRControllerModelFactory.js"
112+
}
113+
}
114+
</script>
115+
116+
<script type="module">
117+
// Theme toggle functionality
118+
const themeToggle = document.getElementById('theme-toggle');
119+
120+
// Check for saved theme preference or use device preference
121+
const savedTheme = localStorage.getItem('theme');
122+
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
123+
124+
// Set initial theme
125+
if (savedTheme === 'dark' || (!savedTheme && prefersDark)) {
126+
document.documentElement.setAttribute('data-theme', 'dark');
127+
themeToggle.textContent = 'Toggle Light Mode';
128+
}
129+
130+
// Toggle theme when button is clicked
131+
themeToggle.addEventListener('click', () => {
132+
const currentTheme = document.documentElement.getAttribute('data-theme');
133+
const newTheme = currentTheme === 'dark' ? '' : 'dark';
134+
135+
document.documentElement.setAttribute('data-theme', newTheme);
136+
localStorage.setItem('theme', newTheme);
137+
138+
themeToggle.textContent = newTheme === 'dark' ? 'Toggle Light Mode' : 'Toggle Dark Mode';
139+
});
140+
141+
// Simulate loading progress
142+
const progressBar = document.getElementById('progress-bar');
143+
const loadingOverlay = document.getElementById('loading-overlay');
144+
145+
let progress = 0;
146+
const interval = setInterval(() => {
147+
progress += Math.random() * 10;
148+
if (progress >= 100) {
149+
progress = 100;
150+
clearInterval(interval);
151+
152+
// Hide loading overlay after a short delay
153+
setTimeout(() => {
154+
loadingOverlay.classList.add('hidden');
155+
}, 500);
156+
}
157+
progressBar.style.width = `${progress}%`;
158+
}, 200);
159+
160+
// Import the UI components example
161+
import './ui-components-example.js';
162+
</script>
163+
</body>
164+
</html>

0 commit comments

Comments
 (0)