javascript - 使用 Canvas 动画化 JS 中的排序算法

标签 javascript html algorithm sorting canvas

为了好玩,我正在尝试创建不同排序算法的可视化,但我遇到了 Canvas 动画的问题。

我假设我只能在 sorter 方法中调用绘制函数,但这会导致浏览器锁定,直到数组完全排序,然后绘制一些中间框架。

我将如何在排序方法中设置动画?以下是我到目前为止的代码,我不会运行此代码段,因为它会使选项卡挂起几秒钟。

N = 250; // Array Size
XYs = 5; // Element Visual Size
Xp = 1; // Start Pos X
Yp = 1; // Start Pos Y
var canvas;
var l = Array.apply(null, {
  length: N
}).map(Number.call, Number);

Array.prototype.shuffle = function() {
  var i = this.length,
    j, temp;
  if (i == 0) return this;
  while (--i) {
    j = Math.floor(Math.random() * (i + 1));
    temp = this[i];
    this[i] = this[j];
    this[j] = temp;
  }
  return this;
}

function map_range(x, in_min, in_max, out_min, out_max) {
  return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}

function rainbow(x) {
  var m = map_range(x, 0, N, 0, 359);
  return 'hsl(' + m + ',100%,50%)';
}

function init() {
  canvas = document.getElementById('canvas');
  l.shuffle();
  draw();
  bubbleSort(l);
}

function draw() {
  if (canvas.getContext) {
    var ctx = canvas.getContext('2d');
    for (var i = 0; i < l.length; i++) {
      ctx.fillStyle = rainbow(l[i]);
      ctx.fillRect((Xp * i) * XYs, Yp * XYs, XYs, XYs);
    }
  }
}

function bubbleSort(a) {
  var swapped;
  do {
    swapped = false;
    for (var i = 0; i < a.length - 1; i++) {
      if (a[i] > a[i + 1]) {
        var temp = a[i];
        a[i] = a[i + 1];
        a[i + 1] = temp;
        swapped = true;
        draw();
        setTimeout(function() {}, 10);
      }
    }
  } while (swapped);
}
<html>

<body onload="init();">
  <canvas id="canvas" width="1500" height="1500"></canvas>
</body>

</html>

最佳答案

一个解决方案是 ES6 Generator function*连同其 yield 声明。

这允许您暂停一个函数并稍后在暂停的地方重新启动它:

N = 100; // Array Size
XYs = 5; // Element Visual Size
Xp = 1; // Start Pos X
Yp = 1; // Start Pos Y
var canvas;
var l = Array.apply(null, {
  length: N
}).map(Number.call, Number);

Array.prototype.shuffle = function() {
  var i = this.length,
    j, temp;
  if (i == 0) return this;
  while (--i) {
    j = Math.floor(Math.random() * (i + 1));
    temp = this[i];
    this[i] = this[j];
    this[j] = temp;
  }
  return this;
}

function map_range(x, in_min, in_max, out_min, out_max) {
  return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}

function rainbow(x) {
  var m = map_range(x, 0, N, 0, 359);
  return 'hsl(' + m + ',100%,50%)';
}

function init() {
  canvas = document.getElementById('canvas');
  l.shuffle();
  var sort = bubbleSort(l);
  // an anim function triggered every 60th of a second
  function anim(){
    requestAnimationFrame(anim);
    draw();
    sort.next(); // call next iteration of the bubbleSort function
  }
  anim();
}

function draw() {
  if (canvas.getContext) {
    var ctx = canvas.getContext('2d');
    for (var i = 0; i < l.length; i++) {
      ctx.fillStyle = rainbow(l[i]);
      ctx.fillRect((Xp * i) * XYs, Yp * XYs, XYs, XYs);
    }
  }
}

function* bubbleSort(a) { // * is magic
  var swapped;
  do {
    swapped = false;
    for (var i = 0; i < a.length - 1; i++) {
      if (a[i] > a[i + 1]) {
        var temp = a[i];
        a[i] = a[i + 1];
        a[i + 1] = temp;
        swapped = true;
        yield swapped; // pause here
      }
    }
  } while (swapped);
}
init();
<canvas id="canvas" width="500" height="20">

您现在还可以使用 async/await为了实现相同目标的更具可读性的代码:

const N = 100; // Array Size
const XYs = 5; // Element Visual Size
const Xp = 1; // Start Pos X
const Yp = 1; // Start Pos Y
let canvas;
const l = Array.from({ length: N }, (_,i) => i);

Array.prototype.shuffle = function() {
  let i = this.length;
  if (i == 0) return this;
  while (--i) {
    const j = Math.floor(Math.random() * (i + 1));
    const temp = this[i];
    this[i] = this[j];
    this[j] = temp;
  }
  return this;
}

function map_range(x, in_min, in_max, out_min, out_max) {
  return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}

function rainbow(x) {
  const m = map_range(x, 0, N, 0, 359);
  return 'hsl(' + m + ',100%,50%)';
}

function init() {
  canvas = document.getElementById('canvas');
  l.shuffle();
  bubbleSort(l);
}

function draw() {
  if (canvas.getContext) {
    const ctx = canvas.getContext('2d');
    for (let i = 0; i < l.length; i++) {
      ctx.fillStyle = rainbow(l[i]);
      ctx.fillRect((Xp * i) * XYs, Yp * XYs, XYs, XYs);
    }
  }
}
function drawNextFrame() {
  return new Promise((res) => requestAnimationFrame(res))
    .then(draw);
}
async function bubbleSort(a) { // async is magic
  let swapped;
  do {
    swapped = false;
    for (let i = 0; i < a.length - 1; i++) {
      if (a[i] > a[i + 1]) {
        const temp = a[i];
        a[i] = a[i + 1];
        a[i + 1] = temp;
        swapped = true;
        await drawNextFrame(); // pause here
      }
    }
  } while (swapped);
}
init();
<canvas id="canvas" width="500" height="20">

关于javascript - 使用 Canvas 动画化 JS 中的排序算法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46951400/

相关文章:

python - 获取给定顶点坐标的多边形轮廓和多边形掩码的坐标

javascript - 在数组中查找重复对象的有效方法

c# - LIKE 的 SQL 语法帮助

css - 如何垂直居中有序列表导航

algorithm - 用于威胁分析的神经网络

algorithm - 找到最大簇的最小值?

javascript - 使用 D3 动态生成设计好的有机形状?

javascript - 如何在没有运算符的情况下将 lodash _ 与数组而不是多个参数一起使用

javascript - 根据 api 响应 react 渲染 Chart.js

javascript - 未定义 onclick 事件的函数 -HTML5 canvas