aframe
aframe copied to clipboard
Ability to autosize text container
It would be great to have a way to render a text on a background with some padding around it.
I’ve tried an example from docs, however, it seems that auto width mode actually sets the maximum width of the container, leaving the gap on the right:

What I would like to achieve is an ability to render a label with some background around it (similar to Bootstrap labels for example)
Here is a hacky solution that I came up with:
import AFRAME from 'aframe';
const fontWidthFactor = 1000;
AFRAME.registerComponent('text-auto-width', {
dependencies: ['text'],
schema: {
padding: {default: 0}
},
init() {
// Hook into text component's updateLayout method
const textComponent = this.el.components.text;
const textMesh = this.el.object3D.children[1];
if (textMesh.geometry.layout) {
resize(textMesh.geometry.layout);
} else {
const origUpdate = textComponent.updateLayout;
textComponent.updateLayout = (data) => {
origUpdate.call(textComponent, data);
resize(textMesh.geometry.layout);
};
}
const resize = (layout) => {
const {value, width} = this.el.getAttribute('text');
const metrics = layout.computeMetrics(value, 0, value.length, Math.INT_MAX);
const actualWidth = metrics.width * width / fontWidthFactor + this.data.padding * 2;
const geometry = this.el.getAttribute('geometry');
this.el.setAttribute('geometry', {...geometry, width: actualWidth});
}
}
});
text="align: center"
Also https://github.com/mayognaise/aframe-html-shader/pull/12 is an option for rendering HTML/CSS as a texture. Slightly expensive to compute, but perhaps cheaper than pulling in and rendering glyphs?
@ngokevin align: center
has nothing to do with auto sizing, it will render container with the same width
I suppose text.padding
wouldn't be too bad.
The text auto width was intended to size the text width to the geometry, not the geometry to the text width - you can't automatically size both height and width, you need to pick something as a FRAME of reference. Is it a more common case to want arbitrary width based upon fixed height, rather than arbitrary height (including line wrap) based upon fixed width? If so, perhaps we should add some switches for that (whether it's padding, or something else)
+1 for text padding
@eng1neer did you end up finding a solution to this? I tried using the code you posted but I get "TypeError: textMesh is undefined"
How would you go about dynamically adjusting the height of the background geometry (a plane in my case) to fit multi-line text when wrapCount
and width
are fixed? The height
and lineHeight
properties are both at 0 for some reason so I can't apply a formula that scales based on text height. height: 0
seems to work when the component is attached but I need the height to update when the text lengthens.
@eng1neer I wrote a Component which works well to add padding. I have added an example HTML code (including the fiddle) which demonstrates the usage of the component.
Component:
AFRAME.registerComponent("planepadder", {
schema: {
addPadding: {type: "boolean", default: false},
padding: {type: "number", default: 0.01}
},
init: function(){
let data = this.data;
this.el.setAttribute("planepadder", "addPadding: false");
},
update: function (oldData) {
let data = this.data;
let el = this.el;
if(Object.keys(data).length === 0) { return; }
/**
* If the flag is true add padding to the plane
*/
if(data.addPadding === true) {
/**
* Set padding using the provided padding value
*/
el.getObject3D("mesh").geometry = new THREE.PlaneGeometry(
el.components.geometry.data.width + el.components.planepadder.data.padding,
el.components.geometry.data.height + el.components.planepadder.data.padding
);
/**
* Remove planepadder attribute so that it padding can be
* changed in the future by adding the attribute again
*/
this.el.removeAttribute("planepadder");
}
},
tick(time, delta) {
let data = this.data;
let el = this.el;
/**
* Check if the width is not NaN, padding hasn't already been
* added i.e `addPadding` schema attribute is false and that the
* plane's width is same as the text's width which means padding
* hasn't been added yet
*/
if(el.components.geometry.data.width != NaN && el.components.planepadder.data.addPadding === false && el.components.geometry.data.width == el.components.text.data.width) {
// Set the schema attribute `addPadding` to true so that the padding can be added
el.setAttribute("planepadder", "addPadding: true");
}
}
});
Usage example:
<a-entity
id="textEntity"
geometry="primitive: plane; width: auto; height: auto"
planepadder="padding: 0.02"
material="color: skyblue;"
text="
width: 0.1;
value: Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry;
color: black;"
position="0 0.02 -0.02">
</a-entity>
<a-camera camera position="0 0.02 0.05" wasd-controls="acceleration: 5" />
Here is the Fiddle