如何让 A-Frame 组件相互通信?
How to make A-Frame components talk to each other?
我想要一些组件响应用户在场景中的位置和方向。我对交互式a-frame场景的经验很少,也没有自己写过组件。
通常,我希望组件能够为其他组件提供回调以供调用,或者如果这不可能,则进行某种组件间数据切换。 “接收”组件将更改其内容(子项)、外观 and/or 行为。
如果我们举一个非常简单的例子,假设我希望场景在用户位于 x>0 时包含一个框,或者在用户位于 x<=0 时包含一个球体。
打破这个,我很乐意了解如何......:
- 读取用户位置并提供给其他人。我找到了how to read the position;我想我可以只使用
<a-scene>
元素并设置一些属性,例如 user-position="1 2 3"
.
- 在某个地方写一些代码,当这个位置改变时运行一个函数(我会去抖动它,我想)并对场景进行更改。我认为如果我编写自己的组件来包含整个场景,我需要......:
- 将用户位置设置为该元素的属性;
- 定义一个
update
方法;
- 在
update
方法中,比较当前与以前的用户位置。
...但我想知道这是否有点矫枉过正,我可以以某种方式连接到场景中,或者完全是其他东西。
如果我采用上面提到的方法,我想缺少的部分是如何“声明”要渲染的内容?例如,使用 ReactJS 我会做 return x > 0 ? <a-box/> : <a-sphere/>;
。是否有等效项,或者我是否需要进入 DOM 并手动 add/remove <a-box>
子项等?
谢谢!
编辑: 我的 box/sphere 可以正常工作(glitch),但感觉很奇怪,很想改进它。
如何让A-Frame组件相互通信?
0。 setAttribute
您可以使用
更改任何组件中的任何 属性
element.setAttribute("component_name", "value");
但我假设您想要的不仅仅是对 update
呼叫做出反应。比组件 schema
更灵活,每秒使用 60 次时性能更高/
1.事件
- 组件 1 发出一个事件
- 组件 2 - x 侦听事件,并做出相应反应。
不依赖于硬编码的组件名称,您可以轻松拥有多个收件人,并且可能稳定 API:
<script src="https://aframe.io/releases/1.2.0/aframe.min.js"></script>
<script>
AFRAME.registerComponent("position-reader", {
tick: function() {
// read the position and broadcast it around
const pos = this.el.object3D.position;
const positionString = "x: " + pos.x.toFixed(2) +
", z: " + pos.z.toFixed(2)
this.el.emit("position-update", {text: positionString})
}
})
AFRAME.registerComponent("position-renderer", {
init: function() {
const textEl = document.querySelector("a-text");
this.el.addEventListener("position-update", (evt) => {
textEl.setAttribute("value", evt.detail.text);
})
}
})
</script>
<a-scene>
<a-box position="-1 0.5 -3" rotation="0 45 0" color="#4CC3D9"></a-box>
<a-sphere position="0 1.25 -5" radius="1.25" color="#EF2D5E"></a-sphere>
<a-cylinder position="1 0.75 -3" radius="0.5" height="1.5" color="#FFC65D"></a-cylinder>
<a-plane position="0 0 -4" rotation="-90 0 0" width="4" height="4" color="#7BC8A4"></a-plane>
<a-camera position-renderer position-reader>
<a-text position="-0.5 0 -0.75" color="black" value="test"></a-text>
</a-camera>
</a-scene>
2。直接
从字面上看,您可以使用
获取组件“对象”引用
entity.components["componentName"]
并调用其函数:
entity.components["componentName"].function();
例如 - 一个组件获取当前位置,并告诉另一个组件打印它:
<script src="https://aframe.io/releases/1.2.0/aframe.min.js"></script>
<script>
AFRAME.registerComponent("position-reader", {
init: function() {
// wait until the entity is loaded and grab the other component reference
this.el.addEventListener("loaded", evt => {
this.rendererComp = this.el.components["position-renderer"];
})
},
tick: function() {
if (!this.rendererComp) return;
// read the position and call 'updateText' in the 'position-renderer'
const pos = this.el.object3D.position;
const positionString = "x: " + pos.x.toFixed(2) +
", z: " + pos.z.toFixed(2)
this.rendererComp.updateText(positionString)
}
})
AFRAME.registerComponent("position-renderer", {
init: function() {
this.textEl = document.querySelector("a-text");
},
updateText: function(string) {
this.textEl.setAttribute("value", string);
}
})
</script>
<a-scene>
<a-box position="-1 0.5 -3" rotation="0 45 0" color="#4CC3D9"></a-box>
<a-sphere position="0 1.25 -5" radius="1.25" color="#EF2D5E"></a-sphere>
<a-cylinder position="1 0.75 -3" radius="0.5" height="1.5" color="#FFC65D"></a-cylinder>
<a-plane position="0 0 -4" rotation="-90 0 0" width="4" height="4" color="#7BC8A4"></a-plane>
<a-camera position-renderer position-reader>
<a-text position="-0.5 0 -0.75" color="black" value="test"></a-text>
</a-camera>
</a-scene>
在你的情况下,我会检查位置,并管理一个组件中的元素。或者用一个来判断 position.x > 0 || < 0,另一个用于可见性更改。
<script src="https://aframe.io/releases/1.2.0/aframe.min.js"></script>
<script>
AFRAME.registerComponent("position-check", {
schema: {
z: {default: 0}
},
tick: function() {
const pos = this.el.object3D.position;
// check if we're 'inside', or outside
if (pos.z >= this.data.z) {
// emit an event only once per occurence
if (!this.inside) this.el.emit("got-inside");
this.inside = true
} else {
// emit an event only once per occurence
if (this.inside) this.el.emit("got-outside");
this.inside = false
}
}
})
AFRAME.registerComponent("manager", {
init: function() {
const box = this.el.querySelector("a-box");
const sphere = this.el.querySelector("a-sphere")
//react to the changes
this.el.sceneEl.camera.el.addEventListener("got-inside", e => {
box.setAttribute("visible", true);
sphere.setAttribute("visible", false);
})
this.el.sceneEl.camera.el.addEventListener("got-outside", e => {
box.setAttribute("visible", false);
sphere.setAttribute("visible", true);
})
}
})
</script>
<a-scene>
<a-box position="-1 0.5 -3" rotation="0 45 0" color="#4CC3D9"></a-box>
<a-sphere position="0 1.25 -5" radius="1.25" color="#EF2D5E"></a-sphere>
<a-cylinder position="1 0.75 -3" radius="0.5" height="1.5" color="#FFC65D"></a-cylinder>
<a-plane position="0 0 -4" rotation="-90 0 0" width="4" height="4" color="#7BC8A4"></a-plane>
<a-entity manager>
<a-box position="0 1 -3" visible="false"></a-box>
<a-sphere position="0 1 -3" visible="false"></a-sphere>
</a-entity>
<a-camera position-check="z: 0"></a-camera>
</a-scene>
我想要一些组件响应用户在场景中的位置和方向。我对交互式a-frame场景的经验很少,也没有自己写过组件。
通常,我希望组件能够为其他组件提供回调以供调用,或者如果这不可能,则进行某种组件间数据切换。 “接收”组件将更改其内容(子项)、外观 and/or 行为。
如果我们举一个非常简单的例子,假设我希望场景在用户位于 x>0 时包含一个框,或者在用户位于 x<=0 时包含一个球体。
打破这个,我很乐意了解如何......:
- 读取用户位置并提供给其他人。我找到了how to read the position;我想我可以只使用
<a-scene>
元素并设置一些属性,例如user-position="1 2 3"
. - 在某个地方写一些代码,当这个位置改变时运行一个函数(我会去抖动它,我想)并对场景进行更改。我认为如果我编写自己的组件来包含整个场景,我需要......:
- 将用户位置设置为该元素的属性;
- 定义一个
update
方法; - 在
update
方法中,比较当前与以前的用户位置。
...但我想知道这是否有点矫枉过正,我可以以某种方式连接到场景中,或者完全是其他东西。
如果我采用上面提到的方法,我想缺少的部分是如何“声明”要渲染的内容?例如,使用 ReactJS 我会做 return x > 0 ? <a-box/> : <a-sphere/>;
。是否有等效项,或者我是否需要进入 DOM 并手动 add/remove <a-box>
子项等?
谢谢!
编辑: 我的 box/sphere 可以正常工作(glitch),但感觉很奇怪,很想改进它。
如何让A-Frame组件相互通信?
0。 setAttribute
您可以使用
更改任何组件中的任何 属性element.setAttribute("component_name", "value");
但我假设您想要的不仅仅是对 update
呼叫做出反应。比组件 schema
更灵活,每秒使用 60 次时性能更高/
1.事件
- 组件 1 发出一个事件
- 组件 2 - x 侦听事件,并做出相应反应。
不依赖于硬编码的组件名称,您可以轻松拥有多个收件人,并且可能稳定 API:
<script src="https://aframe.io/releases/1.2.0/aframe.min.js"></script>
<script>
AFRAME.registerComponent("position-reader", {
tick: function() {
// read the position and broadcast it around
const pos = this.el.object3D.position;
const positionString = "x: " + pos.x.toFixed(2) +
", z: " + pos.z.toFixed(2)
this.el.emit("position-update", {text: positionString})
}
})
AFRAME.registerComponent("position-renderer", {
init: function() {
const textEl = document.querySelector("a-text");
this.el.addEventListener("position-update", (evt) => {
textEl.setAttribute("value", evt.detail.text);
})
}
})
</script>
<a-scene>
<a-box position="-1 0.5 -3" rotation="0 45 0" color="#4CC3D9"></a-box>
<a-sphere position="0 1.25 -5" radius="1.25" color="#EF2D5E"></a-sphere>
<a-cylinder position="1 0.75 -3" radius="0.5" height="1.5" color="#FFC65D"></a-cylinder>
<a-plane position="0 0 -4" rotation="-90 0 0" width="4" height="4" color="#7BC8A4"></a-plane>
<a-camera position-renderer position-reader>
<a-text position="-0.5 0 -0.75" color="black" value="test"></a-text>
</a-camera>
</a-scene>
2。直接
从字面上看,您可以使用
获取组件“对象”引用entity.components["componentName"]
并调用其函数:
entity.components["componentName"].function();
例如 - 一个组件获取当前位置,并告诉另一个组件打印它:
<script src="https://aframe.io/releases/1.2.0/aframe.min.js"></script>
<script>
AFRAME.registerComponent("position-reader", {
init: function() {
// wait until the entity is loaded and grab the other component reference
this.el.addEventListener("loaded", evt => {
this.rendererComp = this.el.components["position-renderer"];
})
},
tick: function() {
if (!this.rendererComp) return;
// read the position and call 'updateText' in the 'position-renderer'
const pos = this.el.object3D.position;
const positionString = "x: " + pos.x.toFixed(2) +
", z: " + pos.z.toFixed(2)
this.rendererComp.updateText(positionString)
}
})
AFRAME.registerComponent("position-renderer", {
init: function() {
this.textEl = document.querySelector("a-text");
},
updateText: function(string) {
this.textEl.setAttribute("value", string);
}
})
</script>
<a-scene>
<a-box position="-1 0.5 -3" rotation="0 45 0" color="#4CC3D9"></a-box>
<a-sphere position="0 1.25 -5" radius="1.25" color="#EF2D5E"></a-sphere>
<a-cylinder position="1 0.75 -3" radius="0.5" height="1.5" color="#FFC65D"></a-cylinder>
<a-plane position="0 0 -4" rotation="-90 0 0" width="4" height="4" color="#7BC8A4"></a-plane>
<a-camera position-renderer position-reader>
<a-text position="-0.5 0 -0.75" color="black" value="test"></a-text>
</a-camera>
</a-scene>
在你的情况下,我会检查位置,并管理一个组件中的元素。或者用一个来判断 position.x > 0 || < 0,另一个用于可见性更改。
<script src="https://aframe.io/releases/1.2.0/aframe.min.js"></script>
<script>
AFRAME.registerComponent("position-check", {
schema: {
z: {default: 0}
},
tick: function() {
const pos = this.el.object3D.position;
// check if we're 'inside', or outside
if (pos.z >= this.data.z) {
// emit an event only once per occurence
if (!this.inside) this.el.emit("got-inside");
this.inside = true
} else {
// emit an event only once per occurence
if (this.inside) this.el.emit("got-outside");
this.inside = false
}
}
})
AFRAME.registerComponent("manager", {
init: function() {
const box = this.el.querySelector("a-box");
const sphere = this.el.querySelector("a-sphere")
//react to the changes
this.el.sceneEl.camera.el.addEventListener("got-inside", e => {
box.setAttribute("visible", true);
sphere.setAttribute("visible", false);
})
this.el.sceneEl.camera.el.addEventListener("got-outside", e => {
box.setAttribute("visible", false);
sphere.setAttribute("visible", true);
})
}
})
</script>
<a-scene>
<a-box position="-1 0.5 -3" rotation="0 45 0" color="#4CC3D9"></a-box>
<a-sphere position="0 1.25 -5" radius="1.25" color="#EF2D5E"></a-sphere>
<a-cylinder position="1 0.75 -3" radius="0.5" height="1.5" color="#FFC65D"></a-cylinder>
<a-plane position="0 0 -4" rotation="-90 0 0" width="4" height="4" color="#7BC8A4"></a-plane>
<a-entity manager>
<a-box position="0 1 -3" visible="false"></a-box>
<a-sphere position="0 1 -3" visible="false"></a-sphere>
</a-entity>
<a-camera position-check="z: 0"></a-camera>
</a-scene>