kikoano / web2vr

Dynamically translate HTML and CSS to A-Frame 3D world for virtual reality.
MIT License
129 stars 23 forks source link

add a more 'aframe' style interface #18

Open kylebakerio opened 3 years ago

kylebakerio commented 3 years ago

The basic example is a bit surprising, since there's not even an a-scene necessary to be present, and since the javascript called is raw JS, outside of an A-Frame component. Further, one has to turn off the default feature to create its own sky (which is again, pretty surprising), and specify position and parent element through params.

It would be nice to have an interface that is syntactic sugar that enables a flow more like this:


const divSrc = `
    <div id="html-container">
        <button id="btn1">Button</button>
        <input id="btn2" type="button" value="Input button">
    </div>
`
const panel = document.createElement('a-div');
panel.setAttribute('html', divSrc)
const inWorldMenuAnchor = document.querySelector('#menu-anchor')
inWorldMenuAnchor.appendChild(panel)

Or, another possible option:

const divSrc = web2vr.node('div', {
    id: "html-container"
  }, [
   web2vr.node('button', { id: "btn1" }, "Button"),
   web2vr.node('input', { id: "btn2" }, "input button")
  ]
)

const panel = web2vr.node('a-div', {
  html: divSrc
});

const inWorldMenuAnchor = document.querySelector('#menu-anchor')
inWorldMenuAnchor.appendChild(panel)

I'm thinking I want to switch over to this library for my in-game menu system, and if I do, I'll probably write a wrapper to accomplish this. Would you be interested in a pull request for this type of feature?

kylebakerio commented 2 years ago

So, for now I've at least built out this component to wrap web2vr and make it correspond to a more idiomatic usage:

        // restructuring web2vr to work as an aframe component
        AFRAME.registerComponent('add-web2vr', {
          schema: {
            pos: {
              // relative to parent component (this.el), instead of world position
              type: 'vec3',
              default: {
                x: 0, y: 0, z: 0
              }
            },
            keyboardPos: {
              // relative to parent component (this.el), instead of world position
              type: 'vec3',
              default: {
                x: 0, y: 0, z: 0
              }
            },
            "selfSelector": {
              // probably ignore this
              type: 'string',
              default: '',
            },
            'htmlSelector': {
              // the html element to clone into VR
              // note: when you pass in e.g. '#html-container', because this schema specifies 
              // type: 'selector',
              // a-frame will parse that and this.data.htmlSelector will actually be the dom node itself, not the text
              type: 'selector',
              default: '',
            },
            debug: {
              // turn on web2vr's built-in verbose mode
              type: 'bool',
              default: false,
            },
          },

          setSelector() {
            // this is... not pretty, but this is a work around for web2vr's odd choice of only allowing specifying an anchor
            // by selector, instead of more idiomatically by attaching a component. So, we define a unique selector, add it, and then
            // pass that in to web2vr
            this.data.selfSelector = this.data.selfSelector || `anchor${Math.round(Math.random()*1000000)}`;
            this.el.setAttribute(
              'class', 
              this.el.getAttribute('class') + " " + this.data.selfSelector
            )
          },

          init() {
            if (!this.data.htmlSelector) {
              throw new Error("no html to inject specified");
            }

            this.setSelector();

            const web2vr = new Web2VR(this.data.htmlSelector/*document.getElementById("html-container")*/, { 
              parentSelector: this.data.selfSelector, 
              position: this.data.pos, //{ x: 0, y: 1.5, z: 0 },
              debug: this.data.bool, 
              skybox: false,            // this is very non-idiomatic, so we remove access to this option
              createControllers: false, // this is very non-idiomatic, so we remove access to this option
            });
            web2vr.start();

            document.querySelector('.vr-container').addEventListener('click',evt => {
              console.log('clicked container', evt)

              document.querySelector('#vr-keyboard').setAttribute('position', this.data.keyboardPos)
            });

          }
        })

example html that uses it:

   <!-- The div we're adding into the scene; notice the id of the parent that we grab here  -->
    <div id="html-container" style="border-radius:10px;">
      <h1>Input-Test</h1>
      <div style="border-radius:10px;">
        <input type="radio" id="bike" name="vehicle" value="bike">
        <label for="bike">Bike</label>
        <input type="radio" id="car" name="vehicle" value="car">
        <label for="car">Car</label>
        <input type="radio" id="boat" name="vehicle" value="boat">
        <label for="boat">Boat</label>
      </div>
      <div>
        <input type="checkbox" id="vehicle1" name="vehicle1" value="Bike" style="border-radius:10px;">
        <label for="vehicle1"> I have a bike</label>
        <input type="checkbox" id="vehicle2" name="vehicle2" value="Car">
        <label for="vehicle2"> I have a car</label>
        <input type="checkbox" id="vehicle3" name="vehicle3" value="Boat">
        <label for="vehicle3"> I have a boat</label>
      </div>
      <input type="range" id="points" name="points" min="0" max="10">
      <br/><br/>
      <input id="name-input" type="text" placeholder="Name">
      <br/><br/>
      <input type="text" placeholder="Sex">
      <br/>
      <input type="text" placeholder="Location"><br/><br/>
      <textarea placeholder="your complaints..." id="w3review" name="w3review" rows="4" cols="17" maxlength="200"></textarea>
    </div>

    <!--   the actual scene; notice the add-web2vr component on the #anchor box   -->
    <a-scene add-inputs vr-super-stats="alwaysshow3dstats:true; showgraphs:null;">
      <a-super-sky orbitduration="100000" startpercent=".2" super-sky-debug="false"></a-super-sky>
      <a-circle rotation="-90 0 0" radius="100"></a-circle>
      <a-box material="color:red;" 
             add-web2vr="
                         pos: 0 1.5 -5; 
                         htmlSelector: #html-container; 
                         keyboardPos: -.27 .8 -5;" 
             id="anchor" 
             position="0 0 -5">
      </a-box>
      <a-entity position="0 1.6 0" movement-controls="camera:[camera]; fly:true; enabled:true;">
        <a-entity look-controls camera>
          <a-entity
              id="cursor-mouse"
              cursor="fuse: false; rayOrigin:mouse;"
              raycaster="enabled: true; showLine: false; far: 4; interval:100; near: 0.03; objects: .vr-interactable, .raycastable; origin: 0.001 0 0;"
              position="0 0 -0.1"
          ></a-entity>
        </a-entity>
        <!-- we have to add .vr-interactable to click the inputs, .collidable to interact with keyboard-->
        <a-entity id="leftHand" laser-controls="hand:left" raycaster="showLine:true; far:10; objects:.vr-interactable, .collidable;"></a-entity>
        <a-entity id="rightHand" laser-controls="hand:right" raycaster="showLine:true; far:10; objects:.vr-interactable, .collidable;"></a-entity> 
      </a-entity>
    </a-scene>
kylebakerio commented 2 years ago

(you can see and remix a demo using it here: https://glitch.com/edit/#!/web2vr-demo?path=index.html%3A27%3A0)