🥷

フリック入力の速さを測れるアプリを作った

2024/12/19に公開

この記事はクソアプリAdvent Calendar 2024の19日目の記事です。

はじめに

私は色々とあって今ドイツに住んでいるのですが、日本人のフリック入力が特殊すぎてFinger Ninjaと呼ばれているのを知りました。(もしかしたら一部の人が呼んでいるかも、ソースは不明)

Finger Ninja? 何それかっこいいと思って、旦那に「私Finger Ninjaだわ」と話していたら、絶対に俺の方がFinger Ninjaと言われ、どちらが真のFinger Ninjaか競おうという話になりました。

ただ問題はフリック入力に特化したタイピングアプリが見つからなかったんです。見つからないなら作れば良いじゃないということで作りました。そうフリック入力しか機能しないタイピングアプリを・・!

https://x.com/toffy_dev/status/1869546607999611051

難しかったところ

日本語入力というのは少し特殊で、入力後確定をさせる必要があります。ただ、今回はタイピングアプリなのでユーザーが個人で確定するのを避けて、自動で次のお題に進むようにする必要がありました。

次のお題に進む際にInputの入力をリセットしてもユーザーが確定する前だと次の入力に移った時に前の入力が残ってしまうという課題に悩まされました。
そこで、Inputにキーを割り当てて、次のお題に移る際にInputを新しくして擬似的に確定とリセットを行うようにしました。その際、少しタイミングをずらさないとうまくリセットできない時があったので成功アニメーションと失敗時の画面を出すようにしました。

具体的には以下です。

  const [inputKey, setInputKey] = useState<number>(0);

  const resetInputField = () => {
    setInputValue("");
    setInputKey((prevKey) => prevKey + 1);
  };

      <InputForm
        inputValue={inputValue}
        handleInputChange={handleInputChange}
        inputKey={inputKey}
        resetInputField={() => setInputValue("")}
      />

InputFormの中でキーが切り替わるたびにrefのvalueをリセットしたりをしています。キーボードを常に出しっぱなしにする必要があったので、リセットした後にrefで再度フォーカスを当てることでキーボードを出しています。resetInputFieldを使ってのリセットはすでに親要素で行なっているですが、これがないとうまくリセットできない時があるのでつけています。おそらくキーが切り替わって、擬似的に確定ボタンを押した状態にしてからの入力欄リセットにほんのわずかな遅延が生じるからではないかと推測しています。(また200ms程度の遅延を持たないとうまく動きませんでした)

import { useEffect, useRef } from "react";

type Props = {
  inputValue: string;
  handleInputChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
  resetInputField: () => void;
  inputKey: number;
};

export const InputForm: React.FC<Props> = ({
  inputValue,
  handleInputChange,
  resetInputField,
  inputKey,
}) => {
  useEffect(() => {
    if (inputRef.current) {
      inputRef.current.value = "";
      inputRef.current.focus();
      setTimeout(() => {
        resetInputField();
      }, 200);
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [inputKey]);
  const inputRef = useRef<HTMLInputElement | null>(null);
  return (
    <input
      ref={inputRef}
      type="text"
      value={inputValue}
      onChange={handleInputChange}
      key={inputKey}
      style={{ opacity: 0, position: "absolute", top: "-9999px" }}
      autoFocus
    />
  );
};

濁点、半濁点の処理

日本語にはややこしいことに濁点・半濁点があります。例えば「が」だと最初に「か」を入力した後に濁点をつける必要があります。
ただ今回のアプリはフリック入力のタイピングアプリ、入力した後すぐにその入力があっているかどうかを判定する必要があります。

そこでもうややこしいのでお題から濁点・半濁点を抜くという逃げの選択肢を取りました。

ちなみにそのための選択肢をChatGPTに出してもらったところ、普通に濁点ありの文章が出されました。ChatGPTには濁点なしはまだ早すぎたみたいです。

濁点をより詳細に定義してみましたがダメでした。

子音が二つ重なる場合の処理

日本語では例えば「なな」と打ちたい場合にフリック入力でそのまま打つと「に」に変換されてしまいます。それを避けるために、上記と同じくお題から省きました。(またしても逃げの解決策です)

あとがき

世の中にタイピングアプリは溢れていますが、フリック入力の速さをはかれるアプリはなかなかないのではないでしょうか??
この機会にFinger Ninjaになってみてはいかがでしょうか。

それでは最後までお読みいただきありがとうございました!
いいねやシェアをしてもらえると励みになります。

Discussion