端末エミュレータもそっちのけで。3 つのボックス A、B、C を互いに線でつなぐ!?
canvas の弄り方が、まだ理解できていないようです。もう今日は力尽きました。
書けば書くほどに、「それってオブジェクトだよな」と考え出すと長くなり、現在収拾がつかない状態です。眠い。リポDも切れる。
三角形の辺(ちょっと改良)をクリックすると色が赤にかわり、ドラッグして形を変える。
力量の無さに orz
<!DOCTYPE html> <title>Test</title> <style> #HOGE { background : #eef; } </style> <body> <h1>PointModeler by Canvas</h1> <canvas id="HOGE" width="800" height="400"> canvas による描画 </canvas> <p> x = <input type="text" id="px" value="0"> / y = <input type="text" id="py" value="0"> / b = <input type="text" id="bt" value="0"> / d = <input type="text" id="dt" value="0"> </p> <script> /** * オブジェクトのクローンを作る */ (function () { function Qbject (arg) { if (arg) for (var p in arg) if (arg.hasOwnProperty (p)) this[p] = arg[p]; } Qbject.create = function (arg, args) { var F = new Function; F.prototype = arg; var o = new F; if (args) Qbject.call (o, args); return o; }; this.Qbject = Qbject; })(); //________________________________ /** * マウスの位置を監視する */ (function () { function MouseMonitor () { this.x = null; this.y = null; this.currentNode = null; this.button = false; } function create (view) { if (1 > arguments.length) view = window; var doc = view.document; var pointer = new MouseMonitor; function onMouseEvents (event) { switch (event.type) { case 'mousemove' : /*@cc_on @if (1) var n = event.srcElement; var c = ('CSS1Compat' === doc.compatMode) ? doc.documentElement : doc.body; pointer.x = event.clientX + c.scrollLeft; pointer.y = event.clientY + c.scrollTop; pointer.currentNode = n; @else@*/ pointer.x = event.clientX + view.pageXOffset; pointer.y = event.clientY + view.pageYOffset; pointer.currentNode = event.target; /*@end@*/ break; case 'mousedown' : pointer.shiftKey = event.shiftKey; pointer.ctrlKey = event.ctrlKey; pointer.altKey = event.altKey; pointer.button = true; pointer.currentNode = event.target; break; case 'mouseup' : pointer.shiftKey = event.shiftKey; pointer.ctrlKey = event.ctrlKey; pointer.altKey = event.altKey; pointer.button = false; pointer.currentNode = event.target; break; case 'unload' : doc.removeEventListener('mousemove', arguments.callee, false); doc.removeEventListener('mousedown', arguments.callee, false); doc.removeEventListener('mouseup', arguments.callee, false); view.removeEventListener('unload', arguments.callee, false); view = doc = pointer = onMouseEvents = null; } } doc.addEventListener ('mousemove', onMouseEvents, false); doc.addEventListener ('mousedown', onMouseEvents, false); doc.addEventListener ('mouseup', onMouseEvents, false); view.addEventListener ('unload', onMouseEvents, false); return pointer; } MouseMonitor.create = create; //this.MouseMonitor = MouseMonitor; this.MouseMonitor = MouseMonitor.create (this); // view は window のみということで })(); //________________________________ /** * 与えられたHTML要素の位置を基準に、オフセット位置を返す */ (function () { function Converter (node, offset, currentFlag) { this.node = node; this.currentFlag = !!currentFlag; this.offset = offset; } function getPosition (base) { if (1 > arguments.length) throw new Error ('引数が無い'); if (this.currentFlag) refresh.call (this); return { 'x': base.x -this.offset.x, 'y': base.y - this.offset.y }; } function refresh () { this.offset = getElementPosition (this.node); } function getElementPosition (node) { var x = 0; var y = 0; while (node) { x += node.offsetLeft; y += node.offsetTop; node = node.offsetParent; } return { 'x': x, 'y': y }; }; function OffsetConverter () { ;} function create (target, currentFlag) { if (1 > arguments.length) throw new Error ('ノードがない'); var position = getElementPosition (target); var obj = new Converter (target, position, currentFlag); var func = new Function; func.getPosition = (function () { return getPosition.apply (obj, arguments); }); func.refresh = (function () { refresh.apply (obj, arguments); }); return func; } OffsetConverter.create = create; this.OffsetConverter = OffsetConverter; })(); //________________________________ /** * 多角形 */ (function () { var DEFAULT_POINTS = [{x:50, y:50}, {x:150, y:50}, {x:100, y:136.3}]; var DEFAULT_COLOR = { 'strok': 'rgb(0, 0, 0)', 'fill': null }; var SELECTED_COLOR = { 'strok': 'rgb(255, 0, 0)', 'fill': 'rgb(255, 0, 0)' }; var SELECTED_POINT_RADIUS = 5; function Polygon (name, points, color, selected) { this.name = name; this.points = points; this.color = color; this.selected = selected; } function draw (ctrx) { var fill; if (this.selected) { this.points.forEach (function (p) { this.beginPath (); this.fillStyle = SELECTED_COLOR.fill; this.arc (p.x, p.y, SELECTED_POINT_RADIUS, 0, Math.PI*2, false); this.fill (); }, ctrx); ctrx.strokeStyle = SELECTED_COLOR.strok; ctrx.beginPath (); this.points.forEach (function (p) { this.lineTo (p.x, p.y); }, ctrx); ctrx.closePath (); ctrx.stroke (); } else { ctrx.strokeStyle = this.color.strok; if ((fill = this.color.fill)) ctrx.fillStyle = fill; ctrx.beginPath (); this.points.forEach (function (p) { this.lineTo (p.x, p.y); }, ctrx); ctrx.closePath (); ctrx.stroke (); if (fill) this.ctrx.fill (); } } function isSelected (point, range) { var i = 0, I = this.points.length; var p0 = this.points[I-1]; var p1; var r; range = (range) ? range: 500; for (; i < I ; i++) { p1 = this.points[i]; r = Math.round (((p1.x - p0.x) * (point.y - p0.y)) / range - ((point.x - p0.x) * (p1.y - p0.y)) / range); if (0 === r) return true; p0 = p1; } return false; } function hasInRange (point, range) { if (2 > arguments.length) range = { 'x': SELECTED_POINT_RADIUS, 'y': SELECTED_POINT_RADIUS }; var L = point.x - range.x; var R = point.x + range.x; var T = point.y - range.y; var B = point.y + range.y; var x, y; for (var i = 0, p; p = this.points[i]; i++) if (L < (x = p.x) && (x < R)) if (T < (y = p.y) && (y < B)) return { 'index': i }; return null; } function create (name, points, color, selected) { if ('undefined' === typeof points) points = DEFAULT_POINTS.splice (0); if ('undefined' === typeof color) color = { 'strok': DEFAULT_COLOR.strok, 'fill': DEFAULT_COLOR.fill }; var polygon = new Polygon (name, points, color, !!selected); polygon.draw = (function () { draw.apply (polygon, arguments); }); polygon.hasInRange = (function () { return hasInRange.apply (polygon, arguments); }); polygon.isSelected = (function () { return isSelected.apply (polygon, arguments); }); return polygon; } Polygon.create = create; this.Polygon = Polygon; })(); //________________________________ /** * 与えられたHTML要素の位置を基準に、オフセット位置を返す * */ (function () { function PointModeler (canvas) { this.canvas = canvas; this.ctrx = canvas.getContext ('2d'); this.stock = []; this.current = null; this.mode = null; } // 登録されているモデルを全て描画する function allDraw (override) { if (! override) { clearScreen.call (this); } this.stock.forEach (function (md) { md.draw (this); }, this.ctrx); } // キャンバス領域を削除する function clearScreen (draw) { this.ctrx.clearRect (0, 0, this.canvas.width, this.canvas.height); if (draw) allDraw.call (this); } // モデルを追加する function add (name, points, color, selected) { var polygon = Polygon.create (name, points, color, selected); this.stock.push (polygon); polygon.draw (this.ctrx); } function click (position) { var r; for (var i = 0, md; md = this.stock[i]; i++) { if (md.isSelected (position)) { md.selected = ! md.selected; clearScreen.call (this, true); this.current = md; return; } } if (this.current) { this.current.selected = false; this.ctrx.clearRect (0, 0, this.canvas.width, this.canvas.height); this.current.draw (this.ctrx); this.current = null; } } function up (position) { if (this.mode && this.mode.state == 'move') { this.current.points[this.mode.index] = position; this.ctrx.clearRect (0, 0, this.canvas.width, this.canvas.height); this.current.draw (this.ctrx); } this.mode = null; } function move (position) { if (this.mode && this.mode.state == 'move') { this.current.points[this.mode.index] = position; this.ctrx.clearRect (0, 0, this.canvas.width, this.canvas.height); this.current.draw (this.ctrx); } } function down (position) { var r; if (! this.mode) { for (var i = 0, md; md = this.stock[i]; i++) { if (md.selected && (r = md.hasInRange (position))) { if (md == this.current) { if (md.selected) { this.mode = { 'state': 'move', 'index': r.index }; return; } } } } } } function PointModeler_create (canvas) { var pointModeler; var func = new Function; if (canvas && canvas.getContext) pointModeler = new PointModeler (canvas); else throw new Erorr ('利用できません'); func.add = (function () { add.apply (pointModeler, arguments); }); func.click = (function () { click.apply (pointModeler, arguments); }); func.down = (function () { down.apply (pointModeler, arguments); }); func.up = (function () { up.apply (pointModeler, arguments); }); func.move = (function () { move.apply (pointModeler, arguments); }); func.clearScreen = (function () { clearScreen.apply (pointModeler, arguments); }); func.allDraw = (function () { allDraw.apply (pointModeler, arguments); }); return func; } PointModeler.create = PointModeler_create; this.PointModeler = PointModeler; })(); //________________________________ (function () { var target = document.getElementById ('HOGE'); var offset = OffsetConverter.create (target); var mouse = MouseMonitor; var modeler= PointModeler.create (target); var count = 0; var mx, my; var x, y; modeler.add (); setInterval (function () { var position = offset.getPosition (mouse); document.getElementById ('px').value = x = position.x; document.getElementById ('py').value = y = position.y; document.getElementById ('bt').value = count; if (mouse.button) { if (count++) modeler.down (position); if ((x !== mx) || (y !== my)) { mx = x; my = y; modeler.move (position); } } else { if (0 < count) { modeler.up (position); modeler.click (position); count = 0; } } }, 20); })(); </script>