Okay, so I have created this javascript text-animation that has a delay when writing text to
my p element "output". The animation works just fine...but I decided that I wanted to add text color to specific words in my "message" parameter. I made that happen by using this code:
function write(message,clear,delay) {
if(clear == true) clearMessage();
if(typeof delay != "number") delay = game.message.delay;
let q = undefined;
let output = document.getElementById("output");
let applyingColor = false;
let dest = output;
for(var i = 0 ; i < message.length ; i++) {
let cur = i;
let letter = message.charAt(i);
let wait = delay*i;
q = setTimeout(() => {
if(letter == "<") {
letter = "";
applyingColor = true;
let color = undefined;
let a = message.substring(cur+1,message.length);
let colorSig = a.substring(0,a.indexOf("/"));
color = colorSig;
let span = document.createElement("span");
span.style.color = color;
output.appendChild(span);
dest = span;
} else if(letter == ">") {
letter = "";
dest = output;
} else if(letter == "|") {
letter = "<br>";
}
if(applyingColor) {
if(letter != "/") {
return;
} else {
applyingColor = false;
return;
}
}
dest.innerHTML += letter;
},wait);
writingQueue.push(q);
}
}
the code above delays each character while also checking for very specific text such as
<rgb(255,0,0)/ this is red>. the "<" and the ">" are starting and ending signifiers for the colored text, while the "/" is used to mark where the end of the color goes. The text you want to be colored would go before ">" and after "/". and that makes it red! or any color you want. But I noticed a small detail. Whenever you use this, it creates an additional delay. For example if you did something like...
write("Hello, foo! My name is <red/ bar>!");
it would delay the "b" in "bar" longer than it would delay the "f" in "foo".
I think the problem is being caused by
if(applyingColor) {
if(letter != "/") {
return
} else {
applyingColor = false
return
}
}
at around the bottom body of the q = setTimeout(...).
What this code above does is skip over any letters that are not "/" while applyingColor is true, then turns it to false when it eventually reaches that wonderful little marker ("/") and returns one last time.
Honestly I dont know how to fix this. I've been working on this for hours now and my head hurts a lot. I could really use some help!
Oh! I forgot to mention. You can just ignore the "clear" and "delay" parameters. I do not believe they are relevant to my issue. The delay parameter is just the delay between each character. I have it to where it has a default value right now. And clear is just to clear previous text and clear out the writingQueue variable.
The problem in your current code is that as i increases over every iteration, so wait is being recalculated every check.. The first time it's 0 * delay, the second 1 * delay, the third 2 * delay. The solution while implementing only the current code would be to just define wait outside of the for loop.
buggy
let delay = 3;
for(var i = 0 ; i < 93 ; i++) {
let wait = delay * i;
}
let delay = 3;
let wait = 3 * 3;
for(var i = 0 ; i < 93 ; i++) {
}
good
However, there is an even better way to do javascript animations like the one you made, assuming you want them all to take the same amount of time (given your problem, as I understand it, is that the later letters are taking longer <?>) & you're making a webpage
window.requestAnimationFrame(function)
which will wait until the next frame to execute the code, making it always happen with the illusion of no delay to the user
suboptimal
q = setTimeout(() => if(letter == "<") {
/*...*/
faster, very rarely skips frames
q = window.requestAnimationFrame(() => if(letter == "<") {
/*...*/
if you want the letters to change all at once, you can just put the entire
for loop in a function and then call it using window.requestAnimationFrame(function).
window.requestAnimationFrame(colorIn);
function colorIn() {
for(var i = 0 ; i < message.length ; i++) {
let letter = message.charAt(i);
if(letter == "<") {
/*...*/
which will either make the letters changed all at once. if it can't, they'll change after dropping a few frames- though that should only happen if your computer has a virus or you change the color of an absolutely massive string
Related
new to JS here and was wondering if someone could guide me on how I can pause and delete when implementing a typewriter effect in JavaScript.
I've got a function that successfully types out each word in an array, however I would like it to pause after typing each word and backspace or delete the letters before typing the next one.
//counter
var i = 0;
var index = 0;
var texts = ['Salesforce Consultant', 'Developer', 'Writer'];
var speed = 110;
let letter = '';
let currentText = '';
let delay = 25;
function typeWriter() {
//If counter is less than the # of letters in txt, reset array of words
if (i === texts.length) {
i = 0;
}
//Use count to select text to display
currentText = texts[i];
letter = currentText.slice(0, ++index);
document.querySelector("#demo").textContent = letter;
//If letters displayed are the same number of letters in the current text
if (letter.length === currentText.length) {
//Pause before deleting
//Delete letters in word
//Word is done displaying, and reset letters on screen
i++;
index = 0;
}
setTimeout(typeWriter, speed);
}
typeWriter();
<div id="demo"></div>
HTML
<div class="centered">
<div class="intro">
<p>A </p>
<p class ="typing" id="demo"></p>
</div>
</div>
You can do this by introducing a variable that determines how the index will change (+1 or -1). The different delay is just a different argument to setTimeout.
I would also suggest converting some global variables into function parameters: that way they are better (more narrowly) scoped. The change that these variables get, can be managed by what you let setTimeout pass on to the next call.
Here is how that could work:
const texts = ['Salesforce Consultant', 'Developer', 'Writer'];
const speed = 110;
const pause = 800; // <--- the longer delay between text direction changes
function typeWriter(i=0, index=1, direction=1) {
let displayed = texts[i].slice(0, index);
document.querySelector("#demo").textContent = displayed;
if (displayed.length >= texts[i].length) { // start removing after pause
setTimeout(() => typeWriter(i, index-1, -1), pause);
} else if (displayed.length === 0) { // go to next text after pause
setTimeout(() => typeWriter((i+1) % texts.length), pause);
} else { // continue in the current direction
setTimeout(() => typeWriter(i, index+direction, direction), speed);
}
}
typeWriter();
<div id="demo"></div>
I need a function that will print letters one by one but make pauses after ",".
I tried to do it like this but it didn't work :
var span = document.getElementById('text')
function print(string){
var i = 0
var time = 50
var timer = setInterval(function(){
span.innerHTML += string[i++]
if(string[i] == ","){
time = 150
}else{
time = 50
}
},time)
}
I am not sure you can change the time of setTimeinterval once defined. It is better to use a setTimeout. I'm using recursive function to achieve the desired -
function print(string, i){
var time = 50
if(string[i]==",") time = 1000
setTimeout(function(){
span.innerHTML += string[i]
if(i<string.length-1) print(string,++i)
},time)
}
complete example can be found here
First, I don't think setInterval is what you're looking for. Since it seems you just want to print each character with a delay, setTimeout would work best in this situation.
I would break this up so that you can focus on printing a single character at a time, and then you can call that in your main print function for each character in the string.
This is how I would do something like this, using Promises:
const printCharacter = (ch, idx) => new Promise(resolve => {
let time = 50 * idx;
if(ch === ',') {
time = 150 + time * idx;
}
setTimeout(() => {
const span = document.getElementById('text');
span.innerHTML += ch;
resolve();
}, time)
});
const print = async string => {
for(let i = 0; i < string.length; i++) {
await printCharacter(string[i], i);
}
};
There are some bugs that could present itself here, such as the timing for multiple commas present in your base string. This is just my first iteration.
Instead of using an interval, I recommend using a for loop to iterate the string and a Promise to pause as long as desired.
Side note, use textContent instead of innerHTML. The former is like myCar.cangeTires(); myCar.closeDoors(), the latter is like myCar.do('change tires'); myCar.do('close doors');.
let sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
var span = document.getElementById('text');
let print = async string => {
span.textContent = '';
for (let c of string) {
span.textContent += c;
await sleep(c === ',' ? 500 : 50);
}
};
print('my text, and a comma');
<span id="text"></span>
This code takes an integer and returns the amount of 1s that are present.
function countOnes(i) {
let str = i.toString();
let ones = 0;
for(let x = 0; x < i.length; x++) {
if(str.charAt(x) === '1') ones++;
}
return ones;
}
console.log(countOnes(111000));
But it only appears to work in certain executors of JavaScript. If I enter this code into p5.js or Mozilla MDN, I will receive the desired output of 3.
But if I use the console in my browser and some other websites emulating that, 0 will be returned with every given value.
Why is this the case?
you cant loop on i.length, i its still a 'Number' type,
you should loop on "str.length" instead.
you better give more meaningful names... i should be num,
str should be numStr, ones should be counter.
try this:
function countOnes(num) {
var counter = 0;
var numsArray = Array.from((num + ''))
numsArray.forEach(num => {
return (num == 1)? counter++ : ''
})
return counter
}
console.log(countOnes(1110010)); // 4
My Javascript timer is for people with a rubiks cube with generates a scramble (nevermind all this, but just to tell you I'm generating after each solve a new scramble will be generated) and my scrambles do actually have a while (true) statement. So that does crash my script, but it 95/100 times stops just before the script crashes but I don't wanna have any times.
Let me explain a bit more detailed about the problem.
Problem: javascript crashes because my script takes too long to generate a scramble.
Below you have 3 functions I use.
This function generates a scramble with the Fisher-Yates shuffle.
Timer.prototype.generateScramble = function(array) {
for (var i = array.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var temp = array[i];
array[i] = array[j];
array[j] = temp;
}
return array;
};
This function validates the input e.g. I receive an array as the following:
Here I only have to check the first character. That's why I use the seconds [ ] notation. I don't want people get an F with an F2 e.g.
var scr = ["F","R","U","B","L","D","F2","R2","U2","B2","L2","D2","F'","R'","U'","B'","L'","D'"]
Timer.prototype.validateScramble2 = function(array) {
var last = array.length-1;
for (var i = 0; i < array.length-1; i++) {
if (array[i][0] == array[i+1][0]) {
return false;
}
}
for (var i = 0; i < array.length-2; i++) {
if (array[i][0] == array[i+2][0]) {
return false;
}
}
if (array[0][0] == [last][0]) {
return false;
}
return true;
};
The above functions are just waiting to be called. Well in the function below I use them.
Timer.prototype.updateScramble2 = function(scrambleArr, len, type) {
var self = this;
var scramble = '', j, updatedArr = [];
while (updatedArr.length < len) {
j = (Math.floor(Math.random() * scrambleArr.length));
updatedArr.push(scrambleArr[j]);
}
while (!self.validateScramble2(updatedArr)) {
updatedArr = self.generateScramble(updatedArr);
}
for (var i = 0; i < updatedArr.length; i++) {
scramble += updatedArr[i] + ' ';
}
scrambleDiv.innerHTML = scramble;
};
I assume you guys understand it but let me explain it briefly.
The first while-loop adds a random value from the given array(scrambleArr) into a new array called updatedArr.
The next while-loop calls the validateScramble2() function if there isn't in an array F next to an F2.
The for-loop adds them into a new variable added with a whitespace and then later we show the scramble in the div: scrambleDiv.innerHTML = scramble;
What do I need know after all this information?
Well I wanna know why my updateScramble2() functions lets my browser crash every time and what I do wrong and how I should do it.
I'm not entirely sure I understand the question, but from the way your code looks, I think you have an infinite loop going on. It appears as if validateScramble2 always returns false which causes your second loop in updateScramble2 to perpetually run.
I suggest you insert a breakpoint in your code and inspect the values. You could also insert debugger; statements in your code, works the same way. Open dev tools prior to doing these.
A suggestion is instead of using loops, use a timer. This breaks up your loop into asynchronous iterations rather than synchronous. This allows the browser breathing space for other operations. Here's an example of a forEachAsync:
function forEachAsync(array, callback){
var i = 0;
var timer = setInterval(function(){
callback.call(null, array[i]);
if(++i >= array.length) clearInterval(timer);
}, 0);
}
forEachAsync([1,2,4], function(item){
console.log(item);
});
You can take this further and use Promises instead of callbacks.
I am coding a simple JavaScript version of the classic boardgame "Mastermind".
I have some (for sure fundamental) problems, 99% with JavaScript arrays and referencing their values or elements. These issues am I "solving" currently for quite long time, so I decided to ask.
Facts:
my game pegs, and the game board at all is made in HTML table, pegs are implemented like this(a row contains 4 pegs and a td containing the results image):
<td>
<a href="javascript:void(0)"
onClick="changePegColor('0','0'); return false"
onFocus="this.blur()">
<img src="img/void.png" width=22 height=22 name="peg_0_0">
</a>
</td>
my default array declaration looks this (showing both variants tried, none of them worked for me):
var pegsAI = ['pegAI_0', 'pegAI_1', 'pegAI_2', 'pegAI_3'];
var pegsAI = new Array('pegAI_0', 'pegAI_1', 'pegAI_2', 'pegAI_3');
Setting AI's pegs, which is the player going to guess works this way (this is working, no problem with array):
pegsAI[position] = Math.floor((Math.random() * possibleColorsNumber));
And here are my issues:
At the moment of clicking Submit button, there is a check if every peg in a row has a colour this way (this does neither work, nor throws an error in chrome F12):
...
for (var position = 0; position <= 3; position++) {
if (document["peg_" + currentRow + "_" + position].src === "img/void.png") {
alert('Finish color picking!');
return false;
}
}
...
After this check, there is function that should convert players pegs to numbers and save it to an array and there is probably a problem, because it doesn't work (array got undefined values in result):
function convertToNumbers() {
for (var position = 0; position <= 3; position++) { //4 pegs in row, var 'position' declares peg's position
if (document["peg_" + currentRow + "_" + position].src === possibleColors[index] ) { //if a peg has an color (his img's src is equal to an element of array 'possibleColors', which contains possible img's src's), then ->
pegsPlayer[position] = index; // -> then index of this color saves to pegsPlayer[] array
}
}
}
///added for explanation
my function for calculating score:
var goodPegPlayer = [false, false, false, false];
var goodPegAI = [false, false, false, false];
function calcSkore() {
convertToNumbers();
alert("array values" + pegsPlayer[0] + "_" + pegsPlayer[1] + "_" + pegsPlayer[2] + "_" + pegsPlayer[3]);
for (var position = 0; position <= 3; position++) {
goodPegPlayer[position] = false;
goodPegAI[position] = false;
if (pegsPlayer[position] === pegsAI[position]) {
skoreBlack++;
goodPegPlayer[position] = true;
goodPegAI[position] = true;
}
}
for (var position = 0; position <= 3; position++) {
if (goodPegPlayer[position] === false) {
for (var positionAI = 0; positionAI <= 3; positionAI++) {
if ((position !== positionAI) && (goodPegPlayer[position] === false) && (goodPegAI[positionAI] === false)) {
if (pegsPlayer[position] === pegsAI[positionAI]) {
skoreWhite++;
goodPegPlayer[position] = true;
goodPegAI[positionAI] = true;
}
}
}
}
}
resultsSubmit();
}
!! right after using converToNumber() function in this function, an alert() is used to check if the values are correct.
You're not accessing the DOM correctly. Try using the id attribute instead of name to identify your images and update your JavaScript as follows:
for (var position = 0; position <= 3; position++) {
var id = "peg_" + currentRow + "_" + position;
if (document.getElementById(id).src === "img/void.png") {
alert('Finish color picking!');
return false;
}
}
I wrote my own version of Mastermind back in 2004.
The code is a bit outdated, but it still works in modern browsers. I'm not sure if it's helpful to you, but feel free to take a look!
The code is MIT licensed, which means you can freely use it (or parts of it) anywhere you want!
Resources
Demo
Github repository