尝试允许用户通过按下按钮逐步使用 JS 和 P5 算法的 for 循环

Trying to allow the user to step through a for loop for an algorithm using JS and P5 via a button press

我正在尝试弄清楚如何让用户通过单击 P5 和 JS 上的按钮逐步执行算法。我的其他代码采用一些文本并显示一些在我下面提到的算法中使用的自定义字符单元格。我希望用户单击下一步按钮并让它逐步完成并在执行每个步骤之前等待用户输入。

下面是一段代码

async function straightforward(patternCells, textCells){

  const timeout = async ms => new Promise(res => setTimeout(res, ms));  
  let nextStep = false;

  forwardButton = createButton("->",0,0);
  forwardButton.position(confirmButton.x + backButton.width, 400);
  forwardButton.mousePressed(() => next = true)

  //Do some set up and display the button
  for (var i = 0; i < textLen; i++) {
    var j = 0;
    await waitButtonNext(); 
    //algorithm runs here
  }
  async function waitButtonNext() {
    while (nextStep === false) await timeout(1); // pause script but avoid browser to freeze ;)
    nextStep = false; // reset var
  } 

chrome 上的控制台也没有错误。

您可以这样做的一种方法是将您想要执行一次的脚本放入一个单独的函数中,您可以在按下按钮时调用该函数,同时在单独的变量中跟踪已完成的迭代

有很多方法可以做到这一点。一种方法是为每个步骤创建一个函数数组,并在按下按钮时一次执行一个。

例如:

const steps = [
  () => {
    text("step 1; click to go to step 2", 10, 50);
  },
  () => {
    text("step 2; click to go to step 3", 10, 50);
  },
  () => {
    text("step 3; click to go to end", 10, 50);
  },
];

const defaultAction = () => text("that's it", 10, 50);

function setup() {
  createCanvas(300, 100);
  textSize(20);
  noLoop();
}

function draw() {
  text("click to start", 10, 50);
}

function mousePressed() {
  clear();
  (steps.shift() || defaultAction)();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.1/p5.js"></script>

这个例子有点做作,因为每一步都没有动画。一个更现实的例子是动画。

一种继续避免 draw 函数中令人讨厌的 if/else 链的方法(尽管在紧要关头确实有效)是替换 draw 每一步并可选择根据需要操纵 noLoop()loop() 来启动和停止动画。

const sleep = ms => new Promise(r => setTimeout(r, ms));

let allowClick = true;

const steps = [
  () => {
    let y = 0;
    draw = () => {
      clear();
      text("click to start step 2", 50, sin(y) * 20 + 50);
      y += 0.1;
    };
    loop();
  },
  async () => {
    allowClick = false;
    let y = 20;
    let n = 4;
    draw = () => {
      clear();
      text(`pausing for ${n} seconds...`, 50, y += 0.2);
    };
    setInterval(() => --n, 1000); // not precise but OK for this
    await sleep(4000);
    allowClick = true;
    let x = 0;
    y = 0;
    draw = () => {
      clear();
      text(
        "click to end",
        cos(x) * 20 + 50,
        sin(y) * 20 + 50
      );
      x += 0.21;
      y += 0.13;
    };
  },
  // ...
];

const defaultAction = () => {
  draw = () => {};
  noLoop();
  clear();
  text("that's it", 50, 50);
};

function setup() {
  createCanvas(300, 100);
  textSize(20);
  noLoop();
}

function draw() {
  text("click to start", 50, 50);
}

function mousePressed() {
  if (allowClick) {
    (steps.shift() || defaultAction)();
  }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.1/p5.js"></script>

更进一步,假设您想重复一个步骤。这个设计很容易。与其将每个函数永久地从操作数组中移出,不如保留一个索引来引用应该采取的操作。响应按钮单击,更改索引并为该行为调用相应的函数。这是在 p5.js 中实现“场景”的一种方法。在某些情况下,使用每个状态具有 clearly-named 个键的对象可能是有意义的,例如{titleScreen: () => ..., endingScreen: () => {...}} 等。请参阅 以了解对此的完整处理。

您还可以“旋转”行为数组以创建循环重复,例如:

function mousePressed() {
  const action = steps.shift();
  steps.push(action);
  action();
}

如果您愿意,可以将所有这些场景或步骤函数存储在单独的外部文件中,使代码易于维护。