よーし、パパ jQuery と CSS3 でフリック入力風の UI 作っちゃうぞ!の巻 その3

なぜか、このブログの正式テーマである C# 以上に jQuery + フリック入力検索エンジンから飛んでくる人が多い。
そんなにフリック入力って需要あんのかしら。でも、フリック入力なんて Appleソフトウェア特許じゃないのかな?

個人的にはフリック入力が激遅なんで、それの練習用のアプリを作ってるってイメージなんやけど。
ま、JavaScriptCSS の学習用の教材として参考にしてもらうのは全然問題無いので、もしもそういう需要があるならどうぞ。

現時点での暫定コード。処理内容が読み取りやすいように平易に記述してある。
DRY 違反になりそうなところは概ね修正した。

$(document).ready(function(){
    var left = 100, top = 100;
    var buttonWidth = 40;
    var columnSize = 3;
    var cnt = 0;
    //初期の位置に移動させる。
    $('.button').each(function() {
        var x = cnt % columnSize;
        var y = Math.floor(cnt / columnSize);
        $(this).css({
            'left' : (x * (buttonWidth + 1) + left) + 'px',
            'top' : (y * (buttonWidth + 1) + top) + 'px'
        });
        cnt = cnt + 1;
    });

    var map = {
        'a' : 'あいうえお',
        'ka' : 'かきくけこ',
        'sa' : 'さしすせそ',
        'ta' : 'たちつてと',
        'na' : 'なにぬねの',
        'ha' : 'はひふへほ',
        'ma' : 'まみむめも',
        'ya' : 'や ゆ よ',
        'ra' : 'らりるれろ',
        'wa' : 'わをんー '
    };
    var mouseDownPosition = {
        'left' : 0,
        'top' : 0
    };
    var id = '';
    //ボタンのクリック処理
    $('.button').mousedown(function(e) {
        mouseDownPosition.left = e.pageX;
        mouseDownPosition.top = e.pageY;
        var parent = $(this).parent();
        //jQuery UI の力も借りる
        var sx = $(this).position().left;
        var sy = $(this).position().top;
        var leftButton = $(this).clone();
        leftButton.css({
            'background-color' : 'whitesmoke',
            'z-index' : 1000
        }).addClass('temporal');
        var rightButton = leftButton.clone();
        var topButton = leftButton.clone();
        var bottomButton = leftButton.clone();
        id = $(this).attr('id');

        leftButton.css({
            'left' : (sx - (buttonWidth + 1)) + 'px'
        }).addClass('left').text(map[id].charAt(1));
        rightButton.css({
            'left' : (sx + (buttonWidth + 1)) + 'px'
        }).addClass('right').text(map[id].charAt(3));
        topButton.css({
            'top' : (sy - (buttonWidth + 1)) + 'px'
        }).addClass('top').text(map[id].charAt(2));
        bottomButton.css({
            'top' : (sy + (buttonWidth + 1)) + 'px'
        }).addClass('bottom').text(map[id].charAt(4));
        if (leftButton.text() != ' ') {
            parent.append(leftButton);
        }
        if (rightButton.text() != ' ') {
            parent.append(rightButton);
        }
        if (topButton.text() != ' ') {
            parent.append(topButton);
        }
        if (bottomButton.text() != ' ') {
            parent.append(bottomButton);
        }
        //開いたボタンの影が邪魔になるんで、真ん中ボタンを全面に押し出す
        $(this).css({ 'z-index' : 1200 });
    });
    $(window).mouseup(function(e) {
        //ウィンドウ上の全てのマウスアップを拾うんで、入力が始まってない時は無視する
        if (mouseDownPosition.left == 0 && mouseDownPosition.top == 0) return;
        var width = e.pageX - mouseDownPosition.left;
        var height = e.pageY - mouseDownPosition.top;
        var dist = Math.sqrt(Math.pow(width, 2) + Math.pow(height, 2));
        var msg = $('#msg2').val();
        var typedChar = '';
        //スレッショルドは約15ピクセル
        if (dist <= 15) {
            typedChar = map[id].charAt(0);
        } else {
            if (Math.abs(width) > Math.abs(height)) {
                //横向き
                if (width < 0) {
                    typedChar = map[id].charAt(1);
                } else {
                    typedChar = map[id].charAt(3);
                }
            } else {
                //縦向き
                if (height < 0) {
                    typedChar = map[id].charAt(2);
                } else {
                    typedChar = map[id].charAt(4);
                }
            }
        }
        if (typedChar != ' ') {
            msg = msg + typedChar;
        }
        $('#msg2').val(msg);
        //後始末
        $('.temporal').remove();
        mouseDownPosition.left = 0;
        mouseDownPosition.top = 0;
        //ボタンを背面に戻す
        $('#' + id).css({ 'z-index' : 10 });
    });
});

個人的な感想になるんやけど、JavaScript で書くとどんどん長いメソッドを書いちゃう。
きちんとテストコード書いてないんで余計なんやろけど、テスタビリティって観点以上にきちんとモジュール化をイメージしないと保守性も糞も無いのな。
積読状態に陥ってる「テスト駆動JavaScript 」と「JUnit実践入門 ~体系的に学ぶユニットテストの技法 (WEB+DB PRESS plus) 」をきちんと読み終えないとね。

ま、上のコードは技術的にどうのこうのってのは無いんで、ちゃちゃっと試してみるには良いかも知らん。
CSS がきちんと適用されてないと同じ感じにはならんやろけど。

そもそも自分のフリック入力練習用に作った奴なんで、本当に練習出来るようにガワもあつらえて Heroku にまたまた乗っけてみた。
「あいうえお」入力だと練習になんないんで、「いろは」を全部入力する。旧仮名もなぜか入ってる。(やゐゆゑよ)

ドライブする処理もフリック入力する処理も手抜きで完成度は低いんで、動かせないことは無いぐらいのレベルになってます。
練習のモチベーションを維持するために、目安になるカウンターを記録してて、jqPlot で折れ線グラフを書いてる。さらにパタパタするデジタルカウンターは flipcounter.js ってのをちょっとだけ改変して使ってる。

Live Demo(in Heroku)
http://stormy-headland-4608.herokuapp.com/

jqPlot
http://http://www.jqplot.com/

flipCounter
http://bloggingsquared.com/jquery/flipcounter/

「いろはにほへと」を全部入力する時間をきちんと時間で計ってないのは、手抜きだからです。時間とやる気が出たらやるかも知れないです。けれど、自分用のフリック入力練習アプリは出来上がったんで、もうやらないかも。

検索してくる人たちが本当にデスクトップパソコンでマウスを使った jQueryフリック入力プラグインを探してるんやとしたら、それはそれで不思議な感じがする。(興味本位で検索してるだけなら全然問題無い)
Windows8 みたいにデスクトップ機とタブレットのハイブリッドみたいな世界やと需要が出てくるんかなあ。でも、やっぱフリック入力Appleソフトウェア特許やろなあ。