Description
The first time an OpacityEffect (or any HasPaint change) updates a TextComponent, the rendered glyphs teleport upward by one font ascent and stay there. Combined with a movement effect this reads as a sudden vertical jump in the middle of an otherwise smooth animation.
Related to #4013 but a distinct defect: that one is about shadow alpha, this one is about glyph positioning.
Cause
TextComponent.updateBounds translates the freshly formatted element down by its ascent:
@internal
void updateBounds() {
_updateElement();
final measurements = _textElement.metrics;
_textElement.translate(0, measurements.ascent);
size.setValues(measurements.width, measurements.height);
}
but TextComponent.onChanged, which runs on every paint mutation, rebuilds the element without that translation:
@override
void onChanged() {
_textRenderer = _textRenderer.copyWithPaint(paint) as T;
_updateElement();
}
So the element created before the first paint change is ascent-translated, and every element created after a paint change is not, shifting the drawn text up by metrics.ascent for the rest of the component's life.
Steps to reproduce
final text = TextComponent(
text: '+1 kr',
textRenderer: TextPaint(style: const TextStyle(fontSize: 24)),
)
..add(MoveByEffect(Vector2(0, -60), EffectController(duration: 0.8)))
..add(OpacityEffect.fadeOut(EffectController(duration: 0.5, startDelay: 0.3)));
The text rises smoothly for 0.3 seconds, then jumps up by roughly the ascent when the fade begins.
Expected behavior
Paint changes only affect paint; the glyphs stay where they are.
Suggested fix
Have onChanged apply the same ascent translation as updateBounds (or call a shared helper), for example:
@override
void onChanged() {
_textRenderer = _textRenderer.copyWithPaint(paint) as T;
_updateElement();
_textElement.translate(0, _textElement.metrics.ascent);
}
Observed on the perf/component-set-backing branch; the implementation is the same on main.
Description
The first time an
OpacityEffect(or anyHasPaintchange) updates aTextComponent, the rendered glyphs teleport upward by one font ascent and stay there. Combined with a movement effect this reads as a sudden vertical jump in the middle of an otherwise smooth animation.Related to #4013 but a distinct defect: that one is about shadow alpha, this one is about glyph positioning.
Cause
TextComponent.updateBoundstranslates the freshly formatted element down by its ascent:but
TextComponent.onChanged, which runs on every paint mutation, rebuilds the element without that translation:So the element created before the first paint change is ascent-translated, and every element created after a paint change is not, shifting the drawn text up by
metrics.ascentfor the rest of the component's life.Steps to reproduce
The text rises smoothly for 0.3 seconds, then jumps up by roughly the ascent when the fade begins.
Expected behavior
Paint changes only affect paint; the glyphs stay where they are.
Suggested fix
Have
onChangedapply the same ascent translation asupdateBounds(or call a shared helper), for example:Observed on the
perf/component-set-backingbranch; the implementation is the same onmain.