春节算命,不准不要赞

3,934 阅读5分钟

PK创意闹新春,我正在参加「春节创意投稿大赛」,详情请看:春节创意投稿大赛

先来看看成品,体验链接

2.gif

对春节最多的印象,对我来说是算命,小时候一到寒假,我爸就拿个老黄历,再用一些铜钱,给我们家人算命。抛铜钱然后查老黄历,反正不管怎么算,给我们算出来的都是好命,什么前途无量、一生富贵这种。刚开始我还觉得很神奇,相信了,有一次跟我姐姐说我的命,我爸听见了就跟我说,不要迷信,你还是得好好学习。

这不,现在的我只相信科学。但是偶尔回想起小时候暖黄色的灯泡下,一家人围在桌子旁一边聊天,一边扔铜钱,一边查老黄历的事情,还是很温馨。

今年因为疫情,又回不了家过年了,很多人和我一样,不如我们来试试用代码实现算命吧。

另外,本项目也可以作为React入门者学习资料,带你掌握从0搭建一个React项目,另外掌握动画、函数组件与Hook等。

创建项目

npx create-react-app goodluck
cd goodluck
yarn dev

此命令需要node环境支持,且版本需要14+,所以如果提示你版本过低,可以用n升级下, 安装n: npm install -g n

安装node指定版本: n stable

背景

先在来个红红火火的背景图,并写个center居中的className样式,留作备用,如下我写在了index.css中:

html,
body {
  height: 100%;
}
body {
  background-image: url("../public/imgs/fu.png");
  background-size: contain;
}

.center {
  display: flex;
  align-items: center;
  justify-content: center;
}

硬币转动特效

coin-rotating.gif

为了探测运气,我们先来实现下硬币转动的动画,首先先来两张硬币正反两面的图片(当然我也没找到完全大小一样的,于是我先是去网上找了两个基本上能用的,但是它们大小不一样、不是png,于是我开始自己动手ps,后面用到的很多图片,都是网图+ps,ps细节就不说了,如果你不会ps,可以去github直接用我的图片,或者留言1,我可以出个ps基本入门教程)。

我设置了五枚铜钱来实现旋转效果,每个铜钱0.5s旋转一圈,无限旋转:

      <div className="canvas">
        {[0, 1, 2, 3, 4].map((item, index) => (
          <div key={index} className={classnames("coin-rotate", "coin")}>
            <div className={classnames("coin0")}></div>
            <div className={classnames("coin1")}></div>
          </div>
        ))}
      </div>

关于实现旋转的css稍复杂,不过核心就是两个div的旋转切换而已,详细css代码如下:

.canvas {
  display: flex;
  justify-content: space-around;
}

.coin {
  position: relative;
  width: 100px;
  height: 100px;
}

.coin:hover {
  animation-play-state: paused;
}

.coin-rotate {
  transform-style: preserve-3d;
  animation: rotate 0.5s infinite linear;
  -webkit-transform-origin: center center;
  -ms-transform-origin: center center;
  transform-origin: center center;
}

@keyframes rotate {
  0% {
    transform: rotateY(0deg);
  }
  100% {
    transform: rotateY(360deg);
  }
}

.coin0,
.coin1 {
  position: absolute;
  left: 0px;
  top: 0px;
  width: inherit;
  height: inherit;
  background-size: contain;
  background-repeat: no-repeat;
  background-position: center;
}
.coin0 {
  transform: translateZ(1px);
  background-image: url("../public/imgs/coin0.png");
}
.coin1 {
  background-image: url("../public/imgs/coin1.png");
}

如果你对css不熟悉,或者不熟悉某个属性,推荐菜鸟教程,对于不同的css效果有详细的动画与描述。

获取铜钱状态

刚刚我们设置了铜钱的无限旋转,那肯定得停下来,并且每个铜钱都有自己的正反面。这个时候我们需要定义下铜钱状态,比如我设置了一个全局变量defaultState:

const defaultState = {
  // loading为true代表铜钱在旋转
  loading: false,
  // 长度为5、默认值为0的数组,(0代表硬币反面,1代表正面)
  data: new Array(5).fill(0),
  luck: {
    // 算命的结果语
    destiny: "",
    // 一个class数组,标记不同命运的不同动画效果,具体的代码定义在index.css
    classNames: [],
    // 头图,如金钱豹
    banner: "",
  },
};

这里我们首先需要获取铜钱状态,这里我多次调用了getLuckNum这个函数,即随机获取0与1:

// 获取五枚铜钱的正反面,当然是随机数,你想什么呢,难不成还看你生辰八字
export function getLuckNum() {
  return Math.round(Math.random());
}

接下来这里我们获取命运结果的函数如下:

  const getLuck = () => {
    let newState = { ...defaultState };
    if (state.loading) {
      newState.loading = false;
      // 获取五枚铜钱的正反面,玄机查看utils
      for (let i = 0; i < 5; i++) {
        newState.data[i] = getLuckNum();
      }
      newState.luck = getGoodLuck(wish);
    } else {
      newState.loading = true;
    }
    setState(newState);
  };

好了,准备工作都做好了,App组件该出场了: 首先是决定命运的button:

<button onClick={getLuck}>
    {state.loading ? "查看结果" : "查看运势"}
</button>

然后是显示命运结果的组件:

      <div className="canvas">
        {state.data.map((item, index) => (
          <div
            key={index}
            className={classnames(state.loading && "coin-rotate", "coin")}
          >
            <div
              className={classnames(
                "coin0",
                !state.loading && item === 1 && "hide"
              )}
            ></div>
            <div
              className={classnames(
                "coin1",
                !state.loading && item === 0 && "hide"
              )}
            ></div>
          </div>
        ))}
      </div>

      {state.luck?.classNames != "" && !state.loading && (
        <div className={classnames("luck-bg center", state.luck?.mainStyle)}>
          {state.luck?.classNames &&
            state.luck?.classNames.map((item, index) => (
              <div
                key={index}
                className={classnames("luck", item, "center")}
              ></div>
            ))}

          <div
            className={classnames(state.luck.banner, "banner", "center")}
          ></div>
          <div className="blessing center">{state.luck?.destiny}</div>
      )}
    </div>
  );
}

说出愿望吧

在上一步中,你可能注意到了,getGoodLuck接收了参数wish,就是说用户可以输入愿望。算命嘛,就算你是去庙里,别人也会问你是求前途还是求姻缘,我当年高考,我妈就去庙里给我算了算,求了高考,结果我就考上了。哈哈,算命有时候当做心里安慰还是很有用的,虽然我高考后才知道这事。

接下来写个输入框,让用户输入所求,如“暴富”,然后点击button开始扔硬币 当然input框我们需要做成受控组件,首先定义状态wish:

  const [wish, setWish] = useState("");

当然是用Hook定义state了,如果你和eric也有一个坚持不用函数组件和Hook的领导,趁过年,赶紧换了吧。

<input
    className="name"
    type="text"
    value={name}
    placeholder="说出你的愿望吧"
    onChange={(e) => {
      setName(e.target.value);
    }}
    onKeyUp={(e) => {
      if (e.keyCode === 13) {
        getLuck();
      }
    }}
  />

如果你很好奇,怎么根据愿望给出命运结果呢,其实我有个大json,去里面搜索数据就行了,相当于算命先生的脑库。具体代码过多,我就不全贴出来了,想看全套的代码的,可以点击文末链接去github查看。

丰富

好了,到现在为止,已经能看到效果了,为了能让用户多次算命,我加了个一键重启的button,当然这和刷新页面是一样的效果~

来输入暴富,看看发生了什么~ image.png

当然你也可以尝试其他输入,如脱单、暴瘦、升职加薪等等~

除了算命,本项目还可以用作React入门学习,被我点名的,赶紧学起来吧~

代码地址 体验地址

知识点总结

除了算命这一娱乐之外,本项目知识点如下:

  1. js基础,如数组、字符串、正则匹配等
  2. 动画
  3. 均衡获取随机数
  4. Hook state使用
  5. 在create react app中配置less
  6. 等等

其中,第五条其实和本项目关系不大,你可以不配置~