Adding dom elements

I have been experimenting with playcanvas and I am trying to better understand how it implements html/css/js to establish a repeatable pattern for easy implementation as it pertains to managing and manipulating dom elements layered on top of playcanvas.

Normally one would create their own html file(s), add references to css files for styling and include a script tag or many depending on the size and scope of your project.

You would the load the html file in your browser and be done with it. Any changes made to the html/css/js simply require a refresh of the browser page you have your html loaded into. Straightforward and fast.

Doing the same in playcanvas requires still the same html, css and js, but the implementation is different and that’s where I am finding difficulty.

It appears that in order to get your html/css/js loaded at runtime, one has to create an "entity’ and place *.js files for each html or css file underneath. Each file requires a “htmlHandler” and “cssHandler” to listen for events? These are required apparently for playcanvas to then create script tags at runtime to load any associated html or css. (There may be other ways and I would be happy to learn more about them.)

I tried to bypass the above and simply add a web component but that also failed to work within playcanvas.

What is the standard way to implement HTML/CSS/JS within playcanvas if someone was to overlay a HTML menu on top of playcanvas and trigger events?

Hello,

I’ve been experimenting with different ways to add UI. Essentially there are two ways that worked for me.

  1. Embed your app into React or other web framework
  • you can create your app inside Playcanvas and just embed url build inside iframe element and send postMessage events.
  • You would create script called something like pcPostMessageListener that listens to messages from UI events and then your 3d app reacts accordinaly.
  1. Create UI with in Playcanvas app
  • if you dont want to use web ui framework or library, you can create hmtl and css files and render on top of playcanvas app it self
  • Essentially what worked for me is I’ve created one main html and css file and created js scripts that work as jsx components and just append them to main div element that lives inside that html file.
  • For events I just use app.fire('some-event')

Creating UI inside playcanvas with traditional html5/css3/JS should be possible.

I’ve noticed that playcanvas just appends all the accompanying JS to the dom at runtime then you can either:

document.body.insertAdjacentHTML("afterbegin",
     `<div id="modal">Your modal here</div>`);

OR

var Modal = pc.createScript('modal');
Modal.attributes.add('css', {type: 'asset', assetType:'css', title: 'CSS Asset'});
Modal.attributes.add('html', {type: 'asset', assetType:'html', title: 'HTML Asset'});

// initialize code called once per entity
Modal.prototype.initialize = function() {
    var style = document.createElement('style');
    document.head.appendChild(style);
    style.innerHTML = this.css.resource || '';

    this.div = document.createElement('div');
    this.div.classList.add('container');
    this.div.innerHTML = this.html.resource || '';

    document.body.appendChild(this.div);
     this.bindEvents();
};

// update code called every frame
Modal.prototype.bindEvents = function() {

        m.addEventListener("click", function() {
            document.getElementById("openModal").style.display = "show";
        }) 
    
   
      this.app.root.findByName('modal').style.display = "show";
      this.app.root.findByName('modal').on('click', function (event) {
        document.getElementById("openModal").style.display = "block";
    }, this);  
};

Though something like the second seems to not work and always throws an error at runtime. The second option would seemingly be ideal but it seems not to work either.

The “app.fire(‘some-event’)” seems closely akin to a connectedCallback() in a web component, where one could then add

this.addEventListener("someEvent", function (e) { //foo });

Actually trying app.fire(“some-event”) results in " Uncaught TypeError: Cannot read properties of undefined (reading ‘fire’)"

I then tried this.app.fire(“some-event”) and the error persists. Could be due to the fact that I’m trying to emit an event from inside a modal prototype?

So, you will have to use pc.app in global namespace because the script doesn’t have access to the app itself Or use browser postMessage API

1 Like

I sorted it out finally… by adding pc.app and that worked. Then I saw your reply and note about the other alternative, thank you!

Looking deeper at the way Playcanvas constructs everything, there’s just a global namespace it seems, so the concept of using IIFE’s to limit scope if needed, isn’t a thing.

1 Like

Glad you figured it out!