jonobr1 / two.js

A renderer agnostic two-dimensional drawing api for the web.
https://two.js.org
MIT License
8.27k stars 454 forks source link

Text `BoundingClientRect` in nested groups #705

Closed dan-fritchman closed 1 year ago

dan-fritchman commented 1 year ago

Fiddle: https://jsfiddle.net/danfritchman/q9sdxzr8/1/

var two = new Two({
  fullscreen: true,
  autostart: true
}).appendTo(document.body);

const g = new Two.Group();
g.position = new Two.Vector(100,100);
const g2 = new Two.Group();
g2.position = new Two.Vector(100,100);
const text = new Two.Text("???");
text.position = new Two.Vector(100, 100);

// Add those into nested groups. `two(g(g2(text)))``
two.add(g);
g.add(g2);
g2.add(text);

// Uncomment *either* of these, and the rect will show up around `text`
/* g.getBoundingClientRect(false); */
/* g2.getBoundingClientRect(false); */

// Now get the text element's bounding box 
const bbox = text.getBoundingClientRect(false);

// And draw it
const rect = new Two.Rectangle(
  (bbox.left + bbox.right)/2,
  (bbox.top + bbox.bottom)/2,
  bbox.width,
  bbox.height,
)
rect.stroke = "red";
rect.fill = "none";
two.add(rect);

Produces this:

image

Uncomment the calls to getBoundingClientRect on either group g or g2 and rect shows up around the boundary of text.
Is that the expected behavior?

jonobr1 commented 1 year ago

Sorry for the delay. This is not ideal behavior, but it is currently accepted. To elaborate:

When you instantiate Two.js with autostart: true, what happens is that an animation loop (an interval function gets set to call multiple times a second) is initiated. This loop starts on the frame after all the code written subsequent to the initialize and in the same scope.

So, in this case the update to render hasn't fired when you call getBoundingClientRect and certain transform values haven't been set yet. You can get around this by ensuring the application is "up-to-date" when you call certain functions:

// ...
g2.add(text);

two.update(); // This forces an update to be called and rendered

const bbox = text.getBoundingClientRect(false);
// ...
dan-fritchman commented 1 year ago

Works here, thanks!