16

I have a sentence where I fade in one word and replace it with another from an array. However, since the words all vary in length, the sentence width abruptly changes and it results in a choppy transition.
How can I animate the width change? I tried adding a transition to the container of the sentence in css but that didn't work. I applied the transition as 1.5s all linear, so it should be animating the width as well as everything else whenever there is change. Any ideas?

$(function() {
  var hello = ['dynamic', 'a', 'aklsjdlfajklsdlkf', 'asdf'];
  var used = ['dynamic'];
  var greeting = $('#what');
  var item;

  function hey() {

    item = hello[Math.floor(Math.random() * hello.length)];
    if (hello.length != used.length) {
      while (jQuery.inArray(item, used) != -1) {
        item = hello[Math.floor(Math.random() * hello.length)];
      }
      used.push(item);
    } else {
      used.length = 0;
      item = hello[Math.floor(Math.random() * hello.length)];
      used.push(item);
    }
    greeting.html(item);
    greeting.animate({
      "opacity": "1"
    }, 1500);
  }

  window.setInterval(function() {
    greeting.animate({
      "opacity": "0"
    }, 1500);
    setTimeout(hey, 1500)
  }, 5000);

});
#sentence {
  transition: 1.5s all linear;
}

#what {
  font-style: italic;
  text-decoration: underline;
  color: red;
}
<p id="sentence">
  This is a sentence that has <span id="what">dynamic</span> text that alters width.
</p>

<script src="//ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>

EDIT: Sorry if I was unclear, I only want to fade out the word, not the entire sentence. I'm trying to animate the width to fit the new word. I don't want to change/add any elements, just solve with the current tags in place.

Josue Espinosa
  • 5,009
  • 16
  • 47
  • 81
  • Css animations wont animate not declared value... Like auto width to auto width... Only if it is set to exact value... Like if you had 100 and and change this value, it will animate changes – Sergei Zahharenko Dec 20 '13 at 05:05
  • So you can do it that way. Set width of existing word by script. Then before changing it measure the new word width (there is many different ways for that) and set it with opacity... – Sergei Zahharenko Dec 20 '13 at 05:07

4 Answers4

32

Fade animate text with jQuery by Roko C.B.

function dataWord () {

  $("[data-words]").attr("data-words", function(i, d){
    var $self  = $(this),
        $words = d.split("|"),
        tot    = $words.length,
        c      = 0; 

    // CREATE SPANS INSIDE SPAN
    for(var i=0; i<tot; i++) $self.append($('<span/>',{text:$words[i]}));

    // COLLECT WORDS AND HIDE
    $words = $self.find("span").hide();

    // ANIMATE AND LOOP
    (function loop(){
      $self.animate({ width: $words.eq( c ).width() });
      $words.stop().fadeOut().eq(c).fadeIn().delay(1000).show(0, loop);
      c = ++c % tot;
    }());
    
  });

}

// dataWord(); // If you don't use external fonts use this on DOM ready; otherwise use:
$(window).on("load", dataWord);
p{text-align: center;font-family: 'Open Sans Condensed', sans-serif;font-size: 2em;}

/* WORDS SWAP */
[data-words]{
  vertical-align: top;
  position: static;
}
[data-words] > span{
  position: absolute;
  color: chocolate;
}
<link href='http://fonts.googleapis.com/css?family=Open+Sans+Condensed:300' rel='stylesheet' type='text/css'>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<p>
  We provide
  <span data-words="code|solutions|design"></span>
  for your business.
</p>

<p>
  You ordered
  <span data-words="1|3|28"></span>
  <b>big</b>
  <span data-words="salad|macs|chips"></span>
</p>
Roko C. Buljan
  • 196,159
  • 39
  • 305
  • 313
  • This does work, what is up with the extra $'s though? Is that jQuery? I have never seen variables used with a $ prior to the name, what is the purpose of that? – Josue Espinosa Dec 20 '13 at 05:58
  • 4
    @JosueEspinosa that is a common way we use to indicate that this variable is a reference to a HTMLElement object. Makes easier to read code. immagine you have `var width = $('#width')`, and than somewhere in line 200 you read `width`, you might think it's a number. Makes easier to read further in the code a variable like `$width` - crystal clear it's about an element. :) – Roko C. Buljan Dec 20 '13 at 07:04
  • The demo appears to be broken In Chrome 33. – JonathanBristow Feb 18 '15 at 16:14
  • 1
    @Limestone interesting, an issue occurred only the first time I've followed my demo link. Not any other time. Any way I've modified the call to the callback inside `.show`... should help. – Roko C. Buljan Feb 18 '15 at 22:10
  • @RokoC.Buljan I must admit I didn't try any other browser or refresh the page when it wasn't working. Either way, it looks like it's working now. Thanks! – JonathanBristow Feb 19 '15 at 02:03
  • @RokoC.Buljan, what if we want to animate 2 words at the same time? – Pawel Gumiela Dec 13 '15 at 16:05
  • @belford sorry I totally missed your comment! Edited my answer to make it work for more words-sets! – Roko C. Buljan Jun 06 '16 at 12:35
  • Using current latest browsers (Chrome 57, Opera 44, Edge 38, Firefox 50), it looks like that the width of the first word coming in the first run of the animation is wrong, please look at this [screenshot](http://tinypic.com/r/2ibj5dv/9) – Rizki Pratama Mar 26 '17 at 09:49
  • @softtama yes, that extra space was actually the width of `sans-serif` default font, than when a custom font was loaded (and cached by the browser) then the calculation was right for all other iterations. See my edit for a fix. (and thanks for noticing!) – Roko C. Buljan Mar 26 '17 at 13:08
2

When you set new word for your sentence, you can save #what width and then make an animation with this width too. Like this:

// declare as global variable and update when you set new word
var width = greeting.css('width'); 
// animation
greeting.animate({
            "opacity": "0", "width": width
        }, 1500, function(){
        });
Ringo
  • 3,795
  • 3
  • 22
  • 37
1

I have had the same problem and went with a different approach, not fading but typing: jsfiddle demo

function type($el, text, position) {
    if (text.length >= position) {
        var rchars = 'qbvol'; // typo chars
        if (position % 3 == 0 && Math.random() > .85) { // insert typo!
            var typo;
            var chr = text.substr(position, 1);
            if (chr == chr.toUpperCase()) { typo = chr.toLowerCase(); }
            else { typo = rchars.substr(Math.floor(Math.random() * rchars.length), 1); }
            $el.text(text.substring(0, position - 1) + typo + '_');
            setTimeout(function() { type($el, text, position - 1); }, 200)
        }
        else {
            $el.text(text.substring(0, position) + '_');
            setTimeout(function() { type($el, text, position + 1); }, 150)
        }
    }
    else {
        setTimeout(function() { $el.text(text); }, 400)
    }
}

It basically inserts your new text on the page, with a nice caret and typo to make it look like someone is typing it.

Willem
  • 5,364
  • 2
  • 23
  • 44
  • This would be probably better suited to a *typewriter* question like http://stackoverflow.com/questions/13325008/typewriter-effect-with-jquery :) – Roko C. Buljan Dec 20 '13 at 08:31
  • you're right, but it's also a nice animation for changing text in a sentence, but maybe it should run a little faster, without the typo and caret – Willem Dec 20 '13 at 08:42
0

Try this out:- http://jsfiddle.net/adiioo7/c8fFU/13/

You can update the sentence effect depending upon your requirement. Currently it is using fadein/fadeout.

JS:-

$(function () {
    var hello = ['jupiter', 'a', 'aklsjdlfajklsdlkf', 'asdf'];
    var used = ['jupiter'];
    var greeting = $('#what');
    var item;
    var sentence = $('#sentence');

    function hey() {
        item = hello[Math.floor(Math.random() * hello.length)];
        if (hello.length != used.length) {
            while (jQuery.inArray(item, used) != -1) {
                item = hello[Math.floor(Math.random() * hello.length)];
            }
            used.push(item);
        } else {
            used.length = 0;
            item = hello[Math.floor(Math.random() * hello.length)];
            used.push(item);
        }
        greeting.html(item);
        greeting.animate({
            "opacity": "1"
        }, 1500);

        sentence.fadeIn(1500);
    }

    window.setInterval(function () {
        sentence.fadeOut(1500);
        greeting.animate({
            "opacity": "0"
        }, 1500);
        setTimeout(hey, 1500);
    }, 5000);

});
Aditya Singh
  • 9,512
  • 5
  • 32
  • 55