Smoothly changing text with JavaScript

前端 未结 4 1063
故里飘歌
故里飘歌 2021-02-01 03:54

I\'m making a landing page where a phrase is constantly changing with select words. For instance,

Design better websites
made for clients.

相关标签:
4条回答
  • 2021-02-01 04:34

    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

    0 讨论(0)
  • 2021-02-01 04:37

    I'm going to write an outline of how I would do did it:

    1. Render the sentence using default initial values for the changing words and static positioning.
    2. Also render the other word variants with visibility: hidden so that you can determine their sizes.
    3. 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).
    4. Measure every sentence part, both changing words and fixed sentence parts widths, including the hidden ones.
    5. 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: 30px 0;
    }
    #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;
    }
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <div id="ubercontainer">
    <div id="container">
        <span class="the-first"></span> 
        <span class="static first">better websites </span><br />
        <span class="static second">made for</span> 
        <span class="the-second"></span>
    </div>
    </div>

    0 讨论(0)
  • 2021-02-01 04:44

    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

    enter image description here

    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;
    
    0 讨论(0)
  • 2021-02-01 04:51

    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;
    }
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <div class="container">
      <h1>This is <span id="target"></span> text</h1>
      <h1>in a longer sentence</h1>
    </div>

    0 讨论(0)
提交回复
热议问题