For a client, I need to use a typewriter effect to display four different lines of text from an array
对于客户端,我需要使用打字机效果来显示数组中的四行不同的文本
I have my typewriter effect setup well, but I'm at a loss on why my forEach
loop only displays the last element from my array
我的打字机效果设置得很好,但是我为什么我的forEach循环只显示我的数组中的最后一个元素我感到很遗憾
var i = 0;
var text;
var txt = [
'Lorem ipsum dummy text blabla.',
'Lorem IPSUM dummy text blabla.',
'Lorem ipsum DUMMY text blabla.',
'Lorem ipsum dummy TEXT blabla.'
];
var speed = 50;
var delay = 3000
var demo = document.getElementById("demo");
function go() {
txt.forEach(function(str, index) {
text = str; // var to pass to typeWriter
setTimeout(typeWriter(), delay * index);
});
}
function typeWriter() {
if (i < text.length) {
demo.innerHTML += text.charAt(i);
i++;
setTimeout(typeWriter, speed);
} else {
// When string is fully typed, delete after 2 second
setTimeout(function() {
demo.innerHTML = '';
i = 0;
}, 2000);
}
}
<button onclick="go()">Click me</button>
<p id="demo"></p>
5 个解决方案
#1
2
You need to pass callback
functions to loop over your array's items
您需要传递回调函数来循环遍历数组的项目
Look at this code snippet
var i = 0;
var text;
var txt = [
'Lorem ipsum dummy text blabla.',
'Lorem IPSUM dummy text blabla.',
'Lorem ipsum DUMMY text blabla.',
'Lorem ipsum dummy TEXT blabla.'
];
var speed = 50;
var delay = 1000
var demo = document.getElementById("demo");
function go() {
function loop(index) {
if (index === txt.length) return;
setTimeout(function() {
text = txt[index];
typeWriter(function() {
loop(++index);
});
}, delay * index);
}
loop(0);
}
function typeWriter(cb) {
if (i < text.length) {
demo.innerHTML += text.charAt(i);
i++;
setTimeout(function() {
typeWriter(cb);
}, speed);
} else {
// When string is fully typed, delete after 2 second
setTimeout(function() {
demo.innerHTML += '<p>';
i = 0;
cb();
}, 2000);
}
}
<button onclick="go()">Click me</button>
<p id="demo"></p>
See? now is looping correctly.
看到?现在循环正确。
#2
1
Just thought it makes a nice question to illustrate async iteration, a brand-new ES2018 feature:
只是认为它是一个很好的问题来说明异步迭代,一个全新的ES2018功能:
async function delay(value, time) {
return new Promise(resolve => setTimeout(() => resolve(value), time));
}
async function *eachDelayed(values, time) {
for (let value of values)
yield await delay(value, time);
}
async function print(xs) {
for await (let x of eachDelayed(xs, 50))
document.body.innerHTML += x;
}
print('The quick brown fox jumps over the lazy dog!')
You need the latest Chrome/FF to run this.
你需要最新的Chrome / FF来运行它。
#3
0
Your line to update the UI is:
您更新UI的行是:
demo.innerHTML = '';
But needs to be:
但需要:
demo.innerHTML += '<br>';
So that you don't overwrite the previous output and you place the new output on a new line.
这样您就不会覆盖以前的输出,而是将新输出放在新行上。
Also, you are actually invoking the typewriter
function immediately because your setTimeout
looks like this:
此外,您实际上是立即调用打字机功能,因为您的setTimeout如下所示:
setTimeout(typeWriter(), delay * index);
Instead of just referencing the function, like this:
而不是仅仅引用该函数,如下所示:
setTimeout(typeWriter, delay * index);
var i = 0;
var text;
var txt = [
'Lorem ipsum dummy text blabla.',
'Lorem IPSUM dummy text blabla.',
'Lorem ipsum DUMMY text blabla.',
'Lorem ipsum dummy TEXT blabla.'
];
var speed = 50;
var delay = 3000
var demo = document.getElementById("demo");
function go() {
txt.forEach(function(str, index) {
text = str; // var to pass to typeWriter
setTimeout(typeWriter, delay * index);
});
}
function typeWriter() {
if (i < text.length) {
demo.innerHTML += text.charAt(i);
i++;
setTimeout(typeWriter, speed);
} else {
// When string is fully typed, delete after 2 second
setTimeout(function() {
demo.innerHTML += '<br>';
i = 0;
}, 2000);
}
}
<button onclick="go()">Click me</button>
<p id="demo"></p>
#4
0
forEach is not delayed. When typeWriter is executed, it will see text = 'Lorem ipsum dummy TEXT blabla.'
forEach没有延迟。当执行typeWriter时,它会看到text ='Lorem ipsum dummy TEXT blabla'。
My not so optimized recommendation:
我没有那么优化的建议:
var txt = [
'Lorem ipsum dummy text blabla.',
'Lorem IPSUM dummy text blabla.',
'Lorem ipsum DUMMY text blabla.',
'Lorem ipsum dummy TEXT blabla.'
];
function go(){
demo.innerHTML = '';
txt.length && doType();
}
function doType(){
if(!txt[0].length){
txt.shift();
demo.innerHTML += '<br />';
return setTimeout( doType, 50 );
}
if(!txt.length) return;
demo.innerHTML += txt[0][0];
txt[0] = txt[0].substr(1);
setTimeout( doType, 50 );
}
I believe you can do the necessary modification.
我相信你可以做必要的修改。
#5
0
Here is a solution that combines Promises with a setTimeout offset controller.
这是一个将Promises与setTimeout偏移控制器结合在一起的解决方案。
const speed = 50;
const delay = 2000;
const demo = document.getElementById("demo");
function go() {
const textArray = [
'Lorem ipsum dummy text blabla.',
'Lorem IPSUM dummy text blabla.',
'Lorem ipsum DUMMY text blabla.',
'Lorem ipsum dummy TEXT blabla.'
];
let linesToType = initializePromises(textArray)
linesToType.reduce((promiseChain, currentLine) => {
return promiseChain.then(currentLine);
}, Promise.resolve())
}
var offsetController = (function() {
let _offset = 0;
return {
increaseOffset(time) {
_offset = _offset + time
},
getOffset(){
return _offset;
}
}
})()
function initializePromises(textArray) {
return textArray.map(line => typeWriter(line) )
}
function typeWriter(text) {
offsetController.increaseOffset(delay);
return new Promise((resolve, reject) => {
let promises = text.split("").map((letter, index) => {
return printNextLetter(demo, text, index)
})
clearLine(demo);
Promise.all(promises).then(() => {
resolve()
})
})
}
function printNextLetter(element, text, i) {
offsetController.increaseOffset(speed);
return new Promise((resolve) => setTimeout(() => {
element.innerHTML += text.charAt(i);
resolve()
}, offsetController.getOffset()));
}
function clearLine(element) {
offsetController.increaseOffset(delay);
return new Promise((resolve) => setTimeout(() => {
element.innerHTML = "";
resolve()
}, offsetController.getOffset()));
}
<button onclick="go()">Click me</button>
<p id="demo"></p>
#1
2
You need to pass callback
functions to loop over your array's items
您需要传递回调函数来循环遍历数组的项目
Look at this code snippet
var i = 0;
var text;
var txt = [
'Lorem ipsum dummy text blabla.',
'Lorem IPSUM dummy text blabla.',
'Lorem ipsum DUMMY text blabla.',
'Lorem ipsum dummy TEXT blabla.'
];
var speed = 50;
var delay = 1000
var demo = document.getElementById("demo");
function go() {
function loop(index) {
if (index === txt.length) return;
setTimeout(function() {
text = txt[index];
typeWriter(function() {
loop(++index);
});
}, delay * index);
}
loop(0);
}
function typeWriter(cb) {
if (i < text.length) {
demo.innerHTML += text.charAt(i);
i++;
setTimeout(function() {
typeWriter(cb);
}, speed);
} else {
// When string is fully typed, delete after 2 second
setTimeout(function() {
demo.innerHTML += '<p>';
i = 0;
cb();
}, 2000);
}
}
<button onclick="go()">Click me</button>
<p id="demo"></p>
See? now is looping correctly.
看到?现在循环正确。
#2
1
Just thought it makes a nice question to illustrate async iteration, a brand-new ES2018 feature:
只是认为它是一个很好的问题来说明异步迭代,一个全新的ES2018功能:
async function delay(value, time) {
return new Promise(resolve => setTimeout(() => resolve(value), time));
}
async function *eachDelayed(values, time) {
for (let value of values)
yield await delay(value, time);
}
async function print(xs) {
for await (let x of eachDelayed(xs, 50))
document.body.innerHTML += x;
}
print('The quick brown fox jumps over the lazy dog!')
You need the latest Chrome/FF to run this.
你需要最新的Chrome / FF来运行它。
#3
0
Your line to update the UI is:
您更新UI的行是:
demo.innerHTML = '';
But needs to be:
但需要:
demo.innerHTML += '<br>';
So that you don't overwrite the previous output and you place the new output on a new line.
这样您就不会覆盖以前的输出,而是将新输出放在新行上。
Also, you are actually invoking the typewriter
function immediately because your setTimeout
looks like this:
此外,您实际上是立即调用打字机功能,因为您的setTimeout如下所示:
setTimeout(typeWriter(), delay * index);
Instead of just referencing the function, like this:
而不是仅仅引用该函数,如下所示:
setTimeout(typeWriter, delay * index);
var i = 0;
var text;
var txt = [
'Lorem ipsum dummy text blabla.',
'Lorem IPSUM dummy text blabla.',
'Lorem ipsum DUMMY text blabla.',
'Lorem ipsum dummy TEXT blabla.'
];
var speed = 50;
var delay = 3000
var demo = document.getElementById("demo");
function go() {
txt.forEach(function(str, index) {
text = str; // var to pass to typeWriter
setTimeout(typeWriter, delay * index);
});
}
function typeWriter() {
if (i < text.length) {
demo.innerHTML += text.charAt(i);
i++;
setTimeout(typeWriter, speed);
} else {
// When string is fully typed, delete after 2 second
setTimeout(function() {
demo.innerHTML += '<br>';
i = 0;
}, 2000);
}
}
<button onclick="go()">Click me</button>
<p id="demo"></p>
#4
0
forEach is not delayed. When typeWriter is executed, it will see text = 'Lorem ipsum dummy TEXT blabla.'
forEach没有延迟。当执行typeWriter时,它会看到text ='Lorem ipsum dummy TEXT blabla'。
My not so optimized recommendation:
我没有那么优化的建议:
var txt = [
'Lorem ipsum dummy text blabla.',
'Lorem IPSUM dummy text blabla.',
'Lorem ipsum DUMMY text blabla.',
'Lorem ipsum dummy TEXT blabla.'
];
function go(){
demo.innerHTML = '';
txt.length && doType();
}
function doType(){
if(!txt[0].length){
txt.shift();
demo.innerHTML += '<br />';
return setTimeout( doType, 50 );
}
if(!txt.length) return;
demo.innerHTML += txt[0][0];
txt[0] = txt[0].substr(1);
setTimeout( doType, 50 );
}
I believe you can do the necessary modification.
我相信你可以做必要的修改。
#5
0
Here is a solution that combines Promises with a setTimeout offset controller.
这是一个将Promises与setTimeout偏移控制器结合在一起的解决方案。
const speed = 50;
const delay = 2000;
const demo = document.getElementById("demo");
function go() {
const textArray = [
'Lorem ipsum dummy text blabla.',
'Lorem IPSUM dummy text blabla.',
'Lorem ipsum DUMMY text blabla.',
'Lorem ipsum dummy TEXT blabla.'
];
let linesToType = initializePromises(textArray)
linesToType.reduce((promiseChain, currentLine) => {
return promiseChain.then(currentLine);
}, Promise.resolve())
}
var offsetController = (function() {
let _offset = 0;
return {
increaseOffset(time) {
_offset = _offset + time
},
getOffset(){
return _offset;
}
}
})()
function initializePromises(textArray) {
return textArray.map(line => typeWriter(line) )
}
function typeWriter(text) {
offsetController.increaseOffset(delay);
return new Promise((resolve, reject) => {
let promises = text.split("").map((letter, index) => {
return printNextLetter(demo, text, index)
})
clearLine(demo);
Promise.all(promises).then(() => {
resolve()
})
})
}
function printNextLetter(element, text, i) {
offsetController.increaseOffset(speed);
return new Promise((resolve) => setTimeout(() => {
element.innerHTML += text.charAt(i);
resolve()
}, offsetController.getOffset()));
}
function clearLine(element) {
offsetController.increaseOffset(delay);
return new Promise((resolve) => setTimeout(() => {
element.innerHTML = "";
resolve()
}, offsetController.getOffset()));
}
<button onclick="go()">Click me</button>
<p id="demo"></p>