[英]Creating a Clickable Grid in a Web Browser
我想在 HTML5 畫布上繪制一個 10 x 10 方塊的網格,方塊上顯示數字 1-100。 單擊一個方塊應該會調用一個 JavaScript 函數,並將方塊的編號作為變量傳遞給函數。
首先,我鼓勵您閱讀有關 HTML5 Canvas 的另一個問題的答案。 你需要明白沒有正方形。 為了檢測對“正方形”的點擊,您必須跟蹤從每個畫布坐標到它邏輯包含的正方形的映射,處理整個畫布上的單擊事件,找出哪個正方形(s) 你想改變,然后用你想要的改變重畫畫布。
然后——因為你似乎不反對使用更合適的技術——我鼓勵你在任何一種 HTML 中這樣做(其中每個“方塊”類似於一個<div>
,它使用 CSS 絕對定位、大小和顏色) , 或 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;
}
編輯:使用 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"]));
正如公認的答案所示,如果這是您的所有設計,那么在 HTML/CSS 中執行此操作是最簡單的,但這里有一個示例,使用畫布作為替代方案,適用於那些用例在畫布中可能更有意義的人(並與 HTML/ CSS)。
問題的第一步歸結為找出用戶鼠標在畫布中的位置,這需要知道畫布元素的偏移量。 這與在 element 中查找鼠標位置相同,因此在這方面畫布沒有任何獨特之處。 我正在使用event.offsetX/Y
來做到這一點。
在畫布上繪制網格相當於行和列的嵌套循環。 使用tileSize
變量來控制步長。 基本數學可讓您根據寬度和高度以及行和列值確定鼠標所在的圖塊(坐標和/或單元格編號)。 使用context.fill...
方法編寫文本和繪制方塊。 我將所有內容都保留為 0 索引以保持理智,但是您可以將其標准化為顯示前的最后一步(不過,請不要在邏輯中混合使用 1 索引)。
最后,將事件偵聽器添加到畫布元素以檢測鼠標動作,這將觸發鼠標位置和選定圖塊的重新計算以及畫布的重新渲染。 我將大部分邏輯附加到 mousemove 中,因為它更易於可視化,但如果您選擇,相同的代碼也適用於單擊事件。
請記住,以下方法並不是特別注重性能; 我只在光標在單元格之間移動時重新渲染,但部分重新繪制或移動覆蓋以指示突出顯示的元素會更快(如果可用)。 我忽略了很多微優化。 將此視為概念驗證。
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; });
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.