/* HTML5 Canvas で drag&drop 共通部分 */ var dtcom = {}; // namespace (function($) { /* アイテムクラス * {int} x アイテムの中心x座標 * {int} y アイテムの中心y座標 * {string} type アイテムの図形タイプ */ dtcom.item = function(x, y, type) { this.x = x; this.y = y; this.type = type; }; /* Canvasクラス * {canvas context} ctx 処理対象となるcontext * {int} w 表示領域の幅 * {int} h 表示領域の高さ * {objext} names 呼び出し元のnamespace */ dtcom.canv = function(ctx, w, h, names) { this.ctx = ctx; // 処理対象context this.area = {w:w, h:h}; // 表示エリア this.cvpos = {x:0, y:0}; // ブラウザ上のCanvas左上座標 this.grSep = 30; // グリッド間隔(px) this.grSephf = this.grSep / 2; // グリッド間隔の半分 this.grWidth = 1; // グリッド線の太さ this.grXAr = []; // グリッドのX座標を格納した配列 this.grRcv = false; // canvasの右端がグリッドと重なるならtrue this.grYAr = []; // グリッドのY座標を格納した配列 this.grBcv = false; // canvasの下端がグリッドと重なるならtrue this.itemAr = []; // アイテム格納配列 /* ブラウザ上のCanvas左上座標を求める */ var $cvdiv = $('#' + names.con.id.cvdiv); this.cvpos.x = $cvdiv.offset().left; this.cvpos.y = $cvdiv.offset().top; /* グリッド座標計算 */ var gcntx = 0; // 縦線本数 if (this.area.w % this.grSep) { gcntx = Math.floor(this.area.w / this.grSep); } else { // canvasの右端がグリッドと重なる場合 gcntx = this.area.w / this.grSep - 1; this.grRcv = true; } for (var i = 0; i < gcntx; i++) { this.grXAr[i] = this.grSep * (i + 1); } var gcnty = 0; // 横線本数 if (this.area.h % this.grSep) { gcnty = Math.floor(this.area.h / this.grSep); } else { // canvasの下端がグリッドと重なる場合 gcnty = this.area.h / this.grSep - 1; this.grBcv = true; } for (var i = 0; i < gcnty; i++) { this.grYAr[i] = this.grSep * (i + 1); } /* 初期処理 * return なし */ this.init = function() { // アイテム初期配置 this.itemAr = []; this.itemAr[0] = new dtcom.item(this.grSephf, this.grSephf, names.con.dtype.cir); this.itemAr[1] = new dtcom.item(this.grSephf, this.grSep + this.grSephf, names.con.dtype.tri); this.itemAr[2] = new dtcom.item(this.grSephf, this.grSep * 2 + this.grSephf, names.con.dtype.squ); } /* 画面を初期化 * return なし */ this.blank = function() { // 画面をクリア this.ctx.clearRect(0, 0, this.area.w, this.area.h); // グリッド表示 this.ctx.save(); this.ctx.globalAlpha = 0.5; this.ctx.strokeStyle = "#000033"; this.ctx.lineWidth = this.grWidth; for (var i = 0; i < this.grXAr.length; i++) { this.ctx.beginPath(); this.ctx.moveTo(this.grXAr[i], 0); this.ctx.lineTo(this.grXAr[i], this.area.h); this.ctx.stroke(); } for (var i = 0; i < this.grYAr.length; i++) { this.ctx.beginPath(); this.ctx.moveTo(0,this.grYAr[i]); this.ctx.lineTo(this.area.w, this.grYAr[i]); this.ctx.stroke(); } this.ctx.restore(); }; /* 画面表示 * return なし */ this.draw = function() { // 画面を初期化 this.blank(); // アイテム配置 for (var i = 0; i < this.itemAr.length; i++) { switch (this.itemAr[i].type) { case names.con.dtype.cir: { this.drawCir(this.itemAr[i].x, this.itemAr[i].y); break; } case names.con.dtype.tri: { this.drawTri(this.itemAr[i].x, this.itemAr[i].y); break; } case names.con.dtype.squ: { this.drawSqu(this.itemAr[i].x, this.itemAr[i].y); break; } default: { this.drawCir(this.itemAr[i].x, this.itemAr[i].y); break; } } } }; /* 毎回計算しないように外に記述 */ this.linelng = this.grSep / 2 - 1; // よく使う長さ this.PI2 = Math.PI * 2; // 2π // 正三角形の高さ÷2 this.trhghthf = this.linelng * Math.tan(Math.PI / 3) / 2; /* 直径 this.grSep - 2 の円を描く * {int} x 中心のx座標 * {int} y 中心のy座標 * return なし */ this.drawCir = function(x,y) { this.ctx.save(); this.ctx.fillStyle = 'green'; this.ctx.beginPath(); this.ctx.arc(x, y, this.linelng, 0, this.PI2, false); this.ctx.fill(); this.ctx.restore(); }; /* 辺の長さ this.grSep - 2 の正三角形を描く * {int} x 中心のx座標 * {int} y 中心のy座標 * return なし */ this.drawTri = function(x,y) { this.ctx.save(); this.ctx.fillStyle = 'blue'; this.ctx.beginPath(); this.ctx.moveTo(x, y - this.trhghthf); this.ctx.lineTo(x - this.linelng, y + this.trhghthf); this.ctx.lineTo(x + this.linelng, y + this.trhghthf); this.ctx.closePath(); this.ctx.fill(); this.ctx.restore(); }; /* 辺の長さ this.grSep - 2 の正方形を描く * {int} x 中心のx座標 * {int} y 中心のy座標 * return なし */ this.drawSqu = function(x,y) { this.ctx.save(); this.ctx.fillStyle = 'purple'; this.ctx.beginPath(); this.ctx.moveTo(x - this.linelng, y - this.linelng); this.ctx.lineTo(x - this.linelng, y + this.linelng); this.ctx.lineTo(x + this.linelng, y + this.linelng); this.ctx.lineTo(x + this.linelng, y - this.linelng); this.ctx.closePath(); this.ctx.fill(); this.ctx.restore(); }; /* アイテムが指定した座標内にあるか判定 * {int} x x座標 * {int} y y座標 * return アイテムがあればthis.itemArのindex アイテムが無ければnull */ this.checkItem = function(x, y) { var rtn = null; for (var i = 0; i < this.itemAr.length; i++) { if (x >= this.itemAr[i].x - this.grSephf && x <= this.itemAr[i].x + this.grSephf) { if (y >= this.itemAr[i].y - this.grSephf && y <= this.itemAr[i].y + this.grSephf) { rtn = i; break; } } } return rtn; }; /* アイテムの座標をグリッドの中央にセット * {int} index 対象アイテムのthis.itemArのindex * {int} x 現在の中心x座標 * {int} y 現在の中心y座標 * return なし */ this.setCenter = function(index, x, y) { // 現在の座標がCanvas外の場合は、端のグリッドにセット var gidx = Math.floor(x / this.grSep); // グリッド配列のindex if (gidx < 0 ) { gidx = 0; } else if (gidx >= this.grXAr.length && !this.grRcv) { gidx = this.grXAr.length - 1; } if (gidx < this.grXAr.length) { this.itemAr[index].x = this.grXAr[gidx] - this.grSephf; } else { // canvasの右端がグリッドと重なる場合 this.itemAr[index].x = this.grXAr[this.grXAr.length - 1] + this.grSephf; } gidx = Math.floor(y / this.grSep); if (gidx < 0 ) { gidx = 0; } else if (gidx >= this.grYAr.length && !this.grBcv) { gidx = this.grYAr.length - 1; } if (gidx < this.grYAr.length) { this.itemAr[index].y = this.grYAr[gidx] - this.grSephf; } else { // canvasの下端がグリッドと重なる場合 this.itemAr[index].y = this.grYAr[this.grYAr.length - 1] + this.grSephf; } }; } })(jQuery);