Проблема при выполнении пользовательской функции набора текста в javascript несколько раз - PullRequest
2 голосов
/ 31 марта 2020

Я пытаюсь создать игру с HTML, CSS и JS, которая включает анимацию набора текста. Я хочу пользовательскую функцию, которая при вызове набирает текст. Однако при многократном использовании функции возникает проблема. Функция объединяет текст из разных вводимых строк. в функции. Пожалуйста, помогите мне найти решение этой проблемы. Мой код похож на пример кода W3 Schools с некоторыми изменениями.

var txt, speed, i;
function typeWriter() {
	if (i < txt.length) {
    document.getElementById("text").innerHTML += txt.charAt(i);
    i++;
    setTimeout(typeWriter, speed);
  }
}
function write(whatToWrite, howFast){
	i = 0;
	txt = whatToWrite;
	speed = howFast;
	typeWriter();
}

//There is a problem when called multiple times
write("hello", 100);
write("world", 100);
html{
  background-color: #0a1c08;
  color: #ffffff;
	font-size: 20px;
}
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">
    <title>The Game of Destiny</title>
    <link href="https://fonts.googleapis.com/css?family=Inconsolata&display=swap" rel="stylesheet">
    <link href="style.css" rel="stylesheet" type="text/css" />
  </head>
  <body>
		<p id="text"></p>
  </body>
</html>
<script src="typewriter.js"></script>

Ответы [ 2 ]

0 голосов
/ 31 марта 2020

setTimeout не блокируется, а следующий write выполняется сразу после того, как первый write напечатал первый символ. Тогда i будет сброшено на 0, и только второй вызов write будет выполнен правильно.

Это можно исправить с помощью обещаний или просто путем цепочки слов для ввода write функция, как это:

var txt = '',
  i = 0,
  pad = document.getElementById('text'),
  speed;
function typeWriter() {
  if (i < txt.length) {
    pad.insertAdjacentText('beforeEnd', txt.charAt(i));
    i++;
    setTimeout(typeWriter, speed);
  }
}

function write(whatToWrite, howFast) {
  txt += whatToWrite;
  speed = howFast;
  typeWriter();
}

write('hello', 100);
write(' world', 100);
html {
  background-color: #0a1c08;
  color: #ffffff;
  font-size: 20px;
}
<p id="text"></p>

Обратите внимание, что txt и i теперь инициализируются в верхней области вместо функции write. Я также определил элемент для ввода вне функции и использовал insertAdjacentText вместо innerHTML. Эти изменения делают код более легким для выполнения, особенно если вы собираетесь набирать очень длинный текст в абзаце.

0 голосов
/ 31 марта 2020

Как сказано в первом комментарии, setTimeout не блокируется, и следующая запись выполняется немедленно. Чтобы предотвратить такое поведение, вам нужно как-то заблокировать выполнение. Одним из способов будет использование обратного вызова в функции тайм-аута, другой - для ожидания обещания. Хорошее объяснение можно найти в этой записи .

. Чтобы решить проблему, я использовал пользовательскую функцию сна, которая блокирует выполнение, пока идет анимация ввода. Он не идеален и может использовать некоторые уточнения, но он работает.

var speed;
function typeWriter(i, txt) {
	if (i < txt.length) {
    document.getElementById("text").innerHTML += txt.charAt(i);
    i++;
    setTimeout(() => typeWriter(i, txt), speed);
  }
}

async function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
};

async function wrapper() {
  let word = "hello";
  speed = 100;
  await typeWriter(0, word);
  await sleep(word.length * speed);

  word = "world";
  speed = 200;
  await typeWriter(0, word);
}
wrapper()
html{
  background-color: #0a1c08;
  color: #ffffff;
	font-size: 20px;
}
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">
    <title>The Game of Destiny</title>
    <link href="https://fonts.googleapis.com/css?family=Inconsolata&display=swap" rel="stylesheet">
    <link href="style.css" rel="stylesheet" type="text/css" />
  </head>
  <body>
		<p id="text"></p>
  </body>
</html>
<script src="typewriter.js"></script>
...