在 Web AR 中,我需要为框架中的文本组件设置动画。如何实现aframe中的文字动画?在文本组件中找不到任何属性。
问问题
101 次
1 回答
1
据我所知,没有简单的方法将字母视为单独的实体。它们甚至不是单独的网格——组件生成一个包含所有字母的几何图形。
可能最好创建一个动画模型,甚至使用动画纹理。
但是,使用一点 javascript,我们可以深入底层THREE.js
并将 a 拆分text
为单独的字母。
一种方法是将text
带有单个字母的组件附加到<a-entity>
节点。
声明<a-entity>
节点后,我们可以将动画附加到普通a-frame
实体(尤其是位置/旋转实体)。但随之而来的是定位问题:
<script src="https://aframe.io/releases/1.2.0/aframe.min.js"></script>
<script>
AFRAME.registerComponent("splitter", {
init: function() {
// grab all <a-entity> letter nodes
const letter_nodes = document.querySelectorAll(".letter");
// grab the "text" configuration
const textData = this.el.getAttribute("text");
// create a temporary vector to store the position
const vec = new THREE.Vector3();
for (var i = 0; i < letter_nodes.length; i++) {
// set the "text" component in each letter node
letter_nodes[i].setAttribute('text', {
value: textData.value[i],
anchor: textData.align, // a-text binding
width: textData.width // a-text binding
})
// set position
vec.copy(this.el.getAttribute("position"));
vec.x += i * 0.1; // move the letters to the right
vec.y -= 0.2; // move them down
letter_nodes[i].setAttribute("position", vec)
}
}
})
</script>
<a-scene>
<!-- original text -->
<a-text value="foo" position="0 1.6 -1" splitter></a-text>
<!-- entities -->
<a-entity class="letter" animation="property: position; to: 0 1.2 -1; dur: 1000; dir: alternate; loop: true"></a-entity>
<a-entity class="letter" animation="property: rotation; to: 0 0 360; dur: 1000; dir: alternate; loop: true"></a-entity>
<a-entity class="letter"></a-entity>
<a-sky color="#ECECEC"></a-sky>
</a-scene>
首先 - 我们需要获取原始字母间距,这很草率。据我所知,a-frames 版本THREE.TextGeometry
有一个属性visibleGlyphs
,它具有字形的位置(以及它们的高度和偏移量)。我们可以使用它来正确定位我们的文本。
其次 - 位置动画需要全局位置。最好输入偏移量,而不是目标位置。为了使其工作,文本节点可以是节点的子.letter
节点。
“通用”组件可能如下所示:
<script src="https://aframe.io/releases/1.2.0/aframe.min.js"></script>
<script>
AFRAME.registerComponent("text-splitter", {
init: function() {
const el = this.el;
// i'll use the child nodes as wrapper entities for letter entities
const letter_wrappers = el.children
// wait until the text component tells us that it's ready
this.el.addEventListener("object3dset", function objectset(evt) {
el.removeEventListener("object3dset", objectset); // react only once
const mesh = el.getObject3D("text") // grab the mesh
const geometry = mesh.geometry // grab the text geometry
// wait until the visibleGlyphs are set
const idx = setInterval(evt => {
if (!geometry.visibleGlyphs) return;
clearInterval(idx);
// we want data.height, data.yoffset and position from each glyph
const glyphs = geometry.visibleGlyphs
// do as many loops as there are <entity - glyph> pairs
const iterations = Math.min(letter_wrappers.length, glyphs.length)
const textData = el.getAttribute("text"); // original configuration
var text = textData.value.replace(/\s+/, "") // get rid of spaces
const letter_pos = new THREE.Vector3();
for (var i = 0; i < iterations; i++) {
// use the positions, heights, and offsets of the glyphs
letter_pos.set(glyphs[i].position[0], glyphs[i].position[1], 0);
letter_pos.y += (glyphs[i].data.height + glyphs[i].data.yoffset) / 2;
// convert the letter local position to world
mesh.localToWorld(letter_pos)
// convert the world position to the <a-text> position
el.object3D.worldToLocal(letter_pos)
// apply the text and position to the wrappers
const node = document.createElement("a-entity")
node.setAttribute("position", letter_pos)
node.setAttribute('text', {
value: text[i],
anchor: textData.align, // a-text binding
width: textData.width // a-text binding
})
letter_wrappers[i].appendChild(node)
}
// remove the original text
el.removeAttribute("text")
}, 100)
})
}
})
</script>
<a-scene>
<!-- child entities of the original a-text are used as letter wrappers -->
<a-text value="fo o" position="0 1.6 -1" text-splitter>
<a-entity animation="property: rotation; to: 0 0 360; dur: 1000; loop: true"></a-entity>
<a-entity animation="property: position; to: 0 0.25 0; dur: 500; dir: alternate; loop: true"></a-entity>
<a-entity animation="property: position; to: 0 -0.25 0; dur: 500; dir: alternate; loop: true"></a-entity>
</a-text>
<!-- just to see that the text is aligned properly-->
<a-text value="fo o" position="0 1.6 -1"></a-text>
<a-sky color="#ECECEC"></a-sky>
</a-scene>
这是一个动态添加文本实体的示例 + 使用anime.js 为每个字母设置时间线。
于 2022-01-11T23:13:54.553 回答