0

I'm new to JavaScript and I'm trying to create a program that visualizes the bubble sort algorithm.

The idea is that rectangles of different sizes are generated via fillRect(x, y, x, y) and the heights are saved to an array. The bubble-sort function is called and the array sorts itself by comparing heights and the canvas repaints itself every time a height is moved in the array.

The functions I made work as intended. The problem is that I don't know how to create a delay between each repaint. Currently it sorts immediately and you can't see the animation. I figured something like sleep from java should be used? I can't figure it out.

Here is my code so far:

var list = [];
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
ctx.beginPath();

var x = 4;
for (i = 0; i < 38; i++) {
    var random = Math.floor(Math.random() * (400 - 10) + 10);
    bar = ctx.fillRect(x, 4, 20, random);
    list.push(random);
    x += 21;
}

function bubbleSort() {
    for (let i = 0; i < list.length - 1; i++) {
        for (let j = 0; j < list.length - i - 1; j++) {
            if (list[j] > list[j + 1]) {
                let temp = list[j];
                list[j] = list[j + 1];
                list[j + 1] = temp;
                ctx.clearRect(0, 0, c.width, c.height);
                repaint(list);
            }
        }
    }
}

function repaint(list) {
    ctx.clearRect(0, 0, c.width, c.height);
    let k = 4;
    for (let i = 0; i < 38; i++) {
        ctx.fillRect(k, 4, 20, list[i]);
        k += 21;
    }
}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>bubble-sort</title>
</head>
<body>
    
    <canvas id="myCanvas" width="805px" height="550px" style="border:2px solid #000000; margin-left:100px; margin-top: 100px; background: gray"></canvas>
    <br><br>
    <button type="button" onclick="bubbleSort()" style="margin-left: 100px">bubble sort</button>
    <script src="bubble.js"></script>
</body>
</html>
btror
  • 113
  • 9
  • Use [`setInterval()`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setInterval). For that to work you have to restructure the two nested for loops though. –  Sep 23 '20 at 06:33
  • https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame – mplungjan Sep 23 '20 at 06:34

1 Answers1

0

You can make a delay function like this:

const delay = ms => new Promise(resolve => setTimeout(resolve, ms));

And then change your bubbleSort function to async, then await several milliseconds after every iteration:

var list = [];
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
ctx.beginPath();

const delay = ms => new Promise(resolve => setTimeout(resolve, ms));

var x = 4;
for (i = 0; i < 38; i++) {
    var random = Math.floor(Math.random() * (400 - 10) + 10);
    bar = ctx.fillRect(x, 4, 20, random);
    list.push(random);
    x += 21;
}

async function bubbleSort() {
    for (let i = 0; i < list.length - 1; i++) {
        for (let j = 0; j < list.length - i - 1; j++) {
            if (list[j] > list[j + 1]) {
                let temp = list[j];
                list[j] = list[j + 1];
                list[j + 1] = temp;
                ctx.clearRect(0, 0, c.width, c.height);
                repaint(list);
                await delay(200);
            }
        }
    }
}

function repaint(list) {
    ctx.clearRect(0, 0, c.width, c.height);
    let k = 4;
    for (let i = 0; i < 38; i++) {
        ctx.fillRect(k, 4, 20, list[i]);
        k += 21;
    }
}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>bubble-sort</title>
</head>
<body>
    
    <canvas id="myCanvas" width="805px" height="550px" style="border:2px solid #000000; margin-left:100px; margin-top: 100px; background: gray"></canvas>
    <br><br>
    <button type="button" onclick="bubbleSort()" style="margin-left: 100px">bubble sort</button>
    <script src="bubble.js"></script>
</body>
</html>

Here's a rather simple way to do it, if you don't want to use async await etc.


Just an FYI, if you can not change the function to async, you can still achieve it by blocking the threading like this, but it's probably gonna crank your CPU to 100%, which is not recommended:

function delay(ms) {
  const target = performance.now() + ms;
  while(performance.now() < target) {
  }
}
Hao Wu
  • 17,573
  • 6
  • 28
  • 60