Reputation: 207
I am using svelte to build a roulette board. My idea is to have a function roll()
that, when clicked, will generate a random number between 50-100. The roll function will also iterate over each number of each roulette square and increase its font size to 20px ( to give off the impression a "ball" is rolling over that number ). However, my current program increases the font size of ALL numbers after the for loop is completed. I want each number to return back to its original font size before the next number's font size increase. I looked up solutions to this problem, and found that some have used `setTimeout1, but that solution never worked for me.
Here is my code
let board = [
{num: 35, color: "black"},
{num: 3, color: "red"},
{num: 26, color: "black"},
{num: 0, color: "green"},
{num: 32, color: "black"},
{num: 15, color: "red"},
{num: 19, color: "black"},
{num: 4, color: "red"},
{num: 21, color: "black"},
{num: 2, color: "red"},
{num: 25, color: "black"},
{num: 17, color: "red"},
{num: 34, color: "black"},
{num: 6, color: "red"},
{num: 27, color: "black"},
{num: 13, color: "red"},
{num: 36, color: "black"},
{num: 11, color: "red"},
{num: 30, color: "black"},
{num: 8, color: "red"},
{num: 23, color: "black"},
{num: 10, color: "red"},
{num: 5, color: "black"},
{num: 24, color: "red"},
{num: 16, color: "black"},
{num: 33, color: "red"},
{num: 1, color: "black"},
{num: 20, color: "red"},
{num: 14, color: "black"},
{num: 31, color: "red"},
{num: 9, color: "black"},
{num: 22, color: "red"},
{num: 18, color: "black"},
{num: 29, color: "red"},
{num: 9, color: "black"},
{num: 7, color: "red"},
{num: 28, color: "black"},
{num: 12, color: "red"},
]
function roll(){
let square = document.getElementById("square");
for(let i = 0; i < board.length; i++){
let number = document.getElementById("number"+[i]);
number.style.fontSize = "20px"
}
}
<main>
<h1>Roulette!</h1>
<div style="display:flex; flex-direction: row;">
{#each board as c, i}
<div class="{c.color}" id="square" style="flex:1" >
<p id="number{i++}">{c.num}</p>
</div>
{/each}
</div>
<button on:click={roll}>Roll</button>
</main>
// for non-svelte users, this is simply creating a roulette board.
I am sure there is a better way to go about this, but I do not want to use jQuery or any libraries to do this.
The effect I am going for is a blinking iteration from one number to the next, but I am not sure how to make the DOM update after each iteration.
Upvotes: 0
Views: 920
Reputation: 7320
You can use tick
for this, where tick
waits for the DOM to update.
I added the highlighted
property which when set makes the number bold.
I also added the sleep
function so you can introduce a delay easily.
Code
<script>
import { tick } from "svelte";
let board = [
{num: 35, color: "black"},
{num: 3, color: "red"},
{num: 26, color: "black"},
{num: 0, color: "green"},
{num: 32, color: "black"},
{num: 15, color: "red"},
{num: 19, color: "black"},
{num: 4, color: "red"},
];
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function roll() {
for (let i = 0; i < board.length; i++) {
board[i].highlighted = true;
await tick();
await sleep(500);
board[i].highlighted = false;
await tick();
await sleep(500);
}
}
</script>
<main>
<h1>Roulette!</h1>
<div style="display:flex; flex-direction: row;">
{#each board as c, i}
<div class="{c.color}" id="square" style="flex:1">
<p id="number{i++}" style="font-weight: { c.highlighted ? 'bold' : 'normal' };">{c.num}</p>
</div>
{/each}
</div>
<button on:click={roll}>Roll</button>
</main>
Notice the style tag, you can replace it with class:highlighted={c.highlighted}
, then add the .highlighted
class to your css.
Generally with svelte, you can avoid touching the DOM directly in most cases.
Thinking about data is much easier than thinking about the DOM.
So in this case, setting a highlighted property of a board is easier than thinking about the necessary steps required to highlight a board in the DOM.
Info: The above code does not work on the svelte repl because of the guard loop, an iteration must not exceed 100ms in the svelte repl. On CodeSandbox, it works fine
Upvotes: 0
Reputation: 65806
Using a loop when pauses are needed is a common mistake people make. Loops run incredibly fast. What you want is timings that (comparably) are much slower. So, don't use a loop at all and instead just use a timer that either calls itself recursively or an interval timer.
I don't know Svelt, so here's a modified example:
// Get a reference to all the "numbers" just once
// not each timme the function is called
let numbers = document.querySelectorAll("div");
let timer = null; // Will hold reference to timer
function roll(){
let wheelCounter = 0; // Will keep track of how many times around the wheel
let counter = 0; // Will keep track of which number we're on
// Start a timer that will call its callback at regular intervals
timer = setInterval(function(){
if(wheelCounter < 3){
// Remove the style from the last number (if there was a last number)
if(counter > 0){
numbers[counter - 1].classList.remove("active");
}
// Check to see if we should keep going
if(counter < numbers.length){
numbers[counter++].classList.add("active"); // Add the needed style
} else {
counter = 0; // Start iterating numbers over again
wheelCounter++ // Adjust how many times around the wheel we've been
}
} else {
clearInterval(timer); // Cancel the timer
}
}, 200); // <-- Note the second argument to setInterval is a number of milliseconds to wait before calling the function again.
}
roll();
div {
width:50px;
height:50px;
border:2px solid #e0e0e0;
margin:3px;
text-align:center;
display:inline-block;
padding-top:20px;
}
.active { background-color: #ff0; font-weight:bold; }
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
<div>6</div>
<div>7</div>
<div>8</div>
<div>9</div>
<div>10</div>
Upvotes: 2