#函数组件
interface LightProps {
color: string;
active?: boolean;
}
const Light: React.FC<LightProps> = ({ color, active }) => {
return <div className={"light " + color + (active ? " active" : "")} />;
};
const leftToRight = (prevState) => {
if (prevState === 2) {
return 0;
}
return ++prevState;
};
const rightToLeft = (prevState) => {
if (prevState === 0) {
return 2;
}
return --prevState;
};
const directionStrategy = {
left: leftToRight,
right: rightToLeft
};
export default function App() {
const lightConfig = ["red", "yellow", "green"];
const [isLoopStart, setIsLoopStart] = React.useState(false);
const [directionStatus, setDirectionStatus] = React.useState("left");
const [timePause, setTimePause] = React.useState(2);
const [activeLight, setActiveLight] = React.useState(-1);
const timer = React.useRef(null);
const startLoopLight = (timeGap) => {
timer.current = setInterval(() => {
console.log(directionStatus);
setActiveLight(directionStrategy[directionStatus]);
}, 1000 * timeGap);
};
const clearTimer = () => {
if (!timer.current) {
return;
}
clearInterval(timer.current);
setIsLoopStart(false);
};
const toggleLoopStart = () => {
if (isLoopStart) {
clearTimer();
} else {
startLoopLight(timePause);
}
setIsLoopStart(!isLoopStart);
};
const toggleDirectionStatus = () => {
clearTimer();
setDirectionStatus(directionStatus === "left" ? "right" : "left");
};
return (
<>
<div className="lightWrapper">
{lightConfig.map((color, index) => {
return (
<Light
color={color}
key={color}
active={activeLight === index ? true : false}
/>
);
})}
</div>
<div className="controlWrapper">
<button onClick={() => toggleLoopStart()}>
点击 {isLoopStart ? "暂停" : "开始"}
</button>
<button onClick={() => toggleDirectionStatus()}>
当前方向
{directionStatus === "left" ? ">>>>>" : "<<<<<"}
</button>
<div>
<label htmlFor="timePause">循环间隔{timePause}(秒)</label>
<input
type="range"
id="timePause"
min="2"
max="10"
defaultValue={timePause}
onBlur={(e) => {
setTimePause(+e.target.value);
clearTimer();
}}
/>
</div>
</div>
</>
);
}
SCSS
.lightWrapper{
display: flex;
margin-bottom: 30px;
width:50vw;
justify-content: space-between;
}
.controlWrapper{display: flex; width:70vw; justify-content: space-between;}
.light {width:100px;height:100px;border-radius: 50%;opacity: 0.4;
&.red{background-color: red;}
&.yellow{background-color: yellow;}
&.green{background-color: green;}
&.active{opacity: 1;}
}
#类组件
interface LightProps {
color: string;
active: boolean;
}
const Light: React.FC<LightProps> = ({ color, active }) => {
return <span className={"light " + color + (active ? " active" : "")} />;
};
interface TrafficLightState {
/** 跳转的 timer */
nextTickTimer: number;
/** 当前激活的灯的 idx */
activeLightIdx: number;
/** 是否向前走 */
forwordPlaying: boolean;
/** 是否在工作中 */
playing: boolean;
}
export default class TrafficLight extends React.Component<
any,
TrafficLightState
> {
lightConfig = ["red", "yellow", "green"];
state = {
nextTickTimer: 1,
activeLightIdx: 0,
forwordPlaying: true,
playing: false
};
timer!: number;
togglePlayState() {
this.setState(({ playing }) => {
let nextState = !playing;
if (nextState) {
this.lightLoop();
} else {
this.stopLoop();
}
return {
playing: nextState
};
});
}
clearTimer() {
if (!!this.timer) {
clearInterval(this.timer);
this.timer = null;
}
}
stopLoop() {
this.clearTimer();
}
lightLoop() {
this.clearTimer();
this.timer = setInterval(() => {
this.setState(({ activeLightIdx, forwordPlaying }) => {
let nextActiveLightIdx = forwordPlaying
? ++activeLightIdx
: --activeLightIdx;
if (nextActiveLightIdx >= this.lightConfig.length - 1) {
forwordPlaying = false;
} else if (nextActiveLightIdx <= 0) {
forwordPlaying = true;
}
return {
activeLightIdx: nextActiveLightIdx,
forwordPlaying
};
});
}, this.state.nextTickTimer * 1000);
}
changeTickTime(timeStr: string) {
const time = +timeStr;
if (time < 0 || time > 10) return;
this.setState(
{
nextTickTimer: time
},
() => this.lightLoop()
);
}
toggleDirection() {
this.setState(({ forwordPlaying }) => ({
forwordPlaying: !forwordPlaying
}));
this.lightLoop();
}
render() {
const {
playing,
activeLightIdx,
nextTickTimer,
forwordPlaying
} = this.state;
return (
<div className="traffic-light">
<div className="light-wrapper">
{this.lightConfig.map((color, idx) => {
return (
<Light
color={color}
key={color}
active={activeLightIdx === idx}
/>
);
})}
</div>
<button className="btn theme" onClick={(e) => this.togglePlayState()}>
{playing ? "暂停" : "开始"}
</button>
<button className="btn theme" onClick={(e) => this.toggleDirection()}>
方向{forwordPlaying ? "下" : "上"}
</button>
<div>
<label htmlFor="changeTimer">时间间隔</label>
<input
type="text"
id="changeTimer"
value={nextTickTimer}
onFocus={(e) => e.target.select()}
onChange={(e) => this.changeTickTime(e.target.value)}
/>
</div>
</div>
);
}
}