投稿者のアーカイブ

XMLHTTPRequestとiframeとpostMessageでクロスドメイン通信

2012年11月23日 金曜日

http://vps.will-co21.net/test/html5proxy/postMessageCrossDomain.html?/formdatapost/tmp/hoge.png
html5のpostMessageとXMLHTTPRequestとiframeを組み合わせて画像のバイナリをXMLHTTPRequestで取得して
base64エンコードしたものをiframeからpostMessageで別ドメインの親ウィンドウに送って
dataURLにして画像として表示。
親ウィンドウ
window.onload = function () {
    require(["base64"], function (lib ) {
        var base64 = lib.base64;
       
        var iframe = document.createElement("iframe");
        iframe.src = "http://habrashi.s351.xrea.com/htmlproxy/proxy.html?" + encodeURIComponent(window.location.search.substr(1));
        iframe.setAttribute("id", "xhrframe");
        document.body.appendChild(iframe);
       
        window.addEventListener("message", function (e) {
            var data = e.data;
            img = document.createElement("img");
            img.src = "data:image/png;base64," + data;
            document.getElementById("content").appendChild(img);
        });
    });
}

window.onload = function () {
    var target = (parent && parent.postMessage ? parent : (parent && parent.document.postMessage ? parent.document : undefined));
    require(["base64"], function (lib ) {
        var xhr = new XMLHttpRequest();
        var base64 = lib.base64;

        xhr.onreadystatechange = function () {
            if(xhr.readyState == 4){
                var res = xhr.responseText;
                var bytes = [];
               
                for(var i=0, len = res.length; i < len; i++) bytes.push(String.fromCharCode(res.charCodeAt(i) & 0xff));
                var data = bytes.join("");
               
                base64String = base64.encode(data);
               
                if (typeof target != "undefined") {
                    target.postMessage(base64String, '*');
                }
            }
        }
        var url = decodeURIComponent(window.location.search.substr(1));
        xhr.open("GET", url, true);
        xhr.overrideMimeType("text/plain; charset=x-user-defined");
        xhr.send(null);
    });
}

子ウィンドウ
※require.jsとsmokescreenで使われてるbase64ライブラリを使用。
子ウィンドウをPHPにしてiframeをtargetとしてフォームデータをPOSTして、
POSTされたデータをJSONエンコードしてJSに渡せば、
POSTデータの中継なんかもできそう。

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

2012年11月18日 日曜日

function darker(src, dst, globalAlpha)
{  
    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.drawImage(src, 0, 0);

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

    var result = ctx2.getImageData(0, 0, w, h);
    var blend  = ctx3.getImageData(0, 0, w, h);
    var alpha;

    for(var data = result.data, blend = blend.data, i = 0 ; i < data.length ; i+=4)
    {
        if(typeof globalAlpha !== "undefined") gA = globalAlpha * 1;
        else gA = 1;
       
        alpha = blend[i+3] * gA;
        alpha = alpha / 255;

        if(data[i] == 0 && data[i+1] == 0 && data[i+2] == 0 && data[i+3] == 0)
        {
            data[i] = blend[i], data[i+1] = blend[i+1], data[i+2] = blend[i+2], data[i+3] = alpha * 255;
        }
        else
        {
            data[i] = ((data[i]) * (1 - alpha) + -blend[i] * (alpha));
            data[i+1] = ((data[i+1]) * (1 - alpha) + -blend[i+1] * (alpha));
            data[i+2] = ((data[i+2]) * (1 - alpha) + -blend[i+2] * (alpha));
            data[i+3] = 255;
        }
    }
   
    ctx1.putImageData(result, 0, 0);
   
    return src;
}

背景色が白以外の場合に動作がおかしかったのと、
globalAlphaへの対応を追加。
いろいろ計算式をいじっていてこれで動いたけど、
なぜこの計算式なのかは不明。
ChromeとSafariの実装にあわせたからこの式になったけど、
Firefoxと同じ結果になる式のほうが論理的に正しいのかもしれない。

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などで使えない場合にはこれが使える。