Search code examples
javascriptimageaframe

A-Frame change image on run time


I want to load an image after assets loaded from a server or a simple web address.

This is my index.html from https://youmustfight.github.io/aframe-asset-lazy-load/no-component.

var assets = $("a-assets");
var sphere1 = $('.sphere-1');
var sphere1 = $('.sphere-2');

// Example Sphere 1
setTimeout(function() {
  // Append Img Element to Assets - Immediately starting load of content
  assets.prepend('<img id="example-sphere-1" src="background.png">');
  //assets.prepend('<img id="example-sphere-1" src="../background.png">');
  // Upon Image Load being Done - Update Skybox Entity
  $('#example-sphere-1').on('load', function() {
    $('a-entity.sphere-1').attr("material", "src: #example-sphere-1");
  });
});

function getPokemonAtIndex(pokeIndex) {
  var pokeSprite;
  var baseURL = "https://pokeapi.co/api/v2/";
  var pURL = baseURL + "pokemon/" + pokeIndex.toString();

  var xmlhttp = new XMLHttpRequest();
  xmlhttp.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 200) {
      var myObj = JSON.parse(this.responseText);
      pokeSprite = myObj.sprites.front_default;
    }
  };
  xmlhttp.open("GET", pURL, true);
  xmlhttp.send();
  return pokeSprite;
}

setTimeout(function() {
  assets.prepend('<img id="example-sphere-1" src="getPokemonAtIndex(800)">');
  $('#example-sphere-2').on('load', function() {
    $('a-entity.sphere-2').attr("material", "src: #example-sphere-2");
  });
}, 1550);
<html>

<head>
  <script src="https://aframe.io/releases/1.2.0/aframe.min.js"></script>
  <script type="text/javascript" src="https://code.jquery.com/jquery-2.2.0.js"></script>
</head>

<body>
  <a-assets></a-assets>
  <a-scene>
    <a-assets>
    </a-assets>
    <!-- <a-image position="-150 0 -1500" rotation="0 0 0" height="1000" width="1000" src="../background.png"></a-image> -->
    <a-entity class="sphere-1" geometry="primitive: sphere;
                  radius: 200;
                  segments-width: 64;
                  segments-height: 64;" material="" scale="-1 1 1" rotation="0 -105 0" position="-400 0 -700"></a-entity>
    <a-entity class="sphere-2" geometry="primitive: sphere;
                  radius: 200;
                  segments-width: 64;
                  segments-height: 64;" material="" scale="-1 1 1" rotation="0 -105 0" position="300 0 -700"></a-entity>
  </a-scene>
  <script src="example.js"></script>
</body>

</html>

The getPokemonAtIndex function retrieves the address of the sprite from pokemon with the id 800. The console log shows this address. Should I use the fileLoader/imageLoader or something else?

Has somebody an idea what to do?


Solution

  • <a-assets> are supposed to be used for preloading. You can simply use the image URL as the material source (element.setAttribute("material", "src", URL)) like this:

    <script src="https://aframe.io/releases/1.2.0/aframe.min.js"></script>
    <script>
      AFRAME.registerComponent("foo", {
        init: function() {
          // switch the material source whenever a new sprite is ready
          this.el.addEventListener("sprite-ready", e => {
            this.el.setAttribute("material", "src", this.pokeSrc)
          })
    
          // iterate through the pokedex
          var i = 1;
          setInterval(e => {
            this.requestPokemonAtIndex(i++);
            if (i > 150) i = 1;
          }, 750)
        },
        // request an image, emit an event when the resource is ready
        requestPokemonAtIndex: function(pokeIndex) {
          var self = this;
          var baseURL = "https://pokeapi.co/api/v2/";
          var pURL = baseURL + "pokemon/" + pokeIndex.toString();
    
          var xmlhttp = new XMLHttpRequest();
          xmlhttp.onreadystatechange = function() {
            if (this.readyState == 4 && this.status == 200) {
              var myObj = JSON.parse(this.responseText);
              let pokeSprite = myObj.sprites.front_default;
              self.pokeSrc = pokeSprite;
              self.el.emit("sprite-ready")
            }
          };
          xmlhttp.open("GET", pURL, true);
          xmlhttp.send();
        }
      })
    </script>
    <a-scene>
      <a-box position="0 1 -3" rotation="0 45 0" foo></a-box>
      <a-sky color="#ECECEC"></a-sky>
    </a-scene>

    You can also use an THREE.ImageLoader but you would have to dive into the underlying three.js layer:

    // load the image
    const image = new THREE.ImageLoader().load(URL);
    // grab the mesh
    const mesh = element.getObject3D("mesh")
    // grab the material reference
    const material = mesh.material;
    // use the loaded image
    material.map = image;
    // reset the material
    material.needsUpdate = true;