致敬 Rick&Morty! SheepCounter 小程序开发实践

1,223 阅读3分钟

“我报名参加金石计划1期挑战——瓜分10万奖池,这是我的第1篇文章,点击查看活动详情

前言

新一季的 Rick&Morty 已经上线,剧集质量虽然有所下降,但 E03 中的 SheepCounter 挺有意思。自己照着剧中的设定开发了一款界面极其相似、交互更为丰富的小程序,小程序的终极目标只有一个:数羊!数羊!!数羊!!!。

不会真有人觉得这款小程序会起到催眠作用吧。

小程序虽然简单,但也探索出了一些有意思的小实现。下面就和大家分享探讨一下:

曲线路径的动画实现

首先就是小羊跳入、跳出界面的动画,仔细观察地话可以发现,小羊移动的路径其实是条曲线。

sheepJump.gif

水平或者垂直移动的动画不难实现,我们可以通过控制绝对定位下的 left/right 或者 top/bottom 来实现。

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <title></title>
    <meta name="description" content="" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <style>
      .zone {
        position: relative;
        width: 200px;
        height: 400px;
        top: 100px;
        left: 400px;
        border: 1px solid red;
      }
      .ball {
        position: absolute;
        width: 50px;
        height: 50px;
        background-color: cadetblue;
        border-radius: 50%;
        top: 30px;
        right: -100px;
        animation: XIn 1s linear;
        animation-fill-mode: forwards;
      }
      @keyframes XIn {
        0% {
          right: -100px;
        }
        100% {
          right: 50%;
        }
      }
    </style>
  </head>
  <body>
    <div class="zone">
      <div class="ball"></div>
    </div>
  </body>
</html>

line.gif

如果是需要曲线移动呢?animation 中似乎没有可以设置曲线移动的属性。不过我们可以参照高中物理知识思考一下,二维平面上移动无外乎水平或者垂直两个方向,当两个方向的移动同时发生且速度不一致时,便可以合成为曲线运动。结合此原理,我们可以轻松地想到:只要在animation中同时设置速度不一致的垂直移动动画和水平移动动画即可。

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <title></title>
    <meta name="description" content="" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <style>
      .zone {
        position: relative;
        width: 200px;
        height: 400px;
        top: 100px;
        left: 400px;
        border: 1px solid red;
      }
      .ball {
        position: absolute;
        width: 50px;
        height: 50px;
        background-color: cadetblue;
        border-radius: 50%;
        top: 30px;
        right: -100px;
        animation: XIn 1s cubic-bezier(0.28, 0.58, 0.4, 0.73), YIn 1s linear;
        animation-fill-mode: forwards;
      }
      @keyframes XIn {
        0% {
          right: -100px;
        }
        100% {
          right: 50%;
        }
      }
      @keyframes YIn {
        0% {
          top: 30px;
        }
        100% {
          top: 50%;
        }
      }
    </style>
  </head>
  <body>
    <div class="zone">
      <div class="ball"></div>
    </div>
  </body>
</html>

这样也就成功实现了曲线移动效果。

curve.gif

自定义输入框的实现

可以看到,在我们小程序的数羊界面中已经有了自定义实现了键盘。在小程序中进行输入时,我们肯定不希望弹起系统键盘。

sheepJump.gif 要实现这个功能,最简单的方法便是将input组件禁用,再通过修改禁用状态下的组件样式即可。 但这款小程序本就是消遣娱乐的产物,所以我决定曲线救国:自定义实现一个输入框。

输入框最有辨识度的特点是什么?那自然是闪烁的光标。光标才可以为我们自定义的输入框注入灵魂。闪烁效果其实也只是个动画而已,以下是简单的实现效果:

.sCursor {
  position: absolute;
  display: inline-block;
  width: 3px;
  height: 50px;
  margin-left: 4px;
  background: #000;
  top: 24px;
  animation: blink 800ms infinite reverse;
}

@keyframes blink {
    0% {
        opacity: 1;
    }
    100% {
        opacity: 0;
    }
}
<View className={style.sInput}>
    {/* <!-- 文本 --> */}
    {inputValue?.split('').map((v: string, i: number) => (
        <Text key={i} className={style.sValue}>{v} </Text>
     ))}
     {/* <!-- 光标 --> */}
    <View className={style.sCursor} />
</View>

为了避免用户忘记了自己数到了第几只羊,我决定在用户输入错误两次后,用placeholder的形式来提示用户。自定义placeholder的思路也非常简单,只需要将其绝对定位至正常输入的文本之下:

.inputZone {
    width: 500px;
    height: 100px;
    position: relative;
    .sInput {
        width: 100%;
        height: 100%;
        padding: 10px 40px;
        letter-spacing: 6px;
        position: relative;
        .sValue {
            font-size: 60px;
            font-family: 'handwriting';
        }
        .sCursor {
            //...
        }
    }

    .sTip {
        position: absolute;
        font-size: 60px;
        font-family: 'handwriting';
        z-index: 1;
        top: 10px;
        left: 40px;
        color: rgb(145, 142, 142);
    }
}
<View className={style.inputZone}>
    <View className={style.sInput}>
        {/* <!-- 文本 --> */}
        {inputValue?.split('').map((v: string, i: number) => (
            <Text key={i} className={style.sValue}>{v}</Text>
        ))}
        {/* <!-- 光标 --> */}
        <View className={style.sCursor} />
    </View>
    {tip && !inputValue ? (
        <Text className={style.sTip}>{tip}</Text>
    ) : null}
</View>

键盘功能的实现就更简单了,只需要根据不同按钮点击事件触发相应的功能就行。想必大家都能想到,这里不贴代码了。

尾言

兴趣是学习的最好老师。一款看起来极其简单的小程序,真正实践开发起来也能遇到一些未曾了解过的知识点。Xdm,如果你也有有趣的点子,别犹豫,行动起来吧!

最后插一句,希望Rick&Morty后续剧集的质量能upup!!