簡體   English   中英

在 Web 瀏覽器中創建可點擊的網格

[英]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 實現:

jsfiddle 示例

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM