Javascript 同时有时间延迟

Javascript while with time delay

我的目标我想运行循环,在n ms(例如:200ms)时间间隔内逐步递减一个全局变量。

提前致谢!

我已经尝试过的

我尝试使用 ascy await。但是结合 css transition i 运行 在一个无限循环中(In codepen.io)。但是在 SO 中,如果你一直按向上箭头,你会发现它开始时 运行 并不顺利。

const procentage = document.querySelector(".procentage");
const green = engine.querySelector(".green");
let number = 0;
let decrementing  = false;

window.addEventListener('keydown', (e) => {
  e = e || window.event;
  e.preventDefault();
  
  if (e.keyCode == '38') {
    console.log("accelerate");     
    actionHandler( number++ );
    decrementing = false;
    downLoop();
  }
});


function actionHandler(num) {
  procentage.innerHTML = num;
  const str = num + "%"
  green.style.width = str;
  procentage.innerHTML = str;    
}

window.addEventListener('keyup', (e) => {
  e = e || window.event;
  e.preventDefault();
  
  if (e.keyCode == '38') {
    console.log("decelerate");
    decrementing = true;
    downLoop();
  }
});


async function downLoop() {
  if (! decrementing) {
    return false
  };
  
  const timer = ms => new Promise(res => setTimeout(res, ms));
   
  while (number > 1) {
    // how to decrement ever 200ms???
    actionHandler( number-- );      
    await timer(200)
  }

}
#engine {
  background-color:black;
  height: 50px;
  position: relative;
}

p {
  text-align: center;
}

.green {
  background:green;
  height: 50px;
  width:0%;
  transition: width 0.2s;
  text-align:center;
}
.procentage {
  position:absolute;
  top: 50%;
  left: 50%;
  transform: translate(0%,-50%);
  color: white;
  fon-weight: bold;
  font-size:28px;
}
<div id="engine">
  <div><span class="procentage">0</span></div>
  <div class="green"></div>    
</div>
<p>press arrow Up</p>

从以上评论...

"Instead of incrementing each time the number value push a new async timer function, set to 200 msec delay but not immediately triggered, into an array. Create an async generator from it and iterate over the latter via the for-await...of statement where one could decrement number again." – Peter Seliger

"@PeterSeliger Hi Peter! Thank you for your comment. Can you make a small example please?" – Maik Lowrey

这里是请求的演示。

function createWait(delay) {
  return async function wait () {
    let settle;
    const promise = new Promise((resolve) => { settle = resolve;});
    setTimeout(settle, delay, { delay, state: 'ok' });
    return promise;
  };
}
async function* getWaitIterables(list) {
  let wait;
  while (wait = list.shift()) {

    yield wait();
  }
}

// demo for ...
// - creating an async `wait` function
//   or a list of such kind.
// - creating an async generator from
//   a list of async `wait` functions.
// - iterating an async generator of
//   async `wait` functions.

const waitingList = [ //  const waitingList = [];
  2000,               //  waitingList.push(createWait(2000));
  1000,               //  waitingList.push(createWait(1000));
  3000,               //  waitingList.push(createWait(3000));
].map(createWait);    //  - The OP of cause needs to push into.

let number = 3;       //  - The incremented `number` value e.g. ... 3.

(async () => {
  for await (const { delay, state } of getWaitIterables(waitingList)) {
    --number;
    console.log({ number, delay, state });
  }
})();

console.log('... running ...', { number });
.as-console-wrapper { min-height: 100%!important; top: 0; }

每当你制作动画时,你不应该依赖 setIntervalsetTimeout,因为这意味着你将更新“X 毫秒后的某处”,这通常会在屏幕重绘,因此会导致动画卡顿。

相反,您应该使用 RequestAnimationFrame,它会在每次重绘之前进行计算。所以如果你有一个帧率为 60 Hz 的监视器,这意味着你将每秒重绘 60 次。对于每次重绘,检查自上次更新以来是否经过了足够的时间(下面的shouldTriggerUpdate()),然后检查是否应该更新数字。

我还添加了 class KeyHandler 来跟踪按下了哪些键。

我最后草率地添加了一个递减作为 if 语句的“else”。当你想要设置更多要按下的键时,你会在到达那里时想出一些办法。

您不应使用 KeyboardEvent.keyCode,而应使用 KeyboardEvent.code

const procentage = document.querySelector(".procentage");
const green = engine.querySelector(".green");
let number = 0;
let speed = 200      // ms
let lastUpdated = 0; // ms
let animationId = 0; // use later on to pause the animation

class KeyHandler {
  ArrowLeft  = false
  ArrowUp    = false
  ArrowRight = false
  ArrowDown  = false

  #setKey(code, value) { // private method
    if (typeof this[code] != undefined) {
      this[code] = value;
    }
  }

  set pressedKey(code) {
    this.#setKey(code, true);
  }

  set releasedKey(code) {
    this.#setKey(code, false);
  }
}

let keyHandler = new KeyHandler();

window.addEventListener('keydown', (e) => {
  e = e || window.event;
  e.preventDefault();
  keyHandler.pressedKey = e.code;
});

window.addEventListener('keyup', (e) => {
  e.preventDefault();
  keyHandler.releasedKey = e.code
});


function actionHandler(num) {
  const str = num + "%"
  green.style.width = str;
  procentage.innerHTML = str;
}

function shouldTriggerUpdate(timeInMillis) {
  let difference = timeInMillis - lastUpdated;

  return difference >= speed;
}

function planeAnimation() {
  let timeInMillis = new Date().getTime();
  
  if (shouldTriggerUpdate(timeInMillis)) {
    lastUpdated = timeInMillis;
    
    if (keyHandler.ArrowUp) {
      actionHandler(++number)
    } else if (number > 0) {
      actionHandler(--number)
    }
  }

  animationId = requestAnimationFrame(planeAnimation)
}

animationId = requestAnimationFrame(planeAnimation);
#engine {
  background-color: black;
  height: 50px;
  position: relative;
}

p {
  text-align: center;
}

.green {
  background: green;
  height: 50px;
  width: 0%;
  transition: width 0.2s;
  text-align: center;
}

.procentage {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(0%, -50%);
  color: white;
  fon-weight: bold;
  font-size: 28px;
}
<div id="engine">
  <div><span class="procentage">0</span></div>
  <div class="green"></div>
</div>
<p>press arrow up</p>