webxr-polyfill/examples/reticle/index.html

135 строки
4.2 KiB
HTML

<html>
<head>
<title>Reticle example</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<style>
body, html {
padding: 0;
margin: 0;
overflow: hidden;
position: fixed;
width: 100%;
height: 100vh;
-webkit-user-select: none;
user-select: none;
}
#target {
width: 100%;
height: 100%;
position: absolute;
}
.common-message {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 20px;
}
</style>
<link rel="stylesheet" href="../common.css"/>
<script src="../libs/three.min.js"></script>
<script type="module" src="../../polyfill/XRPolyfill.js"></script>
<script nomodule src="../../dist/webxr-polyfill.js"></script>
<script src="../common.js"></script>
</head>
<body>
<div id="target" />
<div onclick="hideMe(this)" id="description">
<h2>Reticle</h2>
<h5>(click to dismiss)</h5>
<p>Place a reticle on surfaces.</p>
</div>
<script>
/*
HitTestExample shows how to find surfaces or other features and place content relative to them.
In a production application, you would not create a separate anchor for every user action because
your application would quickly slow down tracking so many anchors. Instead, find an anchor
for groups of content that are positioned relative to some surface or other feature.
*/
class HitTestExample extends XRExampleBase {
constructor(domElement){
super(domElement, false)
this._tapEventData = null // Will be filled in on touch start and used in updateScene
// A message at the bottom of the screen that shows whether a surface has been found
this._messageEl = document.createElement('div')
this.el.appendChild(this._messageEl)
this._messageEl.style.position = 'absolute'
this._messageEl.style.bottom = '10px'
this._messageEl.style.left = '10px'
this._messageEl.style.color = 'white'
this._messageEl.style['font-size'] = '16px'
this._tapEventData = [ 0.5, 0.5 ]
this.el.addEventListener('touchstart', this._onTouchStart.bind(this), false)
}
// Called during construction to allow the app to populate this.scene
initializeScene(){
// Add a reticle at the scene
this.reticle = new THREE.Mesh(
new THREE.RingGeometry(0.04, 0.05, 36, 64),
new THREE.MeshBasicMaterial({ color: '#DDFFDD' })
)
this.reticle.geometry.applyMatrix(new THREE.Matrix4().makeRotationX(THREE.Math.degToRad(-90)))
this.reticle.visible = false
this.scene.add(this.reticle)
// Add a few lights
this.scene.add(new THREE.AmbientLight('#FFF', 0.2))
let directionalLight = new THREE.DirectionalLight('#FFF', 0.6)
directionalLight.position.set(0, 10, 0)
this.scene.add(directionalLight)
}
// Called once per frame, before render, to give the app a chance to update this.scene
updateScene(frame){
const x = this._tapEventData[0]
const y = this._tapEventData[1]
const hit = frame.hitTestNoAnchor(x, y);
var model = new THREE.Matrix4();
var tempPos = new THREE.Vector3();
var tempQuat = new THREE.Quaternion();
var tempScale = new THREE.Vector3();
if (hit && hit.length > 0) {
this.reticle.visible = true
model.fromArray(hit[0].modelMatrix);
model.decompose(tempPos, tempQuat, tempScale);
this.reticle.position.copy(tempPos);
this.reticle.quaternion.copy(tempQuat);
}
}
// Save screen taps as normalized coordinates for use in this.updateScene
_onTouchStart(ev){
if (!ev.touches || ev.touches.length === 0) {
console.error('No touches on touch event', ev)
return
}
//save screen coordinates normalized to -1..1 (0,0 is at center and 1,1 is at top right)
this._tapEventData = [
ev.touches[0].clientX / window.innerWidth,
ev.touches[0].clientY / window.innerHeight
]
}
}
window.addEventListener('DOMContentLoaded', () => {
setTimeout(() => {
try {
window.pageApp = new HitTestExample(document.getElementById('target'))
} catch(e) {
console.error('page error', e)
}
}, 1000)
})
</script>
</body>
</html>