js实现交通灯(两种方案)

简介

刚在抖音上刷到一个面试题,说实现交通灯的方案,我一开始想到的是通过定时器去实现,没想到他提到了一个问询的方式去实现,借此记录下来,本文介绍了两种方案去实现交通灯以及对应的倒计时。废话不多说,上代码

html+css

html代码如下:

<!DOCTYPE html>  
<html lang="en">  
<head>  
<meta charset="UTF-8">  
<meta name="viewport" content="width=device-width, initial-scale=1.0">  
<title>Traffic Light</title>  
</head>  
<body>  
  
<div class="traffic-light-container">   
  <div id="green" class="traffic-light"></div>  
  <div id="yellow" class="traffic-light"></div>  
  <div id="red" class="traffic-light"></div>  
</div>  
  
<div class="countdown">60</div>  
<script src="./index.js"></script>
<!-- <script src="./index1.js"></script> -->
</body>  
</html>

css如下:

.traffic-light {  
  width: 150px;  
  height: 150px;  
  margin: 10px;  
  border: 1px solid black;  
  display: inline-block;  
  border-radius: 50%;
}  
.red { background-color: red; }  
.yellow { background-color: yellow; }  
.green { background-color: green; }  
.countdown {  
  font-size: 24px;  
  margin-top: 20px;  
}  

定时器

index.js代码如下:

const redLight = document.getElementById('red');  
const yellowLight = document.getElementById('yellow');  
const greenLight = document.getElementById('green');  
const countdownElement = document.querySelector('.countdown');  
  
let currentTime = 5; // 初始倒计时时间  
let currentLight = 'green'; // 初始交通灯颜色  
greenLight.classList.add('green');  
// 定义交通灯颜色变化的时间  
const lightDurations = {  
  green: 5000, // 绿灯时间(毫秒)  
  yellow: 2000, // 黄灯时间(毫秒)  
  red: 7000 // 红灯时间(毫秒)  
};  
  
// 更新倒计时的函数  
function updateCountdown() {  
  if (currentTime > 0) {  
    countdownElement.textContent = currentTime;  
    currentTime--;  
    setTimeout(updateCountdown, 1000);  
  } else {  
    changeLight();  
  }  
}  
  
// 切换交通灯颜色的函数  
function changeLight() {  
  switch (currentLight) {  
    case 'green':  
      greenLight.classList.remove('green');  
      yellowLight.classList.add('yellow');  
      currentLight = 'yellow';  
      currentTime = lightDurations.yellow / 1000; // 转换为秒  
      break;  
    case 'yellow':  
      yellowLight.classList.remove('yellow');  
      redLight.classList.add('red');  
      currentLight = 'red';  
      currentTime = lightDurations.red / 1000; // 转换为秒  
      break;  
    case 'red':  
      redLight.classList.remove('red');  
      greenLight.classList.add('green');  
      currentLight = 'green';  
      currentTime = lightDurations.green / 1000; // 转换为秒  
      break;  
  }  
  updateCountdown(); // 重置倒计时  
}  
  
// 初始化倒计时  
updateCountdown();  

问询

index1.js代码如下:

class TrafficLight {
  constructor(lights) {
    this._lights = lights;
    this._currentIndex = 0; // 记录当前灯下的索引
    this._time = Date.now(); // 记录当前时间
  }
  _update() {
    let disTime = this._disTime();
    // 计算交通灯的展示时间总和
    let total = this._lights.reduce((acc, cur) => {
      return acc + cur.lasts
    }, 0);

    // 代表一个完整的循环周期的开始时间
    this._time += total * Math.floor(disTime / total) * 1000
    disTime = disTime % total; // 从上一个循环周期的开始到现在,还有多少时间没有用完

    while (1) {
      // 在每次循环中,disTime 被减去当前交通灯颜色的持续时间(this.currentLight.lasts)。
      // 如果 disTime 仍然大于或等于 0,那么说明当前交通灯颜色还没有结束,
      // 方法将 _time 向前推进当前交通灯颜色的持续时间,并更新 _currentIndex 为下一个交通灯颜色的索引
      // 一旦 disTime 小于 0,说明当前交通灯颜色已经结束,循环终止。此时,_currentIndex 已经指向了下一个应该显示的交通灯颜色。
      disTime -= this.currentLight.lasts;
      if (disTime < 0) break
      else {
        this._time += this.currentLight.lasts * 1000
        this._currentIndex = (this._currentIndex + 1) % this._lights.length;
        // 将当前交通灯颜色的索引向前移动一个位置。如果当前已经是最后一个交通灯颜色,索引将回绕到数组的第一个元素。
        // 这样,交通灯颜色就能按照数组中的顺序循环切换
      }
    }
  }
  // 访问器
  get currentLight() {
    return this._lights[this._currentIndex]
  }
  // 计算从上次更新到现在经过了多少秒
  _disTime() {
    return (Date.now() - this._time) / 1000
  }
  // 获取当前灯的状态
  getCurrentLight() {
    this._update()
    return {
      color: this.currentLight.color,
      remain: this.currentLight.lasts - this._disTime()
    }
  }
}

const light = new TrafficLight([{
    color: 'red',
    lasts: 3
  },
  {
    color: 'yellow',
    lasts: 2
  }, {
    color: 'green',
    lasts: 5
  }
])
const countdown = document.querySelector('.countdown')
const trafficLightDom = document.querySelectorAll('.traffic-light')

function update() {
  const current = light.getCurrentLight();
  countdown.textContent = Math.ceil(current.remain)
  trafficLightDom.forEach(item=>{
    item.classList.remove('red', 'green', 'yellow')
    if(item.id === current.color){
      item.classList.add(current.color)
    }
  })
}
update()
// 动画,类似于定时器
function raf() {
  requestAnimationFrame(() => {
    raf()
    update()
  })
}

raf()

总结

没有太多的详细说明,我想看代码以及注释能看懂,若各位有看不懂或者觉得有问题的可以私聊我或者评论,大家一起共同进步

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章