I like the idea of creating such component; so I had to come up with an implementation, even If I guess you can find some basic implementations on the net.
I wanted to add a way to control the animation to be able to have some basic animations (Left to right / Right to left / Top to bottom / Bottom to top) and also some more complex animation like sinusoidal animation.
I rapidly had a first draft working, doing the wished animation, but it took a few refractor sessions to have some clean code that gives full control over the animation.
Let’s have a look at the code.
First the interface that will be used to control the animation:
public static interface IScrollTextUtils {
int getYpos(float animProgress, int charnum);
int getXpos(float animProgress, int charnum);
double getRotate(float animProgress, final int charnum);
float getTransformedFontSize(float animProgress, int fontSize, final int charnum);
boolean isTimeTolaunchNewTimeline(float animProgress);
void textChanged();
void paintCharacter(Graphics2D g, String s);
int getDuration();
}
It will be used to find out the position of each character, paint the text, the duration of the animation and to determine when to start the next animation.
More than one of the same animation can run at the same time (you want to start the next animation before the end of the current one to always have some text to display).
So to deal with multiple animations at the same time we need a list of something that can handle an animation.
AnimationProgressHandler is used for this purpose.
It contains a timeline and a way to start the next animation when required
public static class AnimationProgressHandler {
private Timeline timeline;
private float animProgress = 0;
private boolean AlreadyStartedNext = false;
private ScrollingText scrolling;
private AnimationProgressHandler(final ScrollingText scrolling) {
this.scrolling = scrolling;
//prepare the timeline
this.timeline = new Timeline(this);
this.timeline.addPropertyToInterpolate("animProgress", 0f, 1f);
this.timeline.setDuration(scrolling.scrollTextUtils.getDuration());
this.timeline.addCallback(createCallBack(this.timeline));
}
public void setAnimProgress(final float animProgress) {
this.animProgress = animProgress;
if (!this.AlreadyStartedNext && this.scrolling.scrollTextUtils.isTimeTolaunchNewTimeline(animProgress)) {
this.AlreadyStartedNext = true;
//start the next animation
this.scrolling.createTimeline().play();
}
this.scrolling.repaint();
}
private TimelineCallback createCallBack(final Timeline parent) {
return new TimelineCallbackAdapter() {
@Override
public void onTimelineStateChanged(final TimelineState oldState, final TimelineState newState,
final float durationFraction, final float timelinePosition) {
if (newState == TimelineState.DONE) {
//when the animation is done
//remove it
AnimationProgressHandler.this.scrolling.animHandlers.remove(this);
}
}
};
}
}
the interesting methods in scrolling text are the paint methods
paintComponent paint the text for every active timeline
protected void paintComponent(final Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g.create();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// paint the text for each running animation
for (int i = 0; i < this.animHandlers.size(); i++) {
paintText(g2, this.animHandlers.get(i).animProgress);
}
g2.dispose();
}
paintText call scrollTextUtils to find the x and y coordinates
check if the coordinates are in the visible area
get and set the rotation/font and then paint each character of the text
protected void paintText(final Graphics2D g2, final float animProgress) {
Font f = getFont();
for (int i = 0; i < this.text.length(); i++) {
int x = this.scrollTextUtils.getXpos(animProgress, i);
if (x < -f.getSize() || x > getWidth()) {
// do not paint, out of the visible bounds
} else {
int y = this.scrollTextUtils.getYpos(animProgress, i);
if (y < 0 || y > getHeight() + f.getSize()) {
// do not paint, out of the visible bounds
} else {
double angle = this.scrollTextUtils.getRotate(animProgress, i);
g2.rotate(angle, x, y);
g2.setFont(f.deriveFont(this.scrollTextUtils.getTransformedFontSize(animProgress, f.getSize(), i)));
g2.translate(x, y);
this.scrollTextUtils.paintCharacter(g2, String.valueOf(this.text.charAt(i)));
g2.translate(-x, -y);
g2.rotate(-angle, x, y);
}
}
}
g2.setFont(f);
}
you can try it here
the full source code is available at https://free-the-pixel.dev.java.net/source/browse/free-the-pixel/trunk/src/com/community/xanadu/components/text/ScrollingText.java