代码之家  ›  专栏  ›  技术社区  ›  pixlboy

如何使用RequestAnimationFrame和SetInterval?

  •  1
  • pixlboy  · 技术社区  · 7 年前

    请参见以下代码片段。

    它通过单击按钮创建一个加载程序。但动画并不流畅。

    我最近读过 requestAnimationFrame 能完成这项工作的功能。但是我怎么用它来代替呢 setInterval 总之,因为没有办法在 请求动画框架 功能。

    它能和 设定间隔 ?

    let idx = 1;
    const timetoEnd = 5000;
    
    function ProgressBar(width){
      this.width = width || 0;
      this.id = `pBar-${idx++}`;
      this.create = () => {
        let pBar = document.createElement('div');
        pBar.id = this.id;
        pBar.className = `p-bar`;
        pBar.innerHTML = `<div class="loader"></div>`;
        return pBar;
      };
      this.animator = () => {
          let element = document.querySelector(`#${(this.id)} div`);
          if(this.width < 100){
            this.width++;
            element.style.width = `${this.width}%`;
          } else {
            clearInterval(this.interval);
          }
      };
      this.animate = () => {
        this.interval = setInterval(this.animator, timetoEnd/100);
      }
    }
    
    function addLoader (){
      let bar1 = new ProgressBar(40);
      let container = document.querySelector("#container");
      container.appendChild(bar1.create());
      bar1.animate();
    }
    .p-bar{
      width: 400px;
      height: 20px;
      background: 1px solid #ccc;
      margin: 10px;
      overflow:hidden;
      border-radius: 4px;
    }
    .p-bar .loader{
      width: 0;
      background: #1565C0;
      height: 100%;
    }
    <input type="button" value="Add loader" onclick="addLoader()" />
    <div id="container"></div>
    1 回复  |  直到 7 年前
        1
  •  1
  •   Clément Prévost    7 年前

    你是对的, requestAnimationFrame 是执行动画时避免用户界面阻塞的推荐方法。

    您可以记住开始时的绝对开始时间,而不是在每一帧尝试这样做。然后,只需要根据开始时间和当前时间之间的增量时间计算宽度。

    也, document.querySelector 被认为是一个相对“沉重”的行动,所以我补充说 this.element 避免在每一帧上进行。

    以下是计算新宽度的方法: ((100 - this.startWidth) / timetoEnd) * deltaT + this.startWidth

    1. 100 - this.startWidth 是我们必须设置动画的总宽度
    2. (100 - this.startWidth) / timetoEnd 是每秒必须增加多少宽度(1)
    3. ((100 - this.startWidth) / timetoEnd) * deltaT 我们要加多少宽度(1)
    4. 我们只需要改变整个事情 this.startWidth 像素有帧的宽度

    还请注意,其中一些计算是常量,不必在每一帧上计算,我将其作为练习留在这里:)

    下面是您稍微修改过的代码:

    let idx = 1;
    const timetoEnd = 5000;
    
    function ProgressBar(startWidth){
      this.startWidth = startWidth || 0;
      this.id = `pBar-${idx++}`;
      this.create = () => {
        let pBar = document.createElement('div');
        pBar.id = this.id;
        pBar.className = `p-bar`;
        pBar.innerHTML = `<div class="loader"></div>`;
        return pBar;
      };
      this.animator = () => {
          const deltaT = Math.min(new Date().getTime() - this.start, timetoEnd);
          if(deltaT < timetoEnd){
            const width = ((100 - this.startWidth) / timetoEnd) * deltaT + this.startWidth;
            this.element.style.width = `${width}%`;
            requestAnimationFrame(this.animator.bind(this))
          }
      };
      this.animate = () => {
        this.element = document.querySelector(`#${(this.id)} div`);
        this.start = new Date().getTime();
        this.animator();
      }
    }
    
    function addLoader (){
      let bar1 = new ProgressBar(40);
      let container = document.querySelector("#container");
      container.appendChild(bar1.create());
      bar1.animate();
    }
    .p-bar{
      width: 400px;
      height: 20px;
      background: 1px solid #ccc;
      margin: 10px;
      overflow:hidden;
      border-radius: 4px;
    }
    .p-bar .loader{
      width: 0;
      background: #1565C0;
      height: 100%;
    }
    <input type="button" value="Add loader" onclick="addLoader()" />
    <div id="container"></div>