In this jsfiddle
, label transitions from one text to another by decreasing font of old text, and in turn increasing the font of new text.
However, I would like new text to apperar in "typewriter" fashion, like in this jsfiddle
. How to write a custom D3 text interpolator that will enable text transitioning in this "typewriter" way?
Interesting ideas, both get the job done, but there's a d3-specific way to do this: a custom tween function.
Fiddle here: http://jsfiddle.net/QbysN/3/
Code:
function transition() {
d3.select('text').transition()
.duration(5000)
.ease("linear")
.tween("text", function () {
var newText = data[i];
var textLength = newText.length;
return function (t) {
this.textContent = newText.substr(0,
Math.round( t * textLength) );
};
});
i = (i + 1) % data.length;
}
The outer function that you pass to .tween()
is called a tween factory because it creates the tween function for each element. It is executed once per element (and would normally use the data and index of the element as parameters), at the start of the transition. It runs any set-up calculations and then returns the tween function that will be used during the transition.
The returned tween function is also called an interpolator because it calculates intermediary values. In this case it is:
function (t) {
this.textContent = newText.substr(0, Math.round( t * textLength) );
};
This function will get called at every "tick" of the transition, and passed a value between 0 and 1 representing how far through the transition the result is supposed to be. (The rate at which 0 changes to 1 will vary according to the easing parameter, I've used linear easing for a steady rate of type.)
When you are specifying custom tween functions for attributes or styles, the tween function would return the value to be used for that attribute or style at that point in the transition. For the generic transition.tween()
, return values are not used, your function has to actually make the change itself -- which I do by directly setting the textContent
property of the element. I set it to a substring of the target text, where the number of characters in the substring is determined by the t
parameter (which is always between 0 and 1) and the length of the text, so that the entire text gets typed out in the length of the transition.
P.S. You can have fun with the easing function, too. The "bounce" ease makes it look like the typist is unsure about what they are writing:
http://jsfiddle.net/QbysN/4/
I gave this a shot. I must say I do not have any prior D3 experience. I tried to get a combination of the two fiddles you showed. I think there's still some cleanup to do and when to stop the typing from occurring, but those should be trivial.
Check out the jsFiddle
HTML:
<div id="writer">
<span id='sentence'></span>
<span id='char'></span>
</div>
CSS:
#writer{
border: 1px solid black;
width:300px;
min-height: 200px;
margin: auto;
}
span
{
float:left;
}
JavaScript:
var text = "Hello World! I'm some typed text!";
var counter = 0;
var speed = 50;
function type()
{
var sentence = document.getElementById("sentence");
var lastText = sentence.innerHTML;
var nextChar = text.charAt(counter);
counter++;
transition(nextChar,
function()
{
lastText+=nextChar;
sentence.innerHTML = lastText;
setTimeout(type, speed);
});
}
function transition(char, onComplete) {
d3.select('#char').transition()
.text("")
.duration(300)
.style("font-size","1px")
.transition()
.duration(300)
.text(char)
.style("font-size","16px")
.each("end", onComplete);
}
type();
I thought it would be easier for the text to get typed automatically. Therefore I iterated through each character and called the transition method. I added 2 arguments to it row
and column
so that I could give appropriate delays.
data.forEach(function(d,row) {
d.split("").forEach(function(d,column) {
transition(row,column);
});
});
function transition(row,column) {
d3.select('text').datum(data[row].split("").slice(0,column+1))
.transition()
.delay(function(d) { return (row*5000) + (column*50); } )
.text(function(d){return d.join("");});
}
Try this Fiddle: http://jsfiddle.net/QbysN/2/