IEでcanvas要素を使えるようにするexcanvas.js。r3以降、transformメソッドが実装されているようなので最新のをtrunkから取ってきて試してみた。おお、ちゃんと倉庫番が表示された。……でもむちゃくちゃ重いよ。一マス動くのに何秒かかっているのか計りたくもない。あまりの重さにタスクマネージャを開いてみたら、仮想メモリサイズ欄が800MB!! これはダメだ……。
クロスブラウザでこういうことをやるんだったらFlash(Flex SDK)でやるべきだよなぁ。手軽さは減るけど。
IEでcanvas要素を使えるようにするexcanvas.js。r3以降、transformメソッドが実装されているようなので最新のをtrunkから取ってきて試してみた。おお、ちゃんと倉庫番が表示された。……でもむちゃくちゃ重いよ。一マス動くのに何秒かかっているのか計りたくもない。あまりの重さにタスクマネージャを開いてみたら、仮想メモリサイズ欄が800MB!! これはダメだ……。
クロスブラウザでこういうことをやるんだったらFlash(Flex SDK)でやるべきだよなぁ。手軽さは減るけど。
ふへー、ようやく整理完了。一つ一つ動作確認をしながらの整理は大変だ。自動テストがあればなぁ。でもテストケースを用意するのも大変だけど。
マウスの要素内座標が取れるようになったので、簡単な例を一つ。
function getElementAbsPos(elem)
{
var x = 0;
var y = 0;
while(elem){
x += elem.offsetLeft;
y += elem.offsetTop;
elem = elem.offsetParent;
}
return {x:x, y:y};
}
function getMousePosOnElement(elem, ev)
{
if(!ev){ ev = event;}//for IE
if(elem.getBoundingClientRect){
var bcr = elem.getBoundingClientRect();
var x = ev.clientX - bcr.left;
var y = ev.clientY - bcr.top;
return {x:x, y:y};
}
else if(typeof(ev.pageX) == "number" && typeof(ev.pageY) == "number"){
var pos = getElementAbsPos(elem);
return {x:ev.pageX-pos.x, y:ev.pageY-pos.y};
}
else{
return {x:0, y:0};
}
}
var BORDER_WIDTH = 10;
var cv = document.createElement("canvas");
cv.setAttribute("width", "320");
cv.setAttribute("height", "240");
cv.style.cssText = "border: "+BORDER_WIDTH+"px solid;";
document.body.appendChild(cv);
var lastPoint = null;
cv.onmousedown = function(ev) {
lastPoint = getMousePosOnElement(cv, ev);
lastPoint.x -= BORDER_WIDTH;
lastPoint.y -= BORDER_WIDTH;
}
cv.onmousemove = function(ev) {
if(lastPoint){
var currPoint = getMousePosOnElement(cv, ev);
currPoint.x -= BORDER_WIDTH;
currPoint.y -= BORDER_WIDTH;
var ctx = cv.getContext("2d");
ctx.beginPath();
ctx.moveTo(lastPoint.x, lastPoint.y);
ctx.lineTo(currPoint.x, currPoint.y);
ctx.stroke();
lastPoint = currPoint;
}
}
cv.onmouseup = function(ev) {
lastPoint = null;
}
cv.onmouseout = function(ev) {
lastPoint = null;
}
例によってインタラクティブコンソールへ貼り付けて実行可能。
getMousePosOnElementより上は昨日書いたとおり。
borderを太くするとその分座標がずれてしまうので、そのあたりは適切に補正してやる必要がありました。
マウスイベントオブジェクトから得られる座標をある要素上の座標系へ変換したいのだが、これがなかなか簡単にはいかない。
色々調べてみるとgetBoundingClientRectというものがIEやFirefox、Operaでは使えるようなので、それを使うことにした。WebKitはつい最近実装したばかりのようだ。なので、getBoundingClientRectが使えればそれとclientX,clientYとの差分で求める。使えなければ、要素の絶対座標を適当に推測して、pageX,pageYとの差分で求める(IEはpageX,pageYをサポートしていないがgetBoundingClientRectの方を使うので問題ない)。
function getElementAbsPos(elem)
{
var x = 0;
var y = 0;
while(elem){
x += elem.offsetLeft;
y += elem.offsetTop;
elem = elem.offsetParent;
}
return {x:x, y:y};
}
function getMousePosOnElement(elem, ev)
{
if(!ev){ ev = event;}
if(elem.getBoundingClientRect){
var cr = elem.getBoundingClientRect();
var x = ev.clientX - cr.left;
var y = ev.clientY - cr.top;
return {x:x, y:y};
}
else if(typeof(ev.pageX) == "number" && typeof(ev.pageY) == "number"){
var pos = getElementAbsPos(elem);
return {x:ev.pageX-pos.x, y:ev.pageY-pos.y};
}
else{
return {x:0, y:0};
}
}
function onClick(elem, ev)
{
var pos = getMousePosOnElement(elem, ev);
alert(pos.x + "," + pos.y);
}
var elem = document.getElementById( ???? );
elem.onclick = function(ev) { onClick(elem, ev);}
とりあえず簡単なスクロール込みのhtmlでテストした感じでは大丈夫っぽい。CSSの指定によってはgetElementAbsPosあたりが怪しいけど。
マウスの要素内座標が簡単に求められないなんて、本当にどうかしていると思う。
今日もテキトーに問題を一つ作りました。
#######... #OO #... #OO #... # #... # @ #### # # #### B # ...# BB # ...# B # ...# # ...#######
ひょうたん型の地形が作りたかっただけです。こういう簡単なの、でも順序に少しだけ制約があって、あとは誤操作は気をつけなければならない程度の問題も良いですね。
ところで倉庫番って商標みたいなんですけど、言い換えってないんですかね。オセロに対するリバーシみたいな。
というわけで、一応動くようになったわけですが、別に倉庫番がやりたかったわけではなくて、倉庫番の問題を作りたかったんですね。倉庫番の問題というのはどうやって作るのか。どんな問題が良くて、どんな問題が良くないのか。
とりあえず一番単純なの。プレイは枠内をクリック。
######## #O B @ # ########
単純すぎ。
適当に五つほど箱を置いてみた。
############## #OO # OOO# #### B # # # # ### B## # B# # # # @ # # # # ### #B #B # # # # ##############
うーん、クリア不可能だ(と思う)。あと、やはり単純というか、すぐに結果が見えてしまうところも気になる。
ここで、最初から作り直すか、それとも、修正してみるか。
############## #OO # OOO# # ## B # # # # ### B## # B# # # # @ # # # ### #B #B # # # # ##############
クリアできるように修正してみた。壁に一つ穴を開けてはプレイしてクリアできるかどうか考えた。二つ開けて箱を動かしながら少し考えた時点でクリアできることに気がついた。気がつくとちょっと嬉しい。意外と難しくね?
というわけで、今日発見した制作メソッド。
もはやIEのことは全く考えておりません。
上のテキスト領域はキーフォーカス固定用。他に良い方法が分からなかったので仕方なく。
template<typename PixelType>
struct Processor
{
static void proc(PixelType *p)
{
proc_inner<PixelType::HAS_COLOR>(p);
}
private:
template<bool SUPPORTED_TYPE>
static void proc_inner(PixelType *p)
{
// Do not anything.
}
template<> // Error
static void proc_inner<true>(PixelType *p)
{
p->setRGB(1,0,0);
}
};
げー、これって二重の意味で規格違反だったのね。VCでは通るから自然に使ってた。クラススコープでtemplate<>と書けない(C++03 14.7.3)のと、外側のクラステンプレートを明示的に特殊化せずに、それに囲まれたクラスメンバテンプレートを明示的に特殊化してはならない(C++03 14.7.3/18)のと。
入れ子クラスの部分特殊化はできるのか……。うーん、面倒くさい。
CPUに依存しすぎ。というか依存する部分と依存しない部分が混在しすぎ。なんとか努力してみるけど、オペレーションが多すぎてくじけそうだ。長期にわたる建て増し構造だからなぁ。