
用js實作一個交通燈
- 🔇前言
- 🔈一、需求分析 - 交通燈
- 🔉二、實作版本
- 1. 版本一:簡單粗暴版
- 2. 版本二:資料抽象版
- 3. 版本三:程序抽象版
- 4. 版本四:命令式和宣告式
- 🔊三、在線online
- 📢四、結束語
- 📣往期推薦
🔇前言
在我們的日常生活中,每天上班下班,伴隨著我們最多的莫過于紅綠燈了,那么,在下面的這篇文章中,我們將手動的來實作一個交通燈,瞅瞅每天都在看的紅綠燈,它到底是怎么實作的呢?
🔈一、需求分析 - 交通燈
首先,我們想要實作的是可以切換多種交通狀態功能的交通燈,如下圖所示:

接下來,我們將由淺入深的,講解多種版本的實作方式,
🔉二、實作版本
1. 版本一:簡單粗暴版
下面先附上代碼:
HTML 代碼:
<ul id="traffic" class="wait">
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
CSS 代碼:
#traffic {
display: flex;
flex-direction: column;
}
#traffic li{
list-style: none;
width: 50px;
height: 50px;
background-color: gray;
margin: 5px;
border-radius: 50%;
}
#traffic.s1 li:nth-child(1) {
background-color: #a00;
}
#traffic.s2 li:nth-child(2) {
background-color: #aa0;
}
#traffic.s3 li:nth-child(3) {
background-color: #0a0;
}
#traffic.s4 li:nth-child(4) {
background-color: #a0a;
}
#traffic.s5 li:nth-child(5) {
background-color: #0aa;
}
JS 代碼:
const traffic = document.getElementById('traffic');
(function reset(){
traffic.className = 's1';
setTimeout(function(){
traffic.className = 's2';
setTimeout(function(){
traffic.className = 's3';
setTimeout(function(){
traffic.className = 's4';
setTimeout(function(){
traffic.className = 's5';
setTimeout(reset, 1000)
}, 1000)
}, 1000)
}, 1000)
}, 1000);
})();
此時瀏覽器的顯示效果為:

ok,到這里,我們就基本上完成了交通燈的基本形態,但是呢,是否有小伙伴覺得,在上面的 JS 代碼中,有點暴力實作了,首先它是先創建了一個閉包 ()() ,然后呢,之后呢,要切換狀態的時候,通過一層一層的 setTimeout() 去進行狀態切換,這樣子實作的話,不管是從復雜度來講,還是其他層面上來講,都是非常的恐怖的,
因此呢,我們要來進行第二版本的改進,對資料進行抽象,
2. 版本二:資料抽象版
先附上代碼:
HTML 代碼:
<ul id="traffic" class="wait">
<li></li>
<li></li>
<li></li>
</ul>
CSS 代碼:
#traffic {
display: flex;
flex-direction: column;
}
#traffic li {
display: inline-block;
width: 50px;
height: 50px;
background-color: gray;
margin: 5px;
border-radius: 50%;
}
#traffic.stop li:nth-child(1) {
background-color: #a00;
}
#traffic.wait li:nth-child(2) {
background-color: #aa0;
}
#traffic.pass li:nth-child(3) {
background-color: #0a0;
}
JS 代碼:
const traffic = document.getElementById('traffic');
const stateList = [
{state: 'wait', last: 1000},
{state: 'stop', last: 3000},
{state: 'pass', last: 3000},
];
function start(traffic, stateList){
function applyState(stateIdx) {
const {state, last} = stateList[stateIdx];
traffic.className = state;
setTimeout(() => {
applyState((stateIdx + 1) % stateList.length);
}, last)
}
applyState(0);
}
start(traffic, stateList);
此時瀏覽器的顯示效果為:

在上面的代碼中,我們對等待、停止和通過這三種狀態進行資料抽象,首先是接收一組狀態,然后再根據這組狀態來進行某些指令的操作,比如通過改變元素的 class 來做狀態的切換,
我們可以把它理解為是把狀態中的資料給封裝起來,以此來使得代碼更具有擴展性,
但是呢,資料抽象還遠遠不夠,因此呢,接下來我們來介紹第三個版本,程序抽象,
3. 版本三:程序抽象版
先附上代碼:
HTML 代碼:
<ul id="traffic" class="wait">
<li></li>
<li></li>
<li></li>
</ul>
CSS 代碼:
#traffic {
display: flex;
flex-direction: column;
}
#traffic li{
display: inline-block;
width: 50px;
height: 50px;
background-color: gray;
margin: 5px;
border-radius: 50%;
}
#traffic.stop li:nth-child(1) {
background-color: #a00;
}
#traffic.wait li:nth-child(2) {
background-color: #aa0;
}
#traffic.pass li:nth-child(3) {
background-color: #0a0;
}
JS 代碼:
const traffic = document.getElementById('traffic');
function wait(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
function poll(...fnList){
let stateIndex = 0;
return async function(...args){
let fn = fnList[stateIndex++ % fnList.length];
return await fn.apply(this, args);
}
}
async function setState(state, ms){
traffic.className = state;
await wait(ms);
}
let trafficStatePoll = poll(setState.bind(null, 'wait', 1000),
setState.bind(null, 'stop', 3000),
setState.bind(null, 'pass', 3000));
(async function() {
// noprotect
while(1) {
await trafficStatePoll();
}
}());
此時瀏覽器的顯示效果為:

在上面的程序抽象中呢,上面的切換狀態呢,是一個典型的輪詢操作,所以呢,我們可以抽象出一個輪詢方法,來把我們前面的 start 方法抽象成一個輪詢方法,然后在這個輪詢方法里面呢,我們給它一個 function list ,也就是一系列的操作函式,
之后呢,我們在 fnList 里面取出當前狀態的操作函式,最后去執行這個函式,
在上面的這個程序中呢,我們實作的是程序抽象,那其實對于這個例子來說,它是一個異步問題,因此,我們可以通過 promise 、 async 和 await 等方式來解決這個問題,請看下方,
4. 版本四:命令式和宣告式
先附上代碼:
HTML 代碼:
<ul id="traffic" class="wait">
<li></li>
<li></li>
<li></li>
</ul>
CSS 代碼:
#traffic {
display: flex;
flex-direction: column;
}
#traffic li{
display: inline-block;
width: 50px;
height: 50px;
background-color: gray;
margin: 5px;
border-radius: 50%;
}
#traffic.stop li:nth-child(1) {
background-color: #a00;
}
#traffic.wait li:nth-child(2) {
background-color: #aa0;
}
#traffic.pass li:nth-child(3) {
background-color: #0a0;
}
JS 代碼:
const traffic = document.getElementById('traffic');
// wait操作,即等待多長時間
function wait(time){
return new Promise(resolve => setTimeout(resolve, time));
}
// 設定狀態
function setState(state){
traffic.className = state;
}
async function start(){
//noprotect
while(1){
setState('wait');
await wait(1000);
setState('stop');
await wait(3000);
setState('pass');
await wait(3000);
}
}
start();
此時瀏覽器的顯示效果是:

交通燈的切換可以把它視為是一個不停的回圈,那在這個回圈里面呢,他會先 setState 一個狀態,之后 await 一段時間,后面繼續再 setState 一個狀態,然后又 await 一段時間,以此類推,
那在實際的應用中,如果我們要對其進行擴展的時候,可以通過修改 while 的方式來進行操作,
🔊三、在線online
以上四個版本的在線地址:
- 版本一:簡單粗暴版
- 版本二:資料抽象版
- 版本三:程序抽象版
- 版本四:命令式和宣告式
📢四、結束語
到這里,由淺入深的四個交通燈版本的實作就講解結束啦!
如果換在以前,那我我可能用暴力破解和資料抽象的方法多一點,那通過上文的學習,現在我又多學習了程序抽象和命令式和宣告式,
所以說, js 還有很多高階的內容值得我們去探索和挖掘~
如果您覺得這篇文章有幫助到您的的話不妨點贊支持一下喲~~😉
📣往期推薦
👉值得關注的HTML基礎知識
👉css還只停留在寫布局?10分鐘帶你探索css中更為奇妙的奧秘!
👉前端只是切圖仔?來學學給開發人看的UI設計
👉緊跟月影大佬的步伐,一起來學習如何寫好JS(上)
👉緊跟月影大佬的步伐,一起來學習如何寫好JS(下)
👉聽紅寶書譯者談Web視角下的前端開發
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/303628.html
標籤:其他
上一篇:Scala 必知必會
下一篇:【演算法學習】1828. 統計一個圓中點的數目(java / c / c++ / python / go / rust)
