我想在一个 HTML5 画布上绘制一个 10 行 10 列的方格网格,并在方格上显示数字1-100。单击一个方格应调用一个 JavaScript 函数,将该方格的编号作为变量传递给函数。
我想在一个 HTML5 画布上绘制一个 10 行 10 列的方格网格,并在方格上显示数字1-100。单击一个方格应调用一个 JavaScript 函数,将该方格的编号作为变量传递给函数。
首先,我鼓励您阅读这个答案,它涉及到HTML5 Canvas。需要理解的是没有正方形。为了检测“正方形”的点击事件,您需要跟踪每个画布坐标到逻辑上包含它的正方形的映射,处理整个画布上的单击事件,确定要更改哪些正方形,然后用您想要的更改重新绘制画布。
既然您似乎没有反对使用更合适的技术,我鼓励您使用HTML(其中每个“正方形”类似于使用CSS进行绝对定位、调整大小和着色的<div>
),或SVG(如果需要正方形能够旋转或者想引入其他形状,可以使用<rect>
)。
HTML和SVG都是“保留模式”图形系统,绘制形状会“保留”该形状的概念。您可以移动形状,更改其颜色、大小等等,计算机会自动为您重新绘制它。而且,在您的用例中更重要的是,您可以使用HTML和SVG:
function changeColor(evt){
var clickedOn = evt.target;
// for HTML
clickedOn.style.backgroundColor = '#f00';
// for SVG
clickedOn.setAttribute('fill','red');
}
mySquare.addEventListener('click',changeColor,false);
编辑: 我已经在JavaScript和HTML中创建了一个简单的实现:http://jsfiddle.net/6qkdP/2/
以下是核心代码,以防JSFiddle无法访问:
function clickableGrid( rows, cols, callback ){
var i=0;
var grid = document.createElement('table');
grid.className = 'grid';
for (var r=0;r<rows;++r){
var tr = grid.appendChild(document.createElement('tr'));
for (var c=0;c<cols;++c){
var cell = tr.appendChild(document.createElement('td'));
cell.innerHTML = ++i;
cell.addEventListener('click',(function(el,r,c,i){
return function(){ callback(el,r,c,i); }
})(cell,r,c,i),false);
}
}
return grid;
}
col-xs-6
,但只有在小型布局上,比如移动设备时,它才填充整个父容器。 - Zeliax编辑:使用HTML元素而不是在画布上绘制这些图形或使用SVG是另一种选择,很可能更好。
在Phrogz的建议后面,这里有一个SVG实现:
document.createSvg = function(tagName) {
var svgNS = "http://www.w3.org/2000/svg";
return this.createElementNS(svgNS, tagName);
};
var numberPerSide = 20;
var size = 10;
var pixelsPerSide = 400;
var grid = function(numberPerSide, size, pixelsPerSide, colors) {
var svg = document.createSvg("svg");
svg.setAttribute("width", pixelsPerSide);
svg.setAttribute("height", pixelsPerSide);
svg.setAttribute("viewBox", [0, 0, numberPerSide * size, numberPerSide * size].join(" "));
for(var i = 0; i < numberPerSide; i++) {
for(var j = 0; j < numberPerSide; j++) {
var color1 = colors[(i+j) % colors.length];
var color2 = colors[(i+j+1) % colors.length];
var g = document.createSvg("g");
g.setAttribute("transform", ["translate(", i*size, ",", j*size, ")"].join(""));
var number = numberPerSide * i + j;
var box = document.createSvg("rect");
box.setAttribute("width", size);
box.setAttribute("height", size);
box.setAttribute("fill", color1);
box.setAttribute("id", "b" + number);
g.appendChild(box);
var text = document.createSvg("text");
text.appendChild(document.createTextNode(i * numberPerSide + j));
text.setAttribute("fill", color2);
text.setAttribute("font-size", 6);
text.setAttribute("x", 0);
text.setAttribute("y", size/2);
text.setAttribute("id", "t" + number);
g.appendChild(text);
svg.appendChild(g);
}
}
svg.addEventListener(
"click",
function(e){
var id = e.target.id;
if(id)
alert(id.substring(1));
},
false);
return svg;
};
var container = document.getElementById("container");
container.appendChild(grid(5, 10, 200, ["red", "white"]));
container.appendChild(grid(3, 10, 200, ["white", "black", "yellow"]));
container.appendChild(grid(7, 10, 200, ["blue", "magenta", "cyan", "cornflowerblue"]));
container.appendChild(grid(2, 8, 200, ["turquoise", "gold"]));
N
和S
使用更具描述性的变量名称可能有助于新用户理解和修改代码,2)在使用addEventListener
时,请确保始终显式传递第三个参数(在这种情况下为false
),否则Firefox会生气。 - Phrogz如所接受的答案所示,如果您的设计只需要这些内容,那么在HTML/CSS中实现最简单,但以下是使用Canvas作为替代方案的示例,为那些在Canvas中更有意义的用户案例提供对比(并与HTML/CSS进行对比)。
问题的第一步归结为确定用户鼠标在Canvas的哪个位置,这需要知道Canvas元素的偏移量。在这方面,与Canvas无关,这与在元素中查找鼠标位置相同。我使用event.offsetX/Y
来完成此操作。
在Canvas上绘制网格相当于嵌套循环遍历行和列。使用tileSize
变量控制步长。基本数学让您根据宽度和高度以及行和列值计算出鼠标所在的瓷砖(坐标和/或单元格编号)。使用context.fill...
方法编写文本和绘制正方形。我将所有内容保持为0索引以保持理智,但是您可以在显示之前将其归一化作为最后一步(但不要在逻辑中混合1索引)。
最后,向Canvas元素添加事件侦听器以检测触发鼠标操作的事件,这将触发鼠标位置和选定瓷砖的重新计算以及Canvas的重新渲染。我将大多数逻辑附加到mousemove上,因为更容易可视化,但是如果您选择,相同的代码也适用于click事件。
请记住,下面的方法并不特别注意性能;我仅在光标在单元格之间移动时重新渲染,但部分重绘或将覆盖层移动以指示突出显示的元素会更快(如果可用)。我忽略了许多微小的优化。将其视为概念验证。
const drawGrid = (canvas, ctx, tileSize, highlightNum) => {
for (let y = 0; y < canvas.width / tileSize; y++) {
for (let x = 0; x < canvas.height / tileSize; x++) {
const parity = (x + y) % 2;
const tileNum = x + canvas.width / tileSize * y;
const xx = x * tileSize;
const yy = y * tileSize;
if (tileNum === highlightNum) {
ctx.fillStyle = "#f0f";
}
else {
ctx.fillStyle = parity ? "#555" : "#ddd";
}
ctx.fillRect(xx, yy, tileSize, tileSize);
ctx.fillStyle = parity ? "#fff" : "#000";
ctx.fillText(tileNum, xx, yy);
}
}
};
const size = 10;
const canvas = document.createElement("canvas");
canvas.width = canvas.height = 200;
const ctx = canvas.getContext("2d");
ctx.font = "11px courier";
ctx.textBaseline = "top";
const tileSize = canvas.width / size;
const status = document.createElement("pre");
let lastTile = -1;
drawGrid(canvas, ctx, tileSize);
document.body.style.display = "flex";
document.body.style.alignItems = "flex-start";
document.body.appendChild(canvas);
document.body.appendChild(status);
canvas.addEventListener("mousemove", evt => {
event.target.style.cursor = "pointer";
const tileX = ~~(evt.offsetX / tileSize);
const tileY = ~~(evt.offsetY / tileSize);
const tileNum = tileX + canvas.width / tileSize * tileY;
if (tileNum !== lastTile) {
lastTile = tileNum;
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawGrid(canvas, ctx, tileSize, tileNum);
}
status.innerText = ` mouse coords: {${evt.offsetX}, ${evt.offsetX}}
tile coords : {${tileX}, ${tileY}}
tile number : ${tileNum}`;
});
canvas.addEventListener("click", event => {
status.innerText += "\n [clicked]";
});
canvas.addEventListener("mouseout", event => {
drawGrid(canvas, ctx, tileSize);
status.innerText = "";
lastTile = -1;
});