Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

用 React 实现一个信号灯(交通灯)控制器 #155

Open
sisterAn opened this issue Mar 7, 2021 · 2 comments
Open

用 React 实现一个信号灯(交通灯)控制器 #155

sisterAn opened this issue Mar 7, 2021 · 2 comments
Labels

Comments

@sisterAn
Copy link
Owner

sisterAn commented Mar 7, 2021

/** 1. 信号灯控制器
用 React 实现一个信号灯(交通灯)控制器,要求:
1. 默认情况下,
  1.1. 红灯亮20秒,并且最后5秒闪烁
  1.2. 绿灯亮20秒,并且最后5秒闪烁
  1.3. 黄灯亮10秒
  1.4. 次序为 红-绿-黄-红-绿-黄
2. 灯的个数、颜色、持续时间、闪烁时间、灯光次序都可配置,如:
   lights=[{color: '#fff', duration: 10000, twinkleDuration: 5000}, ... ]
*/

import React from 'react'
import ReactDOM from 'react-dom'
class TrafficLightItem extends React.Component {
}
@sisterAn sisterAn added the 阿里 label Mar 7, 2021
@sisterAn sisterAn changed the title 实现一个信号灯控制器 用 React 实现一个信号灯(交通灯)控制器 Mar 7, 2021
@zhang0ZGC
Copy link

我的想法是灯的闪烁就用了CSS的动画加上设定的延迟时间🤣利用 CSS 变量设置灯的颜色及闪烁的时间点。

@keyframes twinkle {
  0% {
    /*background-color: #10a54a;*/
    background-color: var(--color);
  }
  50% {
    background-color: #000;
  }
  100% {
    /*background-color: #10a54a;*/
    background-color: var(--color);
  }
}

.light {
  background-color: #000;
  width: 60px;
  height: 60px;
  border-radius: 30px;
  margin: 4px;
}

.light.active {
  background-color: var(--color);
  animation: twinkle 1s ease-in-out infinite var(--twinkle-delay);
}

然后给灯设置上CSS变量:

type LightConf = {
  color: string;
  duration: number;
  twinkleDuration?: number;
};

type TrafficLightItemProps = {
  active?: boolean;
} & LightConf;

class TrafficLightItem extends React.Component<TrafficLightItemProps> {
  render() {
    const { active, color, duration, twinkleDuration } = this.props;
    let styles: any = {
      "--color": color,
      "--twinkle-delay": duration - (twinkleDuration || 0) + "ms"
    };
    return <div className={`light ${active ? "active" : ""}`} style={styles} />;
  }
}

然后整个信号灯的框架组件,设置灯的配置,内部状态记录当前亮的灯的序号,并在组件挂在后,利用 setTimeout 循环修改当前灯的序号

type TrafficLightState = { currentIdx: number; lights: LightConf[] };
class TrafficLight extends React.Component<{}, TrafficLightState> {
  constructor(props: any) {
    super(props);

    this.timer = null;

    this.state = {
      lights: [
        { color: "red", duration: 20000, twinkleDuration: 5000 },
        { color: "green", duration: 20000, twinkleDuration: 5000 },
        { color: "yellow", duration: 10000 }
      ],
      currentIdx: 0
    };
  }

  componentDidMount() {
    this.startTimer();
  }

  componentWillUnmount() {
    clearTimeout(this.timer);
  }

  startTimer = () => {
    const { lights, currentIdx } = this.state;

    const nextIdx: number = lights.length - 1 > currentIdx ? currentIdx + 1 : 0;
    this.timer = setTimeout(() => {
      this.setState({ currentIdx: nextIdx });

      this.startTimer();
    }, lights[currentIdx].duration);
  };

  render() {
    const { currentIdx, lights } = this.state;

    return (
      <div>
        {lights.map((light, k) => (
          <TrafficLightItem key={k} {...light} active={currentIdx === k} />
        ))}
      </div>
    );
  }
}

也不知道有没有跑题,感觉会有点问题。。如果js很卡了的话,最后黄灯可能会闪。。

挫代码😂 https://codesandbox.io/s/new-rain-9pdzn

@xiaoerwen
Copy link

`import React from 'react'
import ReactDOM from 'react-dom'
class TrafficLightItem extends React.Component {

constructor(props) {
    super(props);
    this.state = {
        lights: [
            // 红灯,亮2s,闪烁500ms
            {color: '#ff4500', duration: 2000, twinkleDuration: 500, isLighted: false},
            // 黄灯,亮2s,闪烁500ms
            {color: '#FFD700', duration: 2000, twinkleDuration: 500, isLighted: false},
            // 绿灯,亮1s
            {color: '#32CD32', duration: 1000, isLighted: false}
        ]
        
    }
}

/**
 * 灯的颜色和数据的映射关系
 *
 * @param {string} color
 */
getLightsMap(color) {
    const maps = {
        red: 0,
        yellow: 1,
        green: 2
    };
    return maps[color];
}

/**
 * 为信号灯添加背景色
 *
 * @param {string} color
 */
addLightedStyle(color) {
    const light = this.state.lights[this.getLightsMap(color)];
    return light.isLighted ? light.color : '#fff';
}

render() {
    return (
        <div>
            <div className="light" style={{backgroundColor: this.addLightedStyle('red')}}>red</div>
            <div className="light" style={{backgroundColor: this.addLightedStyle('yellow')}}>yellow</div>
            <div className="light" style={{backgroundColor: this.addLightedStyle('green')}}>green</div>
        </div>
    );
}

componentDidMount() {
    this.start();
}

// 启动信号灯变化
start() {
    // 播放顺序为 红->黄->绿->红->黄->绿
    let playArr = [0, 1, 2, 0, 1, 2];
    // 每个信号灯执行结束,是否进入下一个信号灯播放,存入回调函数
    let len = playArr.length;
    let cbs = new Array(len);
    for (let i = len - 1; i > 0; i--) {
        cbs[i - 1] = () => this.setLightedTime(playArr[i], cbs[i]);
    }
    // 开始第一个灯的播放
    this.setLightedTime(playArr[0], cbs[0]);
}

/**
 * 变换灯的颜色
 *
 * @param {number} colorIdx 
 */
toggleLighted(colorIdx) {
    let light = this.state.lights[colorIdx];
    let value = Object.assign(
        {},
        light,
        {isLighted: !light.isLighted}
    );
    this.setState({
        lights: this.state.lights.map((item, key) => key === colorIdx ? value : item)
    });
}

/**
 * 设置亮灯时长
 *
 * @param {number} colorIdx 
 * @param {Function} callback 
 */
setLightedTime(colorIdx, callback) {
    // 先亮灯
    this.toggleLighted(colorIdx);

    // 设置亮灯时长
    let lightTime = this.state.lights[colorIdx].duration;
    setTimeout(() => {
        // 是否闪烁
        let blingTime = this.state.lights[colorIdx].twinkleDuration;
        if (blingTime && callback) {
            this.blingLightTime(colorIdx, blingTime, callback);
        }
        // 绿灯不闪烁,直接熄灯
        else if (callback) {
            this.toggleLighted(colorIdx);
            callback();
        }
    }, lightTime);
}

/**
 * 闪烁灯
 *
 * @param {number} colorIdx 
 * @param {number} blingTime 
 * @param {Function} callback 
 */
blingLightTime(colorIdx, blingTime, callback) {
    // 通过切换背景颜色来达到闪烁的目的
    let myInterval = setInterval(() => {
        this.toggleLighted(colorIdx);
    }, 100);
    setTimeout(() => {
        clearInterval(myInterval);
        callback();
    }, blingTime);
}

}
`

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants