While pouring over bug reports today I ran across an older one, RT-4829. Essentially what the bug submitter wanted was the ability to animate text in a Text node or label. So it starts out with no characters and then a character at a time is added to the node. The sample code posted in the bug report was pretty cool, it looked a lot like one of the text bubbles you’d see in Dragon Warrior or some other old school RPG. It occurred to me that this is fertile ground for an aspiring JavaFX library developer 🙂
[jfx]
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.effect.DropShadow;
import javafx.scene.text.Text;
import javafx.scene.text.TextOrigin;
import javafx.scene.text.Font;
import javafx.scene.shape.Rectangle;
import javafx.scene.paint.Color;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
def BASE_WIDTH = 800;
def BASE_HEIGHT = 600;
def MARGIN = 20;
var backgroundRect = Rectangle
{
x: 0
y: 0
width: BASE_WIDTH
height: BASE_HEIGHT
onMouseClicked: function(event) { t.playFromStart() }
}
var boundingRect = Rectangle
{
x: MARGIN * 2
y: BASE_HEIGHT * 2 / 3 – MARGIN
width: BASE_WIDTH – MARGIN * 4
height: BASE_HEIGHT – (BASE_HEIGHT * 2 / 3)
fill: Color.rgb(255, 255, 255, 0.2)
stroke: Color.WHITE
strokeWidth: 2
}
var displayedText = Text
{
x: boundingRect.x + MARGIN
y: boundingRect.y + MARGIN
wrappingWidth: boundingRect.width – MARGIN * 2
content: ""
font: Font { size: 36 }
textOrigin: TextOrigin.TOP
//stroke: Color.BLACK
fill: Color.WHITE
effect: DropShadow {}
}
def FINAL_TEXT = "I was trying to get the text to wrap, but the text layout engine would keep putting a character on the previous line until the full word was visible.";
var charCount = 0 on replace
{
var textToDisplay = FINAL_TEXT.substring(0, charCount);
displayedText.content = textToDisplay;
}
Stage
{
title: "Text demo"
visible: true
scene: Scene
{
width: BASE_WIDTH
height: BASE_HEIGHT
fill: Color.BLACK
content:
[
backgroundRect,
boundingRect,
displayedText
]
}
}
var t = Timeline
{
keyFrames:
[
// In reality the speed would be per letter so the times would
// be computed dynamically. I have an idea of how to do that.
// Initial experiments also suggest that the speed of displaying
// each letter should be relative to the width of the letter.
KeyFrame { time: 0s, values: charCount => 0 }
KeyFrame { time: 3s, values: charCount => FINAL_TEXT.length() }
]
};
t.play();
[/jfx]
I think the actual feature the developer was asking for (the ability to say, KeyFrame { time: 3s, values: text => “This is the text to transition to” }) wasn’t the right way to go about the problem. Rather, there should be a handful of Transition class implementations for handling special text transition effects. So that you would be able to write something like:
[jfx]
def transition = TypewriterTextTransition {
rate: .5s
node: someTextNodeOrLabelOrSomething
toValue: "This is the text I’d like to transition to"
}
[/jfx]
And then you start the transition and voilà! It encapsulates all the nasty details and provides useful knobs for various types of effects you may want (for example, in addition to just having the letters appear, you could fade them in one at a time or have them drop from the sky or whatever you want).
Seems like a cool project for somebody who wants to get into learning JavaFX!
Nice post, nice application!
Did you mean a package with transitions like Jfxtras to controls?
This looks awesome!
It’s “voilà”, not “viola” 😉
Fabrice – thanks for that – I’ve fixed up the typo 🙂
Funny, this code style looked familiar… 🙂
I found back the forum thread at the start of the “bug” report, I didn’t know it was reported as bug: “How can I animate the display of text?” http://forums.sun.com/thread.jspa?threadID=5371857
Your idea of making third party text transitions is good! I hope some people will take up the challenge.