I am trying to achieve effect similar to marquee - line of long (in my case) text which is moved in horizontal axis. I managed to get it work, but I can't call it satisfactory.
My Controller
class looks as below:
@FXML
private Text newsFeedText;
(...)
@Override
public void initialize(URL url, ResourceBundle resourceBundle) {
TranslateTransition transition = TranslateTransitionBuilder.create()
.duration(new Duration(7500))
.node(newsFeedText)
.interpolator(Interpolator.LINEAR)
.cycleCount(Timeline.INDEFINITE)
.build();
GraphicsDevice gd = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();
int width = gd.getDisplayMode().getWidth();
transition.setFromX(width);
transition.setToX(-width);
transition.play();
}
newsFeedText
is binded to some text source which is dynamically updated, so it contains various amount of text.
My code has at least two drawbacks:
- Transition goes from
-width
to+width
;width
is monitor's resolution width
There will be moments when text will not be visible at all if window is not full-screened.
If text will be longer and newsFeedText
width will be greater than monitor's resolution width then transition will disappear "in half" (still being on a screen).
- Currently
Duration
is not dependent on a width ofnewsFeedText
.
Now, it's nothing worng, but if transition's fromX
and toX
were be dynamically calculated then it will result in various speeds of marquee.
How to get rid of these drawbacks?
You should be able to do this by listening to your scene's
widthProperty
. You can either access this vianewsFeedText.getScene().widthProperty()
or get a reference from your main class and expose it from there or pass it to a method or constructor to access within your class that declaresnewsFeedText
.The benefit of this approach is that now your logic is dependent upon the width of your scene (a dynamic dependency) rather than the width of your monitor (a static dependency). Note that I have not tested this approach but at the moment see no reason (perhaps naively) it shouldn't work.
As for your duration dependency, you can solve that by performing some sort of calculation based on the length of the text in
newsFeedText
. Something likeDuration.seconds(newsFeedText.get Text().length()/denominator)
wheredenominator
is some value you specify (such as 7500, as in your code). This will make your duration dynamically computed based on the length of your text.If you want to operate with the width of
newsFeedText
itself, rather than the length of its text, then simply replacenewsFeedText.getText().length()
withnewsFeedText.getWidth()
. Ensure you perform this computation afternewsFeedText
has been laid out so a call to get its width returns the actual width. You can also replace the call with any ofgetPrefWidth()
,getMinWidth()
, orgetMaxWidth()
.I have managed it to work, any recalculations can happen only after transition is stopped so we cannot set its
cycleCount
toTimeline.INDEFINITE
. My requirement was that I could change text inside component so there are fxml wirings:The code which works is:
where
rerunAnimation()
is:and
recalculateTransition()
is: