高级前端工程师面试题 -- 实现一个红绿灯

846 阅读1分钟

题目:

实现一个红绿灯。

页面上存在一个div, #TrafficLight. 背景颜色默认颜色是红色,2s后变成绿色, 再2s后变成黄色,再1s后变成红色,再2s后变成绿色.... 如此一致循环。

stateDiagram-v2
红色2s --> 绿色2s
绿色2s --> 黄色1s
黄色1s -->红色2s

方法一 动画的标准实现方法

实现思路:

  1. 使用requestAnimationFrame
  2. 获取到当前动画的进度。获取方式: 动画5s一次循环。当前时间和动画开始的时间得差为动画的执行的时间。与5s区余数,得到一个0-5内的数字。
  3. 根据进度,绘制对应颜色到按钮上

  //需要展示动画的dom
   var dom = document.getElementById('TrafficLight');
   //动画开始时间
   let startTime = new Date().getTime();

   //动画总方法
   function Animate(){
       let now = new Date().getTime();
       let animateTime = now - startTime;

       let process = getProcess(animateTime);

       draw(process)

       window.requestAnimationFrame(Animate)
   }
   //动画进程
   function getProcess(animateTime){
     return  (animateTime/1000) % 5 
   }
   //绘制到页面上
   function draw(process){
     if(process < 2){
         //1-2s显示红色
         setBg('red')
     }else if(process < 4){
         //2-4s显示绿色
         setBg('green')
     }else {
         //4-5s显示黄色
         setBg('yellow')
     }
   }

   function setBg(bgColor){
     dom.style.backgroundColor = bgColor
   }
   
   //执行
   Animate()

为什么使用这个方法

  1. 代码可扩展性强。比如今后需要改为6s一个循环,只需要改getProcess和draw方法即可。假如需要更换颜色,改draw方法即可。代码的维护成本低。
  2. 代码复用性强。 Animate方法基本没有任何业务代码,今后有业务代码,可以直接将Animate作为公用方法,适用任何的动画。
  3. 逐帧绘制,动画稳定性好,无卡顿。
  4. 动画可以暂停,也可以取消。

方法二 使用es6的awit

实现思路: 利用异步函数,等待ns后,绘制出相应的状态。最后,本函数内调用本函数即可实现循环效果。


//需要展示动画的dom
    var dom = document.getElementById("TrafficLight");
    //设置颜色
    function setBg(bgColor) {
      dom.style.backgroundColor = bgColor;
    }
    
    //动画入口
    async function Animate() {
      await sleep(2000).then(() => {
        setBg("green");
      });

      await sleep(2000).then(() => {
        setBg("yellow");
      });

      await sleep(1000).then(() => {
        setBg("red");
      });
      Animate();
    }
    //睡眠函数
    function sleep(time) {
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          resolve();
        }, time);
      });
    }
   //启动动画
   Animate();

分析:

  1. 很难实现暂停和取消。假如在vue的组件内使用,组件周期结束后,很难及时销毁动画。