今天在网上看到了一道 React 的红绿灯题目,就是试试将它写出来
用 React 实现一个信号灯(交通灯)控制器,要求:
默认情况下,红灯亮20秒,并且最后5秒闪烁绿灯亮20秒,并且最后5秒闪烁黄灯亮10秒,
次序为:红-绿-黄-红-绿-黄
灯的个数、颜色、持续时间、闪烁时间、灯光次序都可配置,
如:lights=[{color: ‘#fff’, duration: 10000, twinkleDuration: 5000}, … ]
Components
// light.js
import React, { Component } from 'react';
import './style.scss';
class Lights extends Component {
static propTypes = {};
static defaultProps = {};
lightKey = 0;
twinkTime = 300;
constructor(props) {
super(props);
this.state = {
lights: [
{ color: 'red', duration: 8000, twinkleDuration: 3000 },
{ color: 'yellow', duration: 3000 },
{ color: 'green', duration: 8000, twinkleDuration: 3000 }
]
};
}
componentDidMount() {
this.changeLight();
}
componentWillUnmount() {
if (this.lightKey) {
clearTimeout(this.lightKey);
}
}
changeLight = (index = 0) => {
const { lights } = this.state;
const target = lights[index];
if (target) {
document.getElementsByClassName('lights')[0].style.setProperty('--bg-color', target.color);
this.lightKey = setTimeout(() => {
const key = (index + 1) === lights.length ? 0 : index + 1;
this.changeLight(key);
}, target.duration);
if (target.twinkleDuration) {
setTimeout(() => {
this.handleTwinkleDuration(target.twinkleDuration, target.color);
}, target.duration - target.twinkleDuration);
}
}
}
handleTwinkleDuration = (twinkleDuration, color) => {
const time = twinkleDuration - this.twinkTime;
let currentColor = document.getElementsByClassName('lights')[0].style.getPropertyValue('--bg-color');
if (currentColor === 'gray') {
currentColor = color;
} else {
currentColor = 'gray';
}
document.getElementsByClassName('lights')[0].style.setProperty('--bg-color', currentColor);
if (time >= this.twinkTime) {
setTimeout(() => {
this.handleTwinkleDuration(time, color);
}, this.twinkTime);
}
}
render() {
return (
<div className="lights" />
);
}
}
export default Lights;
hook
import React, { useEffect } from 'react';
import './style.scss';
const Lights = ({ light: propsLights = '' }) => {
let lightKey = 0;
const twinkTime = 300;
const defaultLights = [
{ color: 'red', duration: 8000, twinkleDuration: 3000 },
{ color: 'yellow', duration: 3000 },
{ color: 'green', duration: 8000, twinkleDuration: 3000 }
];
const lights = propsLights || defaultLights;
useEffect(() => {
changeLight();
// 清除定时器循环,不然会导致资源泄漏
return () => clearInterval(lightKey);
}, []);
const changeLight = (index = 0) => {
const target = lights[index];
if (target) {
document.getElementsByClassName('lights')[0].style.setProperty('--bg-color', target.color);
lightKey = setTimeout(() => {
// 当默认设置走完,再从新开始循环
const key = (index + 1) === lights.length ? 0 : index + 1;
changeLight(key);
}, target.duration);
if (target.twinkleDuration) {
setTimeout(() => {
handleTwinkleDuration(target.twinkleDuration, target.color);
}, target.duration - target.twinkleDuration);
}
}
};
const handleTwinkleDuration = (twinkleDuration, color) => {
const time = twinkleDuration - twinkTime;
let currentColor = document.getElementsByClassName('lights')[0].style.getPropertyValue('--bg-color');
if (currentColor === 'gray') {
currentColor = color;
} else {
currentColor = 'gray';
}
document.getElementsByClassName('lights')[0].style.setProperty('--bg-color', currentColor);
if (time >= twinkTime) {
setTimeout(() => {
handleTwinkleDuration(time, color);
}, twinkTime);
}
};
return (
<div className="lights" />
);
};
export default Lights;
// style.scss
.lights {
--bg-color: #eee;
width: 50px;
height: 50px;
border: 1px solid #eee;
border-radius: 27px;
background-color: var(--bg-color);
}