AFrame:重新设置元素保持其世界位置,旋转

Posted

技术标签:

【中文标题】AFrame:重新设置元素保持其世界位置,旋转【英文标题】:AFrame: reparenting an element keeping its world position, rotation 【发布时间】:2021-01-02 12:07:24 【问题描述】:

我正在尝试重新设置元素(实体)的父级,在场景中保持其位置、旋转(如果可能,还包括大小,例如缩放)。理想情况下,我希望有一个组件(例如“reparent”),当在实体上设置时,将其“移动”到指定的父级,从而将实体的方面保持在场景中。例如,对于下一个 html 代码,绿色实体将成为红色实体的子实体,但会保持其在场景中的位置和旋转:

  <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>

显然,这可以使用attach 在三个级别上完成,但是当我尝试使用它编写一个简单的组件时,它不起作用,显然是由于 HTML 的重新父级所做的(我假设,因为 AFrame)。

我已经写了他的测试组件来显示问题:

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);
  
);

运行时,结果为red scene:object3D 的父级已更改,但在 HTML 级别元素的父级仍然是场景。绿色框按预期显示(在“-3 2 -4”、世界坐标和正确的旋转中)。

如果我取消注释这两行注释,我会得到第二行 red red,但绿色框会消失。换句话说,现在在 HTML 中,元素按预期重新设置了父级,但不知何故,它的 object3D 不再工作了。

知道为什么会失败,或者对这样的组件有其他想法吗?

所有这些都使用 AFrame 1.1.0。

【问题讨论】:

【参考方案1】:

在得到重复使用相同元素(实体)进行重新父代不是一个好主意的建议后(请参阅Change parent of component and keep position),我探索了将要重新设置的实体复制到新父级下的新元素中的想法,并且然后删除“旧”的(而不是直接重新设置实体)。诚然,它不是世界上最干净的 hack,但我希望它对我的用例有所帮助。于是,我写了一个组件:

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

    if (el.parentElement == parent) 
      // We're already a child of the intended parent, do nothing
      return;
    ;
    // Reparent, once object3D is ready
    reparent = function() 
      // Attach the object3D to the new parent, to get position, rotation, scale
      parent.object3D.attach(el.object3D);
      let position = el.object3D.position;
      let rotation = el.object3D.rotation;
      let scale = el.object3D.scale;

      // Create new element, copy the current one on it
      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);
        ;
      ;
      // Listener for location, rotation,... when the new el is laded
      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 this one
      parent.appendChild(newEl);
      el.parentElement.removeChild(el);
    ;
    if (el.getObject3D('mesh')) 
      reparent();
     else 
      el.sceneEl.addEventListener('object3dset', reparent, 'once': true);
    ;
  
);

您可以检查它是否适用于以下 HTML 文件:

<!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>

线框实体只是为了显示绿色框如何保持在同一位置,尽管它在红色实体下被“重新设置”(探索 DOM 以检查)。如前所述,绿色框其实是一个新的,是从原来的那个克隆出来的,然后被组件删除了。

当其parent 属性发生更改时,组件应该可以工作,允许在需要时动态重新设置父级。

【讨论】:

你探索过 superhands 组件是如何实现抓取功能的吗? 没有。我没有用这个来抓。不过我还是会看看的,谢谢!

以上是关于AFrame:重新设置元素保持其世界位置,旋转的主要内容,如果未能解决你的问题,请参考以下文章

aframe动态添加元素位置是错误的

Plotly React 在重新渲染之间保持缩放和旋转

旋转元素到点击位置

AFRAME文本几何组件从中心旋转?

旋转时固定位置

当 iPad 旋转时,UIPopovercontroller 不会保持在其位置