JavaScript/HTML Canvas:循环按钮行的鼠标检测

JavaScript/HTML Canvas: mouse detection for looped row of buttons

我正在构建一个使用 canvas 作为 GUI 的简单鼓机。我有一排按钮用 for 循环绘制,单击时会切换 on/off。

Here's a sample on JSFiddle

虽然它有效,但我对我的 buttonToggleDetection 功能感到有点尴尬。这是我能想到的检查鼠标悬停在哪个按钮上的唯一解决方案。我想知道是否有人可以建议更好的方法来做到这一点?

var buttonToggleDetection = function(posx, posy, x) {

    if (posx < canvas.width/2 && posy > x && posy < x*2) {

        if (posx > x*1 && posx < x*2) {
            if (pattern[0] === 0) {
                pattern[0] = 1;
            } else {
                pattern[0] = 0;
            }
        }
        else if (posx > x*2 && posx < x*3) {
            if (pattern[1] === 0) {
                pattern[1] = 1;
            } else {
                pattern[1] = 0;
            }
        }
        else if (posx > x*3 && posx < x*4) {
            if (pattern[2] === 0) {
                pattern[2] = 1;
            } else {
                pattern[2] = 0;
            }
        } 
        else if (posx > x*4 && posx < x*5) {
            if (pattern[3] === 0) {
                pattern[3] = 1;
            } else {
                pattern[3] = 0;
            }
        }
        else if (posx > x*5 && posx < x*6) {
            if (pattern[4] === 0) {
                pattern[4] = 1;
            } else {
                pattern[4] = 0;
            }
        }
        else if (posx > x*6 && posx < x*7) {
            if (pattern[5] === 0) {
                pattern[5] = 1;
            } else {
                pattern[5] = 0;
            }
        }
        else if (posx > x*7 && posx < x*8) {
            if (pattern[6] === 0) {
                pattern[6] = 1;
            } else {
                pattern[6] = 0;
            }
        }
        else if (posx > x*8 && posx < x*9) {
            if (pattern[7] === 0) {
                pattern[7] = 1;
            } else {
                pattern[7] = 0;
            }
        }
    }
    if (posx > canvas.width/2 && posy > x && posy < x*2) {

        if (posx > x*9 && posx < x*10) {
            if (pattern[8] === 0) {
                pattern[8] = 1;
            } else {
                pattern[8] = 0;
            }
        }
        else if (posx > x*10 && posx < x*11) {
            if (pattern[9] === 0) {
                pattern[9] = 1;
            } else {
                pattern[9] = 0;
            }
        } 
        else if (posx > x*11 && posx < x*12) {
            if (pattern[10] === 0) {
                pattern[10] = 1;
            } else {
                pattern[10] = 0;
            }
        }
        else if (posx > x*12 && posx < x*13) {
            if (pattern[11] === 0) {
                pattern[11] = 1;
            } else {
                pattern[11] = 0;
            }
        }
        else if (posx > x*13 && posx < x*14) {
            if (pattern[12] === 0) {
                pattern[12] = 1;
            } else {
                pattern[12] = 0;
            }
        }
        else if (posx > x*14 && posx < x*15) {
            if (pattern[13] === 0) {
                pattern[13] = 1;
            } else {
                pattern[13] = 0;
            }
        }
        else if (posx > x*15 && posx < x*16) {
            if (pattern[14] === 0) {
                pattern[14] = 1;
            } else {
                pattern[14] = 0;
            }
        }
        else if (posx > x*16 && posx < x*17) {
            if (pattern[15] === 0) {
                pattern[15] = 1;
            } else {
                pattern[15] = 0;
            }
        }
    }
    return;
}

我建议使用 svg 而不是 canvas。 SVG 节点的内容与 canvas 节点的内容非常相似,但每个形状都是由 XML 标记或动态创建的元素构建的,很像 document.body 的内容。这给你带来了几个很大的优势:

  1. 您可以将 类 应用于单个形状,并使用 css 来应用 colors/styles。
  2. 您可以利用 css 的 :hover 伪风格 将颜色应用于悬停元素。
  3. 最重要的是:您可以为每个形状绑定事件!

以下代码可供 fiddle 使用 http://jsfiddle.net/3zevLyur/1/

这是html:

<svg id="drumMachine"/>
<div id="debugText"/>

这里是 javascript 构建打击垫和绑定事件:

var svg = document.getElementById("drumMachine");
var activePad = null;
var debugText = document.getElementById("debugText");

for (var x = 0; x < 16; x++) {
    createNewPad(x);
}


function createNewPad(padNumber) {
    var r = document.createElementNS("http://www.w3.org/2000/svg", "rect")
    r.setAttribute("width", "40");
    r.setAttribute("height", "40");
    r.setAttribute("x", padNumber * 50);
    r.setAttribute("data-pad-number", x);
    r.onmouseenter = mouseOver;
    r.onmouseleave = mouseOut;
    svg.appendChild(r);
}

function mouseOver() {
    activePad = this;
    debugText.innerHTML = this.getAttribute("data-pad-number");
}

function mouseOut() {
    activePad = null;
    debugText.innerHTML = "";
}

您检查按钮边界的方式似乎不错...但是您的循环可以通过在数组中定义按钮然后遍历该数组来收紧一点。

这是在 html canvas 中绘制和切换按钮的一种方法:

  • 定义每个按钮的 x、y、宽度、高度和 pressed-state

    var buttons=[];
    buttons.push({x:20,y:20,width:50,height:35,text:'One',isPressed:false});
    buttons.push({x:80,y:20,width:50,height:35,text:'Two',isPressed:true});
    buttons.push({x:140,y:20,width:50,height:35,text:'Three',isPressed:false});
    
  • 测试鼠标是否在任何按钮上:

    for(var i=0;i<buttons.length;i++){
        var b=buttons[i];
        if(mx>b.x && mx<b.x+b.width && my>b.y && my<=b.y+b.height){
            b.isPressed=(!b.isPressed);
        }
    }
    

下面是示例代码和演示:

var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
var $canvas=$("#canvas");
var canvasOffset=$canvas.offset();
var offsetX=canvasOffset.left;
var offsetY=canvasOffset.top;
var scrollX=$canvas.scrollLeft();
var scrollY=$canvas.scrollTop();

ctx.textAlign='center';
ctx.textBaseline='middle';
ctx.font='14px verdana';

var buttons=[];
buttons.push({x:20,y:20,width:50,height:35,text:'One',isPressed:false});
buttons.push({x:80,y:20,width:50,height:35,text:'Two',isPressed:true});
buttons.push({x:140,y:20,width:50,height:35,text:'Three',isPressed:false});

draw();

function draw(){
  var label;
  ctx.clearRect(0,0,cw,ch);
  for(var i=0;i<buttons.length;i++){
    var b=buttons[i];
    if(b.isPressed){
      ctx.shadowBlur=0;
      ctx.shadowOffsetX=0;
      ctx.shadowOffsetY=0;
      ctx.shadowColor=null;
      ctx.fillStyle='powderblue';
      label='ON';
    }else{
      ctx.shadowBlur=2;
      ctx.shadowOffsetX=2;
      ctx.shadowOffsetY=2;
      ctx.shadowColor='black';
      ctx.fillStyle='paleturquoise';
      label='OFF';
    }
    ctx.strokeRect(b.x,b.y,b.width,b.height);
    ctx.fillRect(b.x,b.y,b.width,b.height);
    ctx.shadowBlur=0;
    ctx.shadowOffsetX=0;
    ctx.shadowOffsetY=0;
    ctx.shadowColor=null;
    ctx.fillStyle='black';
    ctx.fillText(label,b.x+b.width/2,b.y+b.height/2);
    ctx.fillStyle='gray';
    ctx.fillText(label,b.x+b.width/2+1,b.y+b.height/2+1);
  }
}


function handleMouseDown(e){
  // tell the browser we're handling this event
  e.preventDefault();
  e.stopPropagation();

  mx=parseInt(e.clientX-offsetX);
  my=parseInt(e.clientY-offsetY);

  // 
  for(var i=0;i<buttons.length;i++){
    var b=buttons[i];
    if(mx>b.x && mx<b.x+b.width && my>b.y && my<=b.y+b.height){
      b.isPressed=(!b.isPressed);
    }
  }
  draw();

}


$("#canvas").mousedown(function(e){handleMouseDown(e);});
body{ background-color: ivory; }
#canvas{border:1px solid red;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<h4>Click the buttons</h4>
<canvas id="canvas" width=300 height=300></canvas>