4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

ブラウザ上でフリック入力ができる仮想キーボードを作ってみた

Posted at

はじめに

先日、対戦型ポケモンWordleをリリースしました。
リリース当初、ただテキストボックスにポケモンの名前を入力する形にしており、入力履歴の管理ができておりませんでした。
入力履歴の機能を追加しようと、大元のWordleを参考に入力用のボタンを用意しようと思いました。
ただ、日本語は50音+濁点・半濁点で80ボタン以上必要で、このボタンからポチポチするのは入力が大変だろうなと思いました。
そこで、ブラウザ上でフリック入力できる仮想キーボードを作ってみました。

完成品

See the Pen フリック入力 by yotty (@yotty) on CodePen.

パソコンからでは直接フリック機能が使えません。
パソコンからフリック機能を確認したい方は、デベロッパーツールでデバイスを変更することで試すことができます。

※Chromeの場合
F12でデベロッパーツールを開き、以下のアイコンからデバイスをモバイル端末に変更
image.png

実装

以下、実装内容の解説です。

事前準備

事前準備としてさくっと外観を作っておきます。

また、フリック入力の際、長押しで文字選択されたり、入力のタップでズームされたりしないように以下を追加しておきます。

css
body{
    user-select: none;
    -webkit-user-select: none;
    -ms-user-select: none;
    -moz-user-select: none;
    -khtml-user-select: none;
    touch-action: none;
}

さらに、フリックした際、濁点・半濁点の切り替えを行った際に入力すべき文字を特定するために以下のような配列を用意しておきます。

const l_word = [
    ['', '', '', '', '' ]
    , ['', '', '', '', '' ]
    , ['', '', '', '', '' ]
    , ['', '', '', '', '' ]
    , ['', '', '', '', '' ]
    , ['', '', '', '', '' ]
    , ['', '', '', '', '' ]
    , ['', '', '', '', '' ]
    , ['', '', '', '', '' ]
    , ['', '', '', '', '' ]
];

const l_word_switch = [
    ['', '']
    , ['', '']
    , ['', '']
    , ['', '']
    , ['', '']
    , ['', '']
    , ['', '']
    , ['', '']
    , ['', '']
    , ['', '']
    , ['', '']
    , ['', '']
    , ['', '']
    , ['', '']
    , ['', '']
    , ['', '']
    , ['', '']
    , ['', '', '']
    , ['', '']
    , ['', '']
    , ['', '', '']
    , ['', '', '']
    , ['', '', '']
    , ['', '', '']
    , ['', '', '']
    , ['', '']
    , ['', '']
    , ['', '']
];

フリック入力

フリックを検知するために、「touchstart」「touchmove」「touchend」の3つのイベントを利用します。

「touchstart」で、タッチした場所を記憶しておきます。
その他、フリックしたときのオブジェクトの位置や文字などの設定もしています。

touchstart
function onTouchStart(event) {
    $(event.currentTarget).addClass('gray_for_touch');

    position = event.originalEvent.touches[0];
    direction = 'c';

    //フリックのオブジェクトの位置を移動させておく
    offset = $(event.currentTarget).offset();
    $('.kb_key_u').offset({ top: offset.top - 48, left: offset.left });
    $('.kb_key_d').offset({ top: offset.top + 48, left: offset.left });
    $('.kb_key_l').offset({ top: offset.top, left: offset.left - 48 });
    $('.kb_key_r').offset({ top: offset.top, left: offset.left + 48 });

    //テキストセット
    var l = l_word.find(x => x.includes($(event.currentTarget).text()));
    $('.kb_key_u').text(l[2]);
    $('.kb_key_d').text(l[4]);
    $('.kb_key_l').text(l[1]);
    $('.kb_key_r').text(l[3]);
};

「touchmove」では、移動した現在の位置を取得して上下左右にどれだけ移動したかを計算します。
上下左右それぞれの移動距離のmaxを取って移動方向を検知し、閾値20を超えたらその移動方向にフリックしたと判定させています。

touchmove
function onTouchMove(event) {
    direction = 'c';
    $('.kb_key_u').addClass('transparent');
    $('.kb_key_d').addClass('transparent');
    $('.kb_key_l').addClass('transparent');
    $('.kb_key_r').addClass('transparent');
    $(event.currentTarget).addClass('gray_for_touch');
    $(event.currentTarget).removeClass('transparent');

    var new_position = event.originalEvent.touches[0];
    
    var u = position.screenY - new_position.screenY;
    var d = new_position.screenY - position.screenY;
    var l = position.screenX - new_position.screenX;
    var r = new_position.screenX - position.screenX;
    var max = Math.max(u, d, r, l);

    if (max < 20) {
        return;
    }

    var input_key = $(event.currentTarget).text();

    $(event.currentTarget).removeClass('gray_for_touch');
    $(event.currentTarget).addClass('transparent');

    if (max == u) {
        direction = 'u';
        $('.kb_key_u').removeClass('transparent');
    }
    else if (max == d) {
        direction = 'd';
        $('.kb_key_d').removeClass('transparent');
    }
    else if (max == l) {
        direction = 'l';
        $('.kb_key_l').removeClass('transparent');
    }
    else if (max == r) {
        direction = 'r';
        $('.kb_key_r').removeClass('transparent');
    }
};

「touchend」では、「touchmove」で検知したフリックの方向から入力すべき文字を判断します。
フリックの方向によってindexを変えて、事前に用意した「l_word 」から入力する文字を決定します。
あとは、テキストボックスの値の最後にその文字を付与します。

touchend
function onTouchEnd(event) {
    $('.kb_key_u').addClass('transparent');
    $('.kb_key_d').addClass('transparent');
    $('.kb_key_l').addClass('transparent');
    $('.kb_key_r').addClass('transparent');
    $(event.currentTarget).removeClass('gray_for_touch');
    $(event.currentTarget).removeClass('transparent');

    var input_key = '';
    var key = $(event.currentTarget).text();
    
    var l = l_word.find(x => x.includes($(event.currentTarget).text()));
    var index = 0;
    
    if (direction == 'c') {
        index = 0;
    }
    else if (direction == 'u') {
        index = 2;
    }
    else if (direction == 'd') {
        index = 4;
    }
    else if (direction == 'l') {
        index = 1;
    }
    else if (direction == 'r') {
        index = 3;
    }

    var txt = $('#txt').val();
    if (l[index] == '') {
        return;
    }

    $('#txt').val(txt + l[index]);
};

濁点・半濁点切り替え

テキストボックスに入力されている最後の文字を「l_word_switch」から探し、indexを1ずらして濁点・半濁点を切り替えて文字を取得しています。

濁点・半濁点切り替え
function onTouchStart_switch(event) {
    $(event.currentTarget).addClass('gray_for_touch');
    temp_word = $('#txt').val();
    targer_word = temp_word.slice(-1);
    var l_switch = l_word_switch.find(x => x.includes(targer_word));
    if (l_switch === undefined) {
        return;
    }

    var index = l_switch.indexOf(targer_word);
    if (index < 0) {
        return;
    }
    else if (index == l_switch.length - 1) {
        index = 0;
    }
    else {
        index += 1;
    }
    
    input_key = l_switch[index];
    $('#txt').val(temp_word.slice(0, -1) + input_key);
};

バックスペース

テキストボックスの最後の文字を削除するだけで実装できます。

バックスペース
$(document).on('touchstart', '#kb_key_bs', function (event) {
    $(event.currentTarget).addClass('gray_for_touch');
    var temp_txt = $('#txt').val();
    $('#txt').val(temp_txt.slice(0, -1));
});

おわりに

実際のアプリでは、今回紹介したフリック入力を組み込み、入力欄を細かく分けることによって入力履歴の管理を実現しています。

アプリが気になった方はこちらから遊ぶことができるので、ぜひ一度触ってみてください!

参考

4
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?