使用JavaScript平滑地更改文本

时间:2014-12-15 01:43:28

标签: javascript

我正在创建一个着陆页,其中一个短语随着选择的单词不断变化。例如,

  

设计更好的网站
  为客户制作。

会将第一个或最后一个单词切换为

  

开发更好的网站
  为客户制作。

然而,由于“开发”是一个比“设计”更大的词,因此文本的其余部分最终会被推动而不会平滑过渡。请记住,这是一个多行句子,它是居中的。

var first  = ['Create','Design','Develop'];
var second = ['you','clients','artists','us'];
var i = 0;
var j = 0;
var maxfirst  = first.length - 1;
var maxsecond = second.length - 1;

function delay() {
    $('#intro').velocity("transi1ion.slideUpIn", 1250);
    setInterval(firstwordchange, 400);
    setInterval(secondwordchange, 500);
}

function firstwordchange() {
    if (i < maxfirst) i++; else i = 0;

    $('#firstword').velocity("transition.slideUpOut", 300);

    setTimeout(function () {
        $('#firstword').text(first[i]);
    }, 200);

  $('#firstword').velocity("transition.slideUpIn", 300);
}

function secondwordchange() {
    if (j < maxsecond) j++; else j = 0;

    $('#secondword').velocity("transition.slideUpOut", 300);

    setTimeout(function () {
        $('#secondword').text(second[j]);
    }, 200);

    $('#secondword').velocity("transition.slideUpIn", 300);
}

setTimeout(delay, 700);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/velocity/1.1.0/velocity.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/velocity/1.1.0/velocity.ui.min.js"></script>
<div id="intro">
    <span id="firstword" class="introchange">Create</span>
    better websites made for
    <span id="secondword" class="introchange">you</span>.
</div>

如何顺利完成内部不变的文本转换?

actual website here

4 个答案:

答案 0 :(得分:20)

我将写一篇关于将如何做的大纲:

  1. 使用默认初始值为更改单词和静态定位渲染句子。
  2. 还使用visibility: hidden渲染其他单词变体,以便您可以确定其大小。
  3. Absolutize每个句子部分。从这一点来看,一切都将是绝对定位的,如果你有一个很好的定位环境(通常在父母的position: relative上完成),它将是最好的。
  4. 测量每个句子部分,包括改变单词和固定句子部分的宽度,包括隐藏的部分。
  5. 更改单词时计算旧尺寸和新尺寸之间的差异。基于这些差异,使用一些非常简单的数学来查看应该向左或向右移动多少部分并在它们上应用水平翻译(当然还要为翻译设置动画 - 可能只是为了你想要左/右移动,也许你想要其他对改变词语的影响)。
  6. <强>演示:

    &#13;
    &#13;
    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);
    &#13;
    #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;
    }
    &#13;
    <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>
    &#13;
    &#13;
    &#13;

答案 1 :(得分:9)

写一个像这样的解决方案你很快就会意识到,如果你的单词的长度差异合理,你将不得不放弃动态换行的想法。

除了这个小细节之外,你可以很容易地达到使用标准金字塔jQuery动画回调地狱后的效果:

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>

答案 2 :(得分:4)

尝试使用Web动画API

Element.animate();

最简单的参考:http://updates.html5rocks.com/2014/05/Web-Animations---element-animate-is-now-in-Chrome-36

答案 3 :(得分:2)

得到了一些好的答案 Alin Purcaru有一个更好,更连贯的答案,但我认为我提供了自己的答案。

Nit有我想要的东西,但由于我不是最好的程序员,我试图想出一个我能理解的解决方案。一两个小时后,here's what I got.

基本上,我将整个文本块与父元素进行比较,找到它们之间的空间,将其减半,然后将其作为负边距应用于文本。我可以用CSS转换它,因为我移动了一个完整的块。

这是MSpaint中一个非常糟糕的绘图来说明我的观点

enter image description here

文字有display: inline-block,因此div适合文字,而不是占据父母的100%。

由于我在我的javascript中使用CSS进行过渡,所以我需要做的就是让它顺利进行

  -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;