javascript - AFrame:重新设置元素保持其世界位置,旋转
问题描述
我正在尝试重新设置一个元素(实体),在场景中保持它的位置、旋转(如果可能的话,保持大小,例如比例)。理想情况下,我希望有一个组件(例如“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。
解决方案
在得到关于重用相同元素(实体)进行重设不是一个好主意的建议后(请参阅更改组件的父级并保持位置),我探索了将要重设的实体复制到新父级下的新元素中的想法,然后删除“旧”的(而不是直接重新设置实体)。诚然,它不是世界上最干净的 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
属性更改时应该工作,允许在需要时动态重新设置父级。
推荐阅读
- python - 将 DataFrame 保存为 .csv,列数据类型:对象(列表)。加载 DataFrame 但列数据类型是对象(str),我错过了什么?
- c# - OpenGL 3.3 中仍然需要 glEnableClientState - OpenGL.NET?
- reactjs - NextJS/CookieBot:带有 data-blockingmode="auto" 的 CookieBot 脚本阻止加载动态生成的脚本
- javascript - window.setInterval 在实现承诺后冻结网页
- python - .astype() 不适用于整个数据帧,但适用于 for 循环中的列
- flutter - 自动浏览颤动的屏幕
- amazon-web-services - 如何在没有 Amplify 的情况下使用 Amazon Cognito
- shell - 使用自动密码在另一台服务器上运行远程 hive 命令失败
- java - setEnabled(false) 时显示 TextInputEditText 错误
- mysql - 无法将 mysql docker 连接到 Drupal docker