javascript - 无法选择网格中的六边形

标签 javascript html

我已经查看我的代码几个小时了,但我仍然无法弄清楚它到底出了什么问题。

我创建了一个非常简单版本的六边形网格系统。我想选择网格内的任何六边形。所有六边形都正确显示,只是当我单击网格时,选择了不正确的六边形。它的行为就像鼠标位置不正确一样,或者 也许六边形的位置数据不正确(?)。 但为什么它们的位置是正确的呢?

////////// [ Hexagon ] ////////////////////
function Hexagon( options ){ 
	if( options !== "undefined" ){
		this.attributes = {
			type : options.type || 0, //// 0 is cell , 1 player /// Default : 0 ////
			id: options.id,
			color : this.getColor(),
			coords: [], //// [ r, q ] /// row / col ///
			points: [],
			pos: options.pos,
			size: options.size
		};
		
		this.states = {
			selected: false
		};
		
		//// make short-cuts to frequently used attributes ////
		this.pos = this.attributes.pos;
		this.coords = this.attributes.coords;
		this.size = this.attributes.size;
		this.points = this.attributes.points;
		
		///// caclulate top_left, bottom and center points ////
		this.TopLeftPoint = [ this.pos[0], this.pos[1] ];
		this.BottomRightPoint = [ this.pos[0] + this.size.w, this.pos[1] + this.size.h ];
		this.MidPoint = [ this.pos[0] + (this.size.w / 2), this.pos[1] + (this.size.h / 2) ];
		
		///////// generate points ///////
		this.generate(); 
	}
}

Hexagon.prototype = {
	constructor : Hexagon,
	changeState: function( st_name, st_value ){
		if( this.checkState( st_name ) ) {
			this.states[st_name] = st_value;
		}
	},
	checkState: function( st_name ){
		if( typeof this.states[st_name] !== "undefined" ) {
			return  this.states[st_name];
		}
		return false;
	},
	isInHexBounds : function( p ){  /*Point*/
		if(this.TopLeftPoint[0] < p[0] && this.TopLeftPoint[1] < p[1] && p[0] < this.BottomRightPoint[0] && p[1] < this.BottomRightPoint[0]){
			return true;
		}
		return false;
	},
	contains: function( p ) {
		var isIn = false;
		if( this.isInHexBounds( p ) ){
			var i, j = 0;
			for (i = 0, j = this.points.length - 1; i < this.points.length; j = i++){
				var iP = this.points[i];
				var jP = this.points[j];
				if (
					( ((iP[1] <= p[1]) && (p[1] < jP[1])) || ((jP[1] <= p[1]) && (p[1] < iP[1]))) && (p[0] < (jP[0] - iP[0]) * (p[1] - iP[1]) / (jP[1] - iP[1]) + iP[0])
				){
					isIn = !isIn;
				}
			}
		}
		return isIn;
	},
	getColor: function( ){
		switch( this.type ){
			case 0: 
				return "blue";
			case 1: 
				return "red";
			default:
				return "yellow";
		}
	},
	trigger: function( e_name ){
		this.events[ e_name ].call(this);
	},
	events: {
		"select" : function(){
			if( ! this.checkState( "selected" ) ){
				this.changeState("selected", true);
				//console.log( this.coords )
				this.type = 1;
			}
		}
	},
	setType: function( type ){
		this.attributes.type = type;
	},
	generate: function(){///// generate hexagon points //////
		var x1 = (this.size.w - this.size.s)/2;
		var y1 =  (this.size.h / 2);
		this.points.push( 
			[ x1 + this.pos[0], this.pos[1] ],
			[ x1 + this.size.s + this.pos[0], this.pos[1] ],
			[ this.size.w + this.pos[0], y1 + this.pos[1] ],
			[ x1 + this.size.s + this.pos[0], this.size.h + this.pos[1] ],
			[ x1 + this.pos[0], this.size.h + this.pos[1] ],
			[ this.pos[0], y1 + this.pos[1] ]
		);
	},
	draw : function( ctx ){
		if( this.type > 0 ){
			ctx.globalAlpha = 0.5;
			ctx.fillStyle = this.color;
			ctx.fill();
			ctx.globalAlpha = 1.0;
		}else{
			ctx.strokeStyle = "grey";
		}
		
		//ctx.rect( this.BottomRightPoint[0],this.BottomRightPoint[1],4,4);
		//ctx.stroke();
		
		ctx.lineWidth = 1;
		ctx.beginPath();
		ctx.moveTo( this.points[0][0], this.points[0][1] );
		for( var c=1; c < this.points.length; c++ ){
			ctx.lineTo( this.points[c][0], this.points[c][1] );
		}
		ctx.closePath();
		ctx.stroke();
		
		this.draw_coords( ctx );
	},
	draw_coords: function( ctx ){
		ctx.font="10px Georgia";
		ctx.textAlign = "center";
		ctx.textBaseline = 'middle';
		ctx.fillStyle = "blue";
		ctx.fillText(this.coords[0]+" , "+this.coords[1], this.MidPoint[0], this.MidPoint[1]);
	}
}


///////// [ Grid ] /////////////////////
function Grid( options ){
	if(typeof options !== "undefined"){
		this.size = {
			width: options.size[0],
			height: options.size[1]
		};
		
		//this.mouse_pos = [];
		
		this.pos = options.pos; //// position within the canvas /// [ x , y ] ////
		this.ctx = options.ctx;
		this.ctx_pos = options.ctx_pos; //// position of canvas element /// [ left, top] ///
		this.hex_size = this.calculate_hex_size( options.hex_def );
		this.hexagons = []; //// [ row, col ] /////  just a temporary array  ////
		this.grid2D = []; ///// includes all hexagons to be drawn ///
	}
	this.generate();
	this.animate();
	this.enable_mouse_events();
}

Grid.prototype = {
	constructor : Grid,
	
	generate : function(){
		var hex_pos_x = 0.0, hex_pos_y = 0.0, row = 0, col = 0, offset = 0.0, h = null, h_id = 0; 
		while( (hex_pos_y + this.hex_size.h) <= this.size.height ){
			col = 0; //// reset col 
			offset = 0.0; //// reset offset 
			
			if( (row % 2) == 1){
				offset = ( ( this.hex_size.w - this.hex_size.s ) /2 ) + this.hex_size.s ;
				col = 1;
			}
			
			hex_pos_x = offset;
			
			while( (hex_pos_x  + this.hex_size.w) <= this.size.width ){
				h =  new Hexagon( { pos : [ hex_pos_x, hex_pos_y ], size: this.hex_size , id: row+""+col,  type: 0 }); 
				h.coords[0] = col; //// set coord X ///
				
				this.grid2D.push( h );
				if( ! this.hexagons[col] ){
					this.hexagons[col] = [];
				}
				this.hexagons[col].push( h );
		
				col += 2;
				hex_pos_x += (this.hex_size.w + this.hex_size.s);
			}
			
			row++;
			
			hex_pos_y += (this.hex_size.h / 2);
		}
		
		////finally go through our list of hexagons by their x co-ordinate to assign the y co-ordinate
		var coordX = 0, coordY = 0, h_l =  this.hexagons.length, hex_arr = [];
		for(  ; coordX <  h_l; coordX++ ){
			hex_arr =  this.hexagons[ coordX ];
			coordY =  Math.floor( (coordX / 2 ) + (coordX % 2) );
			for( var h = 0, size = hex_arr.length; h < size; h++ ){
				hex_arr[h].coords[1] = coordY++;
			}
		}
	},
	
	getHexAt: function( p ){ //// point [ x, y ]
		for ( var h = 0, h_l = this.grid2D.length; h < h_l; h++ ){
			if ( this.grid2D[h].contains( p ) ){
				return this.grid2D[h];
			}
		}
		return null;
	},
	
	animate: function(){
		var self = this;
		window.requestAnimationFrame( function(){
			self.animate();
		});
		self.draw();
	},

	draw : function( ){
		this.ctx.clearRect(0, 0, this.size.width, this.size.height);
		for( var h = 0, h_l = this.grid2D.length; h < h_l; h++ ){
			this.grid2D[h].draw( this.ctx );
		}
	},

	calculate_hex_size : function( hex_def ){
		return {
			w: hex_def.radius * 2,
			m: hex_def.margin,
			h: (Math.sqrt(3) / 2) * ( hex_def.radius * 2),
			r: hex_def.radius,
			s: hex_def.radius
		}
	},
	
	enable_mouse_events: function(){
		var self = this;
		var mouse_pos = [];
		var cur_hex = null;
		window.addEventListener( 'mousemove', function(e){
			mouse_pos  =  [ ( e.clientX - self.ctx_pos[0] ), ( e.clientY - self.ctx_pos[1] )];
			//self.mouse_pos = mouse_pos;
		});
		
		window.addEventListener( 'mousedown', function(e){
			if( mouse_pos.length > 0 ){
				cur_hex = self.getHexAt( mouse_pos );
				if( cur_hex != null ){
					cur_hex.trigger("select");
				}
			}
		});
	}
}

	var c_el = document.getElementById("myCanvas");
	var ctx = c_el.getContext("2d");
	var nGrid = new Grid({
		/// size : [ c_el.width, c_el.height ], /// [rows / cols ] //// 20 px x 10 px///
		size : [ 70 , 70 ], 
		pos: [ 20, 20 ], /// [X, Y] ////
		hex_def: {
			radius: 20,
			margin: 0
		},
		ctx : ctx,
		ctx_pos: [ c_el.getBoundingClientRect().left, c_el.getBoundingClientRect().top ]
	});
<body  stye="width: 100%; height: 100%" >
	<canvas id="myCanvas" width="750px" height="405px" style="margin:0; padding:0; border:1px solid #d3d3d3;"></canvas>
</body>

最佳答案

事实证明我混淆了某些上下文方法的顺序。因此它在 ctx.begonPath()/ctx.closePath() 之前调用 ctx.fill()。这是错误的,因此任何先前绘制的六边形都会被填充,作为此错误的副作用。
一旦我在 ctx.begonPath()/ctx.closePath() 之后添加了 ctx.fill() ,一切都运行良好。

请参阅下面的结果。

////////// [ Hexagon ] ////////////////////
function Hexagon( options ){ 
	if( options !== "undefined" ){
		this.attributes = {
			type : options.type || 0, //// 0 is cell , 1 player /// Default : 0 ////
			id: options.id,
			color : this.getColor(),
			coords: [], //// [ r, q ] /// row / col ///
			points: [],
			pos: options.pos,
			size: options.size
		};
		
		this.states = {
			selected: false
		};
		
		//// make short-cuts to frequently used attributes ////
		this.pos = this.attributes.pos;
		this.coords = this.attributes.coords;
		this.size = this.attributes.size;
		this.points = this.attributes.points;
		
		///// caclulate top_left, bottom and center points ////
		this.TopLeftPoint = [ this.pos[0], this.pos[1] ];
		this.BottomRightPoint = [ this.pos[0] + this.size.w, this.pos[1] + this.size.h ];
		this.MidPoint = [ this.pos[0] + (this.size.w / 2), this.pos[1] + (this.size.h / 2) ];
		
		///////// generate points ///////
		this.generate(); 
	}
}

Hexagon.prototype = {
	constructor : Hexagon,
	changeState: function( st_name, st_value ){
		if( this.checkState( st_name ) ) {
			this.states[st_name] = st_value;
		}
	},
	checkState: function( st_name ){
		if( typeof this.states[st_name] !== "undefined" ) {
			return  this.states[st_name];
		}
		return false;
	},
	isInHexBounds : function( p ){  /*Point*/
		if(this.TopLeftPoint[0] < p[0] && this.TopLeftPoint[1] < p[1] && p[0] < this.BottomRightPoint[0] && p[1] < this.BottomRightPoint[1]){
			return true;
		}
		return false;
	},
	contains: function( p ) {
		var isIn = false;
		if( this.isInHexBounds( p ) ){
			var i, j = 0;
			for (i = 0, j = this.points.length - 1; i < this.points.length; j = i++){
				var iP = this.points[i];
				var jP = this.points[j];
				if (
					( ((iP[1] <= p[1]) && (p[1] < jP[1])) || ((jP[1] <= p[1]) && (p[1] < iP[1])) ) && (p[0] < (jP[0] - iP[0]) * (p[1] - iP[1]) / (jP[1] - iP[1]) + iP[0])
				){
					isIn = !isIn;
				}
			}
		}
		return isIn;
	},
	getColor: function( ){
		switch( this.type ){
			case 0: 
				return "blue";
			case 1: 
				return "red";
			default:
				return "yellow";
		}
	},
	trigger: function( e_name ){
		this.events[ e_name ].call(this);
	},
	events: {
		"select" : function(){
			if( ! this.checkState( "selected" ) ){
				this.changeState("selected", true);
				//console.log( this.coords )
				this.type = 1;
			}
		}
	},
	setType: function( type ){
		this.attributes.type = type;
	},
	generate: function(){///// generate hexagon points //////
		var x1 = (this.size.w - this.size.s)/2;
		var y1 =  (this.size.h / 2);
		this.points.push( 
			[ x1 + this.pos[0], this.pos[1] ],
			[ x1 + this.size.s + this.pos[0], this.pos[1] ],
			[ this.size.w + this.pos[0], y1 + this.pos[1] ],
			[ x1 + this.size.s + this.pos[0], this.size.h + this.pos[1] ],
			[ x1 + this.pos[0], this.size.h + this.pos[1] ],
			[ this.pos[0], y1 + this.pos[1] ]
		);
	},
	draw : function( ctx ){
		
		ctx.lineWidth = 1;
		ctx.beginPath();
		ctx.moveTo( this.points[0][0], this.points[0][1] );
		for( var c=1; c < this.points.length; c++ ){
			ctx.lineTo( this.points[c][0], this.points[c][1] );
		}
		ctx.closePath();
		ctx.stroke();
	
      	if( this.type > 0 ){
			ctx.globalAlpha = 0.5;
			ctx.fillStyle = this.color;
			ctx.fill();
			ctx.globalAlpha = 1.0;
		}else{
			ctx.strokeStyle = "grey";
		}
      
		this.draw_coords( ctx );
	},
	draw_coords: function( ctx ){
		ctx.font="10px Georgia";
		ctx.textAlign = "center";
		ctx.textBaseline = 'middle';
		ctx.fillStyle = "blue";
		ctx.fillText(this.coords[0]+" , "+this.coords[1], this.MidPoint[0], this.MidPoint[1]);
	}
}


///////// [ Grid ] /////////////////////
function Grid( options ){
	if(typeof options !== "undefined"){
		this.size = {
			width: options.size[0],
			height: options.size[1]
		};
		
		//this.mouse_pos = [];
		
		this.pos = options.pos; //// position within the canvas /// [ x , y ] ////
		this.ctx = options.ctx;
		this.ctx_pos = options.ctx_pos; //// position of canvas element /// [ left, top] ///
		this.hex_size = this.calculate_hex_size( options.hex_def );
		this.hexagons = []; //// [ row, col ] /////  just a temporary array  ////
		this.grid2D = []; ///// includes all hexagons to be drawn ///
	}
	this.generate();
	this.animate();
	this.enable_mouse_events();
}

Grid.prototype = {
	constructor : Grid,
	
	generate : function(){
		var hex_pos_x = 0.0, hex_pos_y = 0.0, row = 0, col = 0, offset = 0.0, h = null, h_id = 0; 
		while( (hex_pos_y + this.hex_size.h) <= this.size.height ){
			col = 0; //// reset col 
			offset = 0.0; //// reset offset 
			
			if( (row % 2) == 1){
				offset = ( ( this.hex_size.w - this.hex_size.s ) /2 ) + this.hex_size.s ;
				col = 1;
			}
			
			hex_pos_x = offset;
			
			while( (hex_pos_x  + this.hex_size.w) <= this.size.width ){
				h =  new Hexagon( { pos : [ hex_pos_x, hex_pos_y ], size: this.hex_size , id: row+""+col,  type: 0 }); 
				h.coords[0] = col; //// set coord X ///
				
				this.grid2D.push( h );
				if( ! this.hexagons[col] ){
					this.hexagons[col] = [];
				}
				this.hexagons[col].push( h );
		
				col += 2;
				hex_pos_x += (this.hex_size.w + this.hex_size.s);
			}
			
			row++;
			
			hex_pos_y += (this.hex_size.h / 2);
		}
		
		////finally go through our list of hexagons by their x co-ordinate to assign the y co-ordinate
		var coordX = 0, coordY = 0, h_l =  this.hexagons.length, hex_arr = [];
		for(  ; coordX <  h_l; coordX++ ){
			hex_arr =  this.hexagons[ coordX ];
			coordY =  Math.floor( (coordX / 2 ) + (coordX % 2) );
			for( var h = 0, size = hex_arr.length; h < size; h++ ){
				hex_arr[h].coords[1] = coordY++;
			}
		}
	},
	
	getHexAt: function( p ){ //// point [ x, y ]
		for ( var h = 0, h_l = this.grid2D.length; h < h_l; h++ ){
			if ( this.grid2D[h].contains( p ) ){
				return this.grid2D[h];
			}
		}
		return null;
	},
	
	animate: function(){
		var self = this;
		window.requestAnimationFrame( function(){
			self.animate();
		});
		self.draw();
	},

	draw : function( ){
		this.ctx.clearRect(0, 0, this.size.width, this.size.height);
		for( var h = 0, h_l = this.grid2D.length; h < h_l; h++ ){
			this.grid2D[h].draw( this.ctx );
		}
	},

	calculate_hex_size : function( hex_def ){
		return {
			w: hex_def.radius * 2,
			m: hex_def.margin,
			h: (Math.sqrt(3) / 2) * ( hex_def.radius * 2),
			r: hex_def.radius,
			s: hex_def.radius
		}
	},
	
	enable_mouse_events: function(){
		var self = this;
		var mouse_pos = [];
		var cur_hex = null;
		window.addEventListener( 'mousemove', function(e){
			mouse_pos  =  [ ( e.clientX - self.ctx_pos[0] ), ( e.clientY - self.ctx_pos[1] )];
			//self.mouse_pos = mouse_pos;
		});
		
		window.addEventListener( 'mousedown', function(e){
			if( mouse_pos.length > 0 ){
				cur_hex = self.getHexAt( mouse_pos );
				if( cur_hex != null ){
					cur_hex.trigger("select");
				}
			}
		});
	}
}

	var c_el = document.getElementById("myCanvas");
	var ctx = c_el.getContext("2d");
	var nGrid = new Grid({
		/// size : [ c_el.width, c_el.height ], /// [rows / cols ] //// 20 px x 10 px///
		size : [ 70 , 70 ], 
		pos: [ 20, 20 ], /// [X, Y] ////
		hex_def: {
			radius: 20,
			margin: 0
		},
		ctx : ctx,
		ctx_pos: [ c_el.getBoundingClientRect().left, c_el.getBoundingClientRect().top ]
	});
	
	<body  stye="width: 100%; height: 100%" >
	<canvas id="myCanvas" width="750px" height="405px" style="margin:0; padding:0; border:1px solid #d3d3d3;"></canvas>
</body>

关于javascript - 无法选择网格中的六边形,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37546705/

相关文章:

javascript - 在 Node js中同步工作线程

javascript - jQuery 函数不适用于克隆

javascript - 为什么 JavaScript 函数不创建 HTML 元素?

php - 创建一个像这样的 HTML 表格

html - 过渡边界底部?

javascript - 仅触发 jQuery 中最内层元素的点击事件

javascript - 无法使用 JQuery 功能切换图像

javascript - 如何将数据插入到对象数组中javascript

jquery - 如何在背景上正确定义 .overlay 类的高度?

javascript - 在 iframe 重新加载时隐藏浏览器加载图标