‘JavaScript’ カテゴリーのアーカイブ

タッチイベントでもマウスイベントでも操作できるUIを作る実装方法

2015年3月18日 水曜日
  • 序文
  • スマートフォンの普及によってタッチイベントをJavascriptから扱うようになって久しいですが、
    世の中には「タッチイベントもマウスイベントもサポートしている端末」というものがあります。
    そして、こういう端末はマウスでクリックすると「touchstartに続けてmousedownイベントが自動で発火する」
    という困った特徴があったりします。
    windows8もその一つです。
    マウスでもタッチでも操作できるのがこういう端末のUIとしては理想ですが、実装が難しく、
    タッチのみに対応させるのが関の山でした(タッチをサポートしてる端末を判定してタッチで処理させる実装)
    マウスのみサポートだと、スマートフォンで動きませんからね。。。

    しかし、ようやく、この問題をどうにかクリアできる実装を考え出せたっぽいので、ここに公開します。

  • イベントはtouchstart⇒mousedownの順で発生するっぽい
  • なので、まずイベントハンドラの一つ外側のスコープに即時関数を定義して、
    そこにイベントタイプ記録用の変数を用意してnullで初期化します。

  • イベントタイプを判定する
  • イベントハンドラ先頭で保存したイベントタイプ文字列がtouchstartで、かつ現在のイベントタイプがmousedownか判定する。
    判定処理を先に書いて判定結果が真なら何もせずにreturnする(必要に応じてpreventDefaultも実行)

  • 現在のイベントタイプ文字列を保存する
  • イベントハンドラの引数にeを書いてe.typeで取れます。

  • 保存したイベントタイプをnullクリア
  • ここが重要です。
    クリアしないと次回以降正常動作してくれませんが、クリアをすぐに行ってはいけません。
    クリア後にmousedownが発生してしまいます。

  • setTimeout!
  • はい。「300ミリ秒後」かそれ以上後の時間を設定してsetTimeout内でクリアします。
    これで最初のイベントだけを処理対象にできます。

  • 応用
  • フリックスクロールをタッチ、マウス両方で行う方法です。

    イベントタイプの保存は同じですが、
    touchstart、mousedown時に保存したイベントタイプを
    「touchendかmouseup」でnullクリアします。
    ※タッチ、マウスの座標の取り方の違いを吸収する実装も忘れずに!
    さらに、touchmoveでは保存したイベントタイプがtouchstartである場合だけ、
    mousemoveでは保存したイベントタイプがmousedownである場合だけ処理を行います。
    touchend、mouseupも同様に保存したイベントタイプを見て対応するデバイスのイベントだった場合のみ処理を行います。

  • 実際の利用例
  • 僕の公開しているSNSのタイムラインクリック時にメニューを開く実装で利用しています。
    (ブラウザのウィンドウ内の高さが狭くなって「返信」ボタンが消えてる状態で機能します)

    SNSは以下。

    todays!

globalCompositeOperation=”darker”が使えないブラウザ用の自前JS実装

2012年11月17日 土曜日

function darker(src, dst)
{  
    var w,h;
   
    if(src.width > dst.width)
    {
        w = src.width;
    }
    else
    {
        w = dst.width;
    }
   
    if(src.height > dst.height)
    {
        h = src.height;
    }
    else
    {
        h = dst.height;
    }

    var ctx1 = src.getContext("2d");
    var canvas = document.createElement("canvas");
    canvas.setAttribute("width", w);
    canvas.setAttribute("height", h);
    ctx2 = canvas.getContext("2d");
    ctx2.fillStyle = "rgba(255,255,255,1)";
    ctx2.fillRect(0, 0, w, h);
    ctx2.drawImage(src, 0, 0);

    var canvas2 = document.createElement("canvas");
    canvas2.setAttribute("width", w);
    canvas2.setAttribute("height", h);
    ctx3 = canvas2.getContext("2d");
    ctx3.fillStyle = "rgba(255,255,255,1)";
    ctx3.fillRect(0, 0, w, h);
    ctx3.drawImage(dst, 0, 0);

    var result = ctx2.getImageData(0, 0, w, h);
    var blend = ctx3.getImageData(0, 0, w, h);
   
    var aO;
   
    for(var data = result.data, blend = blend.data, i = 0 ; i < data.length ; i+=4)
    {
        aO = (data[i+3]) + (blend[i+3]);
        data[i] = 255 - ((255 - data[i]) * (data[i+3]) + (255 - blend[i]) * (blend[i+3]));
        data[i+1] = 255 - ((255 - data[i+1]) * (data[i+3]) + (255 - blend[i+1]) * (blend[i+3]));
        data[i+2] = 255 - ((255 - data[i+2]) * (data[i+3]) + (255 - blend[i+2]) * (blend[i+3]));
    }
   
    ctx1.putImageData(result, 0, 0);
   
    return src;
}

globalAlphaを使ったときと同じ動作にするためには、
dst側のalphaの値をglobalAlphaの値で置き換えればいけるんじゃないかと思うけどまだ未確認。

[JavaScript]ユニークIDを発行する関数を生成する

2012年11月10日 土曜日

var Uuid = (function () { var i=0; return function () { return ++i; }; })();

さらにeval,String.replaceと組み合わせて

var params = ["hoge", "fuga", "aaa"];
Class = function () {};
Class.reflection = function (classname, params) {
    var args = [];
    for(var i=0; i < params.length; i++) args.push("params[%d]");
    return eval("new " + classname + "(" + args.join(",").replace(/%d/g, (function () {
        var i=0; return function () { return i++; };
    })()) + ")");
}
var a = Class.reflection("Array", params);

クラス名文字列とパラメータの配列からクラスを生成するリフレクション
Function.bindを利用しても同じことはできるけど、IEなどで使えない場合にはこれが使える。