【问题标题】:Show text letter by letter逐个字母显示文本
【发布时间】:2011-09-01 00:27:34
【问题描述】:

使用 CSS 和 JavaScript 逐字母显示 html 文本(如视频游戏字幕)的最优雅方式是什么?

虽然我确信可以使用蛮力方法解决他的问题(例如,拆分字符并使用 jQuery.append() 逐个打印),但我希望有一些 CSS3(伪元素?)或 JQuery 魔法来更优雅地做到这一点。

如果解决方案考虑了内部 HTML 内容,则加分。

【问题讨论】:

  • 是否需要弹出单个字母?或者它可以是一个平滑的揭示?
  • 如果平滑显示按顺序显示字母,是的。

标签: javascript jquery html css


【解决方案1】:

HTML

<div id="msg"/>

Javascript

var showText = function (target, message, index, interval) {   
  if (index < message.length) {
    $(target).append(message[index++]);
    setTimeout(function () { showText(target, message, index, interval); }, interval);
  }
}

致电:

$(function () {
  showText("#msg", "Hello, World!", 0, 500);   
});

【讨论】:

  • @Luke:加个小提琴真的很有帮助!
  • 一旦运行,您将如何阻止它继续运行?例如,假设这是在单击期间调用的。在运行时,用户在第一次调用完成之前再次单击以显示另一条消息。这会导致两个循环重叠。如何阻止第一个循环继续?
  • 我认为 setTimeout 内部的匿名函数是不必要的,因为它只包含一个函数调用,这就是 setTimeout 本身的目的。 setTimeout 可以将参数传递给函数,当您在延迟后添加它们时。所以,这应该有效 - 并且正如我测试的那样有效:setTimeout(showText, interval, target, message, index, interval);
【解决方案2】:

如果流畅的展示是合理的,那么我认为这应该很简单。未经测试,但这是我想象的工作方式

html

<div id="text"><span>The intergalactic space agency</span></div>

css

div#text { width: 0px; height: 2em; white-space: nowrap; overflow: hidden;  }

jQuery

var spanWidth = $('#test span').width();
$('#text').animate( { width: spanWidth }, 1000 );

好吧,我忍不住做了一个小提琴。我修复的一个小代码错误。不过我觉得不错!

http://jsfiddle.net/mrtsherman/6qQrN/1/

【讨论】:

    【解决方案3】:

    100% vanilla javascript,严格模式,不显眼的 html,

    function printLetterByLetter(destination, message, speed){
        var i = 0;
        var interval = setInterval(function(){
            document.getElementById(destination).innerHTML += message.charAt(i);
            i++;
            if (i > message.length){
                clearInterval(interval);
            }
        }, speed);
    }
    
    printLetterByLetter("someElement", "Hello world, bonjour le monde.", 100);
    

    【讨论】:

      【解决方案4】:

      你真的应该只是append,或者显示/隐藏。

      但是,如果出于某种奇怪的原因您不想更改文本,则可以使用这段过于复杂的代码:

      HTML:

      <p>I'm moving slowly...<span class="cover"></span></p>
      

      CSS:

      p {
          font-family: monospace;
          float: left;
          padding: 0;
          position: relative;
      }
      .cover {
          height: 100%;
          width: 100%;
          background: #fff;
          position: absolute;
          top: 0;
          right: 0;
      }
      

      jQuery:

      var $p = $('p'),
          $cover = $('.cover'),
          width = $p.width(),
          decrement = width / $p.text().length;
      
      function addChar()
      {        
          $cover.css('width', '-=' + decrement);
      
          if ( parseInt( $cover.css('width') ) < width )
          {
              setTimeout(addChar, 300);
          }
      }
      
      addChar();
      

      最后,这是小提琴:http://jsfiddle.net/dDGVH/236/

      但是,说真的,不要使用这个......

      【讨论】:

      • +1 有趣的想法。可惜它同时显示所有行。也许它可以通过一些更改来工作。
      • @hgpc:当然可以,但这会涉及到 2 个.cover spans。这正变得越来越复杂。您确定不想简单地追加吗?
      • 我很可能会使用 append,但无论如何我觉得你的想法很有趣。此外,它还具有保留内部 html 内容的优点。
      【解决方案5】:

      通过为每次迭代准备 Promise 使您的代码更优雅,然后在第二步执行它们,您可以在其中注入 DOM 逻辑。

      const message = 'Solution using Promises';
      
      const typingPromises = (message, timeout) =>
        [...message].map(
          (ch, i) =>
            new Promise(resolve => {
              setTimeout(() => {
                resolve(message.substring(0, i + 1));
              }, timeout * i);
            })
        );
      
      typingPromises(message, 140).forEach(promise => {
        promise.then(portion => {
          document.querySelector('p').innerHTML = portion;
        });
      });
      &lt;div &gt;&lt;p&gt;&lt;/p&gt;&lt;/div&gt;

      【讨论】:

        【解决方案6】:

        当我这样做时,我遇到了一个单词从一行的末尾跳到下一行的乞求的问题,因为它的字母似乎绕过了这个我曾经并排跨度,其中一个文本是透明的,另一个是可见的,只是将字母从不可见的跨度一个接一个地移动到可见的范围内。这是fiddle

        HTML

        <div class='wrapper'>
          <span class='visible'></span><span class='invisible'></span>
        </div>
        

        CSS

        .visible {
          color: black;
        } 
        
        .invisible {
          color: transparent;
        }
        

        JS

        var text = "Whatever you want your text to be here",
            soFar = "";
        
        var visible = document.querySelector(".visible"),
            invisible = document.querySelector(".invisible");
        
        invisible.innerHTML = text;
        var t = setInterval(function(){
          soFar += text.substr(0, 1),
          text = text.substr(1);
        
          visible.innerHTML = soFar;
          invisible.innerHTML = text;
        
          if (text.length === 0) clearInterval(t);
        }, 100)
        

        【讨论】:

          【解决方案7】:

          我为此制作了一个小型 jquery 插件。首先,您需要确保如果禁用了 javascript,文本将可见,如果没有,则逐个字母地重新显示文本。

          $.fn.retype = function(delay) {
              var el = this,
                  t = el.text(),
                  c = t.split(''),
                  l = c.length,
                  i = 0;
              delay = delay || 100;
              el.empty();
              var interval = setInterval(function(){
                  if(i < l) el.text(el.text() + c[i++]); 
                  else clearInterval(interval);
              }, delay);
          };
          

          使用就这么简单:

          $('h1').retype();
          

          【讨论】:

          • 哇!在将其应用于多个元素的集合后,这使我的浏览器崩溃了……但否则效果很好。你在github上有吗?我想贡献一些东西。
          • 不,我在 github 上没有,你可以自己做。
          【解决方案8】:

          这是使用钩子的 React 版本。

          import React, { useState, useEffect } from "react";
          import "./styles.css";
          
          const App = ({ speed, msg }) => {
            const Typer = ({ speed = 250, children = " Introduce your text" }) => 
          {
            const [idx, setidx] = useState(0);
            useEffect(() => {
              const timer = window.setInterval(() => setidx((v) => v + 1), speed);
              return () => window.clearInterval(timer);
            });
          
            return <div>{children.substr(0, idx)}</div>;
          };
          return (
            <div>
              <Typer speed={speed} children={msg}></Typer>
            </div>
           );
          };
          
          export default App;
          

          和主要的 index.js

          import { StrictMode } from "react";
          import ReactDOM from "react-dom";
          
          import App from "./App";
          
          const message2 = "This is some text to get typed on time";
          
          const rootElement = document.getElementById("root");
          ReactDOM.render(
            <StrictMode>
              <App speed={100} msg={message2} />
            </StrictMode>,
            rootElement
          );
          

          看看code sandbox

          【讨论】:

            【解决方案9】:

            这是基于 armen.shimoon 的:

            var showText = function (target, message, index, interval) {    
                if (index <= message.length && $(target).is(':visible')) { 
                    $(target).html(message.substr(0, index++)); 
                    setTimeout(function () { showText(target, message, index, interval); }, interval); 
                } 
            }
            

            message[index++] 在我的 jquery 网页中不起作用 - 我必须将其更改为 substr。我的原始文本也使用 HTML,输入的文本使用 HTML 格式(br、b 等)。如果目标被隐藏,我也会停止该功能。

            【讨论】:

            • 在 Chrome 等浏览器中,当您将一些 HTML 代码放入目标时,有时会看到 <...>
            【解决方案10】:

            @armen.shimoon 回答的 Vanilla JavaScript 版本:

            document.addEventListener('DOMContentLoaded', function() {
            
                showText("#msg", "Hello, World!", 0, 100);
            
            });
            
            let showText = function (target, message, index, interval) {
                if (index < message.length) {
                    document.querySelector(target).innerHTML =
                        document.querySelector(target).innerHTML + message[index++];
                    setTimeout(function () { showText(target, message, index, interval); }, interval);
                }
            }
            <div id="msg"></div> 
            <span id="text_target"></span>

            【讨论】:

              【解决方案11】:

              您需要将每个字母包装在 span 标记中,因为无法设置匿名 html 元素的样式。然后一次显示一个跨度。这避免了一些 innerText / innerHTML 问题(没有 DOM 重排?),但在您的情况下可能会过大。

              【讨论】:

                【解决方案12】:

                香草

                (function () {
                
                var showText = function(target, msg, index, interval){
                
                  var el = document.getElementById(target);
                
                  if(index < msg.length){
                    el.innerHTML = el.innerHTML + msg.charAt(index);
                    index = index + 1;
                    setTimeout(function(){
                      showText(target,msg,index,interval);
                    },interval);
                  }
                
                };
                
                
                showText("id", "Hello, World!", 0, 50);   
                
                })();
                

                您可以通过更改此代码来改进它,因此您只能获得一次 el,因为修改 DOM 需要一些资源。

                【讨论】:

                  【解决方案13】:

                  有一个很好的答案here: 这是一种您可以使用任何可用的 .animate() 属性来操作每个字母的方法,而不是像用 s 覆盖文本等技巧。

                  【讨论】:

                    【解决方案14】:

                    我试图解决同样的问题,但我想出了这个似乎可行的解决方案。

                    HTML

                    <div id='target'></div>
                    

                    jQuery

                    $(function() {
                      var message = 'Hello world';
                      var index = 0;
                    
                      function displayLetter() {
                        if (index < message.length) {
                          $('#target').append(message[index++]);
                        }
                        else{
                          clearInterval(repeat);
                        }
                      }
                      var repeat = setInterval(displayLetter, 100);
                    });
                    

                    【讨论】:

                      【解决方案15】:

                      现在有几个库。 TypeItJsTypedJS 都允许 html 出现在您输入的内容中,以及许多其他有助于动画打字机效果的方法。

                      【讨论】:

                        【解决方案16】:

                        我将输入的答案是答案Show text letter by letter,但我对其进行了一些更改

                        HTML

                        <h1>
                            <span id="msg"></span>
                        </h1>
                        

                        Java脚本

                        var showText = function (target, message, index, interval) {    
                          if (index < message.length) { 
                            //$(target).append(message[index++]);
                            $(target).text($(target).text() + message[index++]);
                            setTimeout(function () { showText(target, message, index, interval); }, interval); 
                          } 
                        }
                        
                        $(function () { 
                          showText("#msg", "Hello, World!", 0, 80);    
                          //showText("#msg", "Hello, World!", 0, 500);
                        }); 
                        

                        CSS

                        #msg {
                            color: #6d67c6;
                            font-family: sans-serif;
                            font-size: 30px;
                            font-weight: bold;
                        }
                        

                        【讨论】:

                          【解决方案17】:
                          function textRoll(message, interval){
                             setTimeout(() => {
                                   para.innerHTML += message.charAt(0);
                                   return textRoll(message.slice(1))
                             }, interval)
                          }
                          

                          【讨论】:

                          • 您介意对您的代码进行一些解释吗?
                          • 是的,您只需在调用时传入 2 个参数,一个是要输入的消息,另一个是您希望它输出的速度。您只需将消息的第一个字符加上它需要放置的位置(在这种情况下,我希望它在 para.innerhtml 中,但可以更改)。然后你回忆起这个函数,但把第一个字母剪掉,这样第一个字母现在就是你的信息中的下一个字母。
                          • 当有人要求解释时,您可以在 cmets 中回复,但如果请求是一般性的并且会导致通过包含改进答案,那么编辑答案以包含该解释是个好主意.在用 15 个现有答案回答一个 8 年前的问题时,重要的是要指出您的答案所针对的问题的新方面。
                          • Tbh 我不明白你在说什么。我只是输入了这个答案,因为我查找了这个问题,看看是否有办法做到这一点,然后意识到我可以做得更短。所以我提出了答案,因为我认为它比其他答案简单得多。
                          【解决方案18】:

                          用 jquery 试试这个

                          var spanWidth = $('#text span').width();
                          $('#text').animate( { width: spanWidth }, 3000 );
                          div#text { width: 0px; height: 2em; white-space: nowrap; overflow: hidden;  }
                          <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
                          <div id="text"><span>Hello World !</span></div>

                          【讨论】:

                            猜你喜欢
                            • 1970-01-01
                            • 2020-10-02
                            • 1970-01-01
                            • 2016-08-02
                            • 2016-04-06
                            • 1970-01-01
                            • 2017-03-21
                            • 1970-01-01
                            • 2021-10-06
                            相关资源
                            最近更新 更多