Search code examples
aframear.js

AFrame - Set Size of Camera Feed


My last question was marked as duplicate: A-Frame AR - Set Size of Scene

Here is the question that it was marked duplicate of: Aframe and aframe-ar: display video stream in div instead of fullscreen

Using iframe is not the optimal solution for me, I need the AR to be present on my webpage. The answerer alludes to changing the source code to be able to resize the scene and camera video. Can someone help me achieve this?


Solution

  • tldr.

    Afaik arjs has hardcoded sizes and multiple boundaries all over the place to keep the library universal (different devices with different aspect ratios, mobile portrait vs landscape etc...).

    Re-creating it all with "embedding" options would be a huge project so I'll focus on a simple example with a non - resizable window.

    Disclaimer: most of the following is just my take, mostly showing which parts I changed and why. All of this could be done in many other ways.

    0. a wrapper element

    So the idea is to be able to embed the video + scene into some div, like here enter image description here

    You can see both the video and scene are contained within a div - #arjs-cell. So just after the namespace I've added my variable for keeping my wrapper element, and a setter, which will also reparent the video:

    var THREEx = THREEx || {}
    THREEx.wrapperElement = null;
    THREEx.setWrapperElement = function(wrapperEl, video) {
      THREEx.wrapperElement = wrapperEl
      wrapperEl.appendChild(video);
    }
    

    1. The video element

    Upon creation the video element is given multiple style attributes (source) like position, top, left, zIndex). In this case I've just threw out the left position and changed the z-index.

    The width / height is a completely different story. For the first 10000 * 1000ms arjs recalculates multiple things including the video size (source). It makes it difficult to change the elements "from the outside". Anyway one of those recalculated things is the video element size, which is based on the window dimensions (source)

    We can get cut in with our wrapper:

    ARjs.Source.prototype.onResizeElement = function () {
    // (...)
      var screenWidth = window.innerWidth;
      var screenHeight = window.innerHeight;
      if (THREEx.wrapperElement) {
          const bounds = THREEx.wrapperElement.getBoundingClientRect();
          screenWidth = THREEx.wrapperElement.offsetWidth;
          screenHeight = THREEx.wrapperElement.offsetHeight;
          // by default the video parent is the window, so top is 0;
          this.domElement.style.top = bounds.top + 'px';
      }
    

    2. The a-frame scene

    To adjust the scene arjs sets the body dimensions to the <video> element ( (source) forcing an update in a-frame. Since we don't want to mess with the <body>, lets do the same on the wrapperElement:

    AFRAME.registerSystem('arjs', {
    // (...)
      // ugly kludge to get resize on aframe... not even sure it works
      if (arProfile.contextParameters.trackingBackend !== 'tango' && THREEx.wrapperElement) {
        arSource.copyElementSizeTo(THREEx.wrapperElement)
      }
    

    arSource.copyElementSizeTo() will mess with the margins, so lets comment that out:

    ARjs.Source.prototype.copyElementSizeTo = function (otherElement) {
    if (window.innerWidth > window.innerHeight) {
       //landscape
       otherElement.style.width = this.domElement.style.width
       otherElement.style.height = this.domElement.style.height
       // otherElement.style.marginLeft = this.domElement.style.marginLeft
       // otherElement.style.marginTop = this.domElement.style.marginTop
    }
    // (...)
    

    3. Putting it all to use

    Now we can wait for the video (and arjs core) to be fully initialized, and do our magic:

    window.addEventListener("arjs-video-loaded", evt => {
      const video = evt.detail.component;
      const wrapper = document.querySelector("#arjs-cell")
      THREEx.setWrapperElement(wrapper, video)
    })
    

    You can check it out here.


    It really would be way easier not to break the library and use an iframe. You can easily call functions withing the iframed website - check it out here