Smoothly Changing Text With Javascript
Solution 1:
I'm going to write an outline of how I would do did it:
- Render the sentence using default initial values for the changing words and static positioning.
- Also render the other word variants with
visibility: hidden
so that you can determine their sizes. - Absolutize every sentence part. From this point on everything will be absolutely positioned so it's best if you have a nice positioning context around it (usually done with
position: relative
on the parent). - Measure every sentence part, both changing words and fixed sentence parts widths, including the hidden ones.
- When changing the words compute the differences between the old sizes and the new ones. Based on those differences, use some very simple Math to see how much parts should move left or right and apply a horizontal translation on them (and of course animate the translation - possibly just for what you want moving left/right, maybe you want other effects for the changing words).
Demo:
var first = ['Create','Cut','Reticulate'];
var second = ['you','clients','artists','us'];
var firstM = [], secondM = [], el;
var $first = $('.the-first');
var $second = $('.the-second');
var $container = $('#container');
// init static //
$first.text(first[0]);
$second.text(second[0]);
// create measurables //for(var i = 0; i < first.length; i++){
el = $('<div class="measurable">' + first[i] + '</div>');
$container.append(el);
firstM.push(el.width());
}
for(var i = 0; i < second.length; i++){
el = $('<div class="measurable">' + second[i] + '</div>');
$container.append(el);
secondM.push(el.width());
}
// absolutize //var positions = [];
$('#container > span').each(function(){
positions.push($(this).position());
});
$('#container > span').each(function(){
var pos = positions.shift();
$(this).css({
position: 'absolute',
left: pos.left,
top: pos.top
});
});
// remember initial sizes //var firstInitialWidth = $first.width();
var secondInitialWidth = $second.width();
// loop the loop //var activeWordsIndex = 0;
setInterval(function(){
activeWordsIndex++;
var firstIndex = activeWordsIndex % first.length;
var secondIndex = activeWordsIndex % second.length;
$first.text( first[firstIndex] );
$second.text( second[secondIndex] );
var firstLineOffset = (firstM[firstIndex] - firstInitialWidth) / 2;
var secondLineOffset = (secondM[secondIndex] - secondInitialWidth) / 2;
$('.static.first').css({
transform: 'translateX(' + firstLineOffset + 'px)'
});
$('.static.second').css({
transform: 'translateX(' + (-secondLineOffset) + 'px)'
});
$first.css({
transition: 'none',
transform: 'translate(' + (-firstLineOffset) + 'px, -30px)',
opacity: '0'
});
setTimeout(function(){
$first.css({
transition: 'all 1s ease',
transform: 'translate(' + (-firstLineOffset) + 'px, 0px)',
opacity: '1'
});
}, 50);
$second.css({
transition: 'none',
transform: 'translate(' + (-secondLineOffset) + 'px, 30px)',
opacity: '0'
});
setTimeout(function(){
$second.css({
transition: 'all 1s ease',
transform: 'translate(' + (-secondLineOffset) + 'px, 0px)',
opacity: '1'
});
}, 50);
}, 2000);
#ubercontainer {
border: 1px solid #eaeaea;
border-radius: 2px;
background-color: #ffefc6;
width: 500px;
margin: 20px auto;
padding: 30px0;
}
#container {
position: relative;
text-align: center;
font-family: sans-serif;
font-size: 32px;
font-weight: 800;
color: #4a6b82;
height: 78px;
}
.measurable {
position: absolute;
visibility: hidden;
}
.static.first, .static.second {
transition: transform 1s ease;
}
<scriptsrc="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script><divid="ubercontainer"><divid="container"><spanclass="the-first"></span><spanclass="static first">better websites </span><br /><spanclass="static second">made for</span><spanclass="the-second"></span></div></div>
Solution 2:
Writing a solution like this you will quickly come to realize you will have to abandon the idea of dynamic line breaks if your words differ in length by a reasonable amount.
That small detail aside, you can easily achieve the effect you're after using a standard pyramid of jQuery animate callback hell:
var target = $('#target');
var change = function(str) {
var tmp = $('<h1>' + str + '</h1>');
tmp.css({
display: "inline-block",
position: "absolute"
})
.appendTo('body')
.hide();
var targetWidth = tmp.outerWidth();
tmp.remove();
target.animate({
opacity: 0
}, 200, function() {
target.animate({
width: targetWidth
}, 300, function() {
target.empty()
.html(str)
.css({
display: "initial"
})
.animate({
opacity: 1
}, 200);
});
});
}
var samples = [
"some sample",
"another example",
"just"
];
var i = 0;
setInterval(function() {
change(samples[++i % samples.length]);
}, 1400);
.container {
margin: 0 auto;
text-align: center;
}
#target {
display: inline-block;
vertical-align: bottom;
white-space: no-wrap;
height: 1em;
}
<scriptsrc="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script><divclass="container"><h1>This is <spanid="target"></span> text</h1><h1>in a longer sentence</h1></div>
Solution 3:
Try using the Web Animation API
Element.animate();
Simplest reference: http://updates.html5rocks.com/2014/05/Web-Animations---element-animate-is-now-in-Chrome-36
Solution 4:
Got some good answers. Alin Purcaru has a much better and more coherently-written answer, but I thought I'd provide my own.
Nit had what I was looking for, but since I'm not the best programmer, I tried to come up with a solution I could understand. After an hour or two, here's what I got.
Basically, I'm comparing the full block of text to the parent element, finding the space between them, halving it, and then applying that as a negative margin to the text. I can transition this with CSS since I'm moving a full block.
Here's a very bad drawing in MSpaint to illustrate my point
the text has display: inline-block
so the div fits to the text rather than taking up 100% of the parent.
Since I was transition with CSS in my javascript, all I needed to do to make it smooth was
-webkit-transition: all 1s ease-in-out;
-moz-transition: all 1s ease-in-out;
-o-transition: all 1s ease-in-out;
transition: all 1s ease-in-out;
Post a Comment for "Smoothly Changing Text With Javascript"