AFrame: keeping an element's world position and rotation intact while reparenting

I am attempting to reassign a child element (entity) to another parent while preserving its position, rotation, and possibly size in the scene. Ideally, I would like to implement a component (let's call it "reparent") that can be added to an entity to "move" it to a specified parent while maintaining its appearance in the scene. For example, in the following HTML code, the green entity should become a child of the red one without altering its position and rotation:

  <a-scene id="scene" background="color: grey">
      <a-entity id="red"
        position="-1 3.5 -4" rotation="30 0 0" material="color: red"
        geometry="primitive: cylinder"></a-entity>
        
      <a-entity id="green" reparent="parent: #red"
        position="-3 2 -4" rotation="0 45 0" material="color: green"
        geometry="primitive: box"></a-entity>
  </a-scene>

While this functionality seems achievable using attach at the Three.js level, my attempts to create a simple AFrame component for it have been unsuccessful. It seems that the reparenting done in HTML through AFrame is causing issues.

To illustrate the problem, I have created a test component:

AFRAME.registerComponent('reparent', {
  schema: {
    parent: {type: 'selector', default: null},
  },
  init: function () {
    const el = this.el;
    const parent = this.data.parent;

    move = function(evt) {
      parent.object3D.attach(el.object3D);
      console.log(el.object3D.parent.el.id, el.parentElement.id);
//      parent.appendChild(el);
//      console.log(el.object3D.parent.el.id, el.parentElement.id);
    };
    el.sceneEl.addEventListener('loaded', move);
  }
});

Upon execution, the result is red scene: the object3D's parent has changed, but the element's parent remains the scene in HTML. The green box appears as intended at the specified coordinates with the correct rotation.

If I uncomment the two lines of code, a second line red red is displayed, but the green box disappears. This means that although the element is successfully reparented in HTML, its object3D properties are somehow affected.

Any insights on why this approach fails or any alternative ideas for implementing such a component?

All testing has been performed on AFrame version 1.1.0.

Answer №1

After receiving advice against reusing the same element for reparenting, I decided to take a different approach by copying the entity to be reparented into a new element under the new parent. This workaround may not be perfect, but it suits my current needs. Here is the component I created:

AFRAME.registerComponent('reparent', {
  schema: {
    parent: {type: 'selector', default: null},
  },
  update: function () {
    const el = this.el;
    const parent = this.data.parent;

    if (el.parentElement == parent) {
      // No action needed if already a child of the intended parent
      return;
    };
    
    // Reparent once object3D is ready
    reparent = function() {
      // Attach object3D to the new parent to inherit position, rotation, scale
      parent.object3D.attach(el.object3D);
      let position = el.object3D.position;
      let rotation = el.object3D.rotation;
      let scale = el.object3D.scale;

      // Create a new element and copy attributes from the original one
      let newEl = document.createElement(el.tagName);
      if (el.hasAttributes()) {
        let attrs = el.attributes;
        for(var i = attrs.length - 1; i >= 0; i--) {
          let attrName = attrs[i].name;
          let attrVal = el.getAttribute(attrName);
          newEl.setAttribute(attrName, attrVal);
        };
      };

      // Set location, rotation, scale when new element loads
      relocate = function() {
        newEl.object3D.location = location;
        newEl.object3D.rotation = rotation;
        newEl.object3D.scale = scale;
      };
      
      newEl.addEventListener('loaded', relocate, {'once': true});
      
      // Attach the new element and remove the old one
      parent.appendChild(newEl);
      el.parentElement.removeChild(el);
    };
    
    if (el.getObject3D('mesh')) {
      reparent();
    } else {
      el.sceneEl.addEventListener('object3dset', reparent, {'once': true});
    };
  }
});

To test this component, use the provided HTML file:

<!DOCTYPE html>
<html>
  <head>
    <script src="https://aframe.io/releases/1.1.0/aframe.min.js"></script>
    <script src="reparent.js"></script>
  </head>
  <body>
    <a-scene id="scene" background="color: grey">
      <a-entity id="red"
        position="1 2 -4" rotation="30 0 0" material="color: red"
        geometry="primitive: cylinder"></a-entity>
        
      <a-entity id="green" reparent="parent: #red"
        position="-1 0 -4" rotation="0 45 0" material="color: green"
        geometry="primitive: box"></a-entity>

      <a-entity id="wf"
        position="-1 0 -4" rotation="0 45 0" material="wireframe: true"
        geometry="primitive: box"></a-entity>
    </a-scene>
  </body>
</html>

The wireframe entity demonstrates how the green box remains in its original position after being "reparented" under the red entity. This behavior is achieved by cloning the original box into a new element.

The component allows for dynamic reparenting by changing the parent property as needed.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Displaying subsets of data based on the identifier of the primary array

In my JSON file, I have an array containing various categories and their respective subcategories. { "Women": [ { "id" : 1, "name" : "See All", &q ...

Display endless data within a set window size

I am looking to create a fixed-size window for displaying text from the component <Message/>. If the text is longer, I want it to be scrollable within the fixed window size. See screenshot below: Screenshot export default function AllMessages(){ ...

When a form input is submitted, the webpage will refresh with a new

I have a form embedded on my WordPress page. I want to identify users without referrers so that I can set the referrer for them automatically (the referrer part is handled by a plugin). The registration form URL looks like this: The code provided below w ...

Utilizing JavaScript to handle the value from a selected form

My form is quite simple: <form id="signup"> <div class="form-group"> <input type="text" name="email" placeholder="Email" id="email" required> </div> <div class="form-group"> <input type= ...

How can we efficiently execute text searches on a large scale by utilizing static index files that are easily accessible online

Looking for a lightweight, yet scalable solution for implementing a full text search index in JavaScript using static files accessible via HTTP? Seeking to make about 100k documents searchable online without breaking the bank on hosting costs like Elastics ...

Leveraging React to efficiently connect with friends on Firebase Realtime Database, enhancing the capability to include multiple connections

Currently, I am working on a project that involves React and Firebase's real-time database to create a friend network feature. The main challenge I'm facing is when a user enters an email into an input field. Upon submission, the system takes a s ...

Looking to extract, interpret, and display PDF documents using React?

I'm working on displaying a PDF file from an external URL. My goal is to extract text from the PDF for analysis, and then present a specific section of a page to users in my React application. This will involve cropping the content using coordinates o ...

Screenshots of playwright failing to work on Github.''

Within my playwright.config.ts file, I have specified the following: use: { ... screenshot: 'only-on-failure', } When running tests locally and they fail, screenshots are successfully saved in /test-results. However, the issue arises when th ...

Guide on how to modify the color of a single row within a table with just a click

My table structure is as follows: <table> <tr> <td>A1</td> <td>A2</td> <td>A3</td> <td>A4</td> </tr> <tr> ...

Google Tag Manager experiencing issues with retrieving dataLayer variable, showing as undefined

I'm attempting to establish a dataLayer variable in order to push the product name into the event label. Here is the dataLayer push that occurs when a user adds a product to their cart: { event: "addToCart", gtm: { uniqueEventId: 10 ...

Explore our array of images displayed in an interactive gallery featuring clickable

I have a query I'm facing an issue with my code. Currently, I have a gallery that displays images perfectly. Now, I want to enhance it by showing a larger resolution of the image when clicked. However, when I add the href tag to link the images, they ...

Implementing Dynamic CSS Styles in AngularJS

Essentially, I am facing a challenge on my page where a control needs to toggle two different elements with two distinct CSS classes upon being clicked. While I have successfully managed to toggle one of the controls, the other remains unchanged. Here is ...

Mastering the art of implementing dynamic template variables in TinyMCE

After exploring the 'full' example and conducting research on the Wiki and moxie forums, I have yet to find a solution. I am attempting to implement what the wiki states is possible, but encountered an issue when replacing the 'staffid' ...

Updating the background of a div in HTML through the power of JavaScript

I am having trouble changing the background of a DIV. I believe my code is correct, but it doesn't seem to be working. I suspect the error lies with the URL parameter, as the function works without it. Here is my code: <script language="JavaS ...

Strange behavior of Lambda function in Typescript

Within a larger class, I'm working with the following code snippet: array.map(seq => this.mFunction(seq)); After compiling using the tsc command, it becomes: array.map(function (seq) { return _this.mFunction(seq); }); Everything seems fine so f ...

How do I preserve data within $scope upon switching views using ng-include?

Can you please take a look at this jsFiddle? http://jsfiddle.net/mystikacid/b7hqcdfk/4/ This is the template code: <div ng-app="myApp"> <div ng-controller="dataCtrl"> <div>Data : {{data}} (Value outside views)</div> < ...

react tried to recycle markup error

Working on server-side rendering with React, encountering an error mentioned below. Here is the context: I have 2 React components, each stored in its own separate folder: - react-components - component-1-folder - component-1 - component-2-fo ...

Error in the syntax containing ?callback=jQuery1113

I'm attempting to initiate a simple JSONP query: <html> <head> <script type="text/javascript" src="js/jquery1.js"></script> <script> $(document).ready(function(){ $.ajax({ ...

Safari compatible JavaScript needed for enforcing required fields in HTML5

I have been using the following script to adjust the HTML5 required attribute of my input elements. However, I am now looking for a way to tweak this script so that it can also function properly in Safari browsers, where support for this attribute may be l ...

Tips for adjusting div content to fit a fixed height on all devices

Looking to adjust the size of the #left-content div based on the height of the window, while ensuring that all content is visible within the fixed #left-bg. However, when viewing on mobile or tablet devices, the content appears hidden. .left-bg{ backgro ...