【问题标题】:HTML Canvas & Javascript - Hover and Click EventsHTML Canvas & Javascript - 悬停和点击事件
【发布时间】:2017-04-27 16:28:10
【问题描述】:

下面是一个脚本,它定义了两个函数,分别绘制 4 个矩形按钮和 1 个圆形按钮。我正在尝试在按钮中实现特定的悬停和单击功能(如脚本警报中所述),但我对如何执行此操作有点茫然。我尝试在每次单击时调用 makeInteractiveButton() 函数,但这会导致很多奇怪的重叠和滞后。我希望脚本执行以下操作:

如果圆形按钮悬停,我希望它的 fillColour 更改,如果单击它,我希望它再次更改为代码中描述的颜色(#FFC77E 用于悬停,#FFDDB0 用于单击)。这应该在悬停或点击期间发生。

HTML:

<html lang="en">
<body>
    <canvas id="game" width = "750" height = "500"></canvas>
    <script type='text/javascript' src='stack.js'></script>
</body>
</html>

JavaScript:

var c=document.getElementById('game'),
    canvasX=c.offsetLeft,
    canvasY=c.offsetTop,
    ctx=c.getContext('2d')
    elements = [];

c.style.background = 'grey';

function makeInteractiveButton(x, strokeColor, fillColor) {
    ctx.strokeStyle=strokeColor;
    ctx.fillStyle=fillColor;
    ctx.beginPath();
    ctx.lineWidth=6;
    ctx.arc(x, 475, 20, 0, 2*Math.PI);
    ctx.closePath();
    ctx.stroke();
    ctx.fill();
    elements.push({
        arcX: x,
        arcY: 475,
        arcRadius: 20
    });
}

b1 = makeInteractiveButton(235, '#FFFCF8', '#FFB85D');

c.addEventListener('mousemove', function(event) {
    x=event.pageX-canvasX; // cursor location
    y=event.pageY-canvasY;

    elements.forEach(function(element) {
        if (x > element.left && x < element.left + element.width && 
            y > element.top && y < element.top + element.height) { // if cursor in rect
          alert('Rectangle should undergo 5 degree rotation and 105% scale');
        }
        else if (Math.pow(x-element.arcX, 2) + Math.pow(y-element.arcY, 2) < 
                 Math.pow(element.arcRadius, 2)) { // if cursor in circle
            alert('Set b1 fillColour to #FFC77E.');
        }
    });
}, false);

c.addEventListener('click', function(event) {
    x=event.pageX-canvasX; // cursor location
    y=event.pageY-canvasY;

    elements.forEach(function(element) {
        if (x > element.left && x < element.left + element.width && 
            y > element.top && y < element.top + element.height) { // if rect clicked
            alert('Move all cards to centre simultaneously.');
        }
        else if (Math.pow(x-element.arcX, 2) + Math.pow(y-element.arcY, 2) < 
                 Math.pow(element.arcRadius, 2)) { // if circle clicked
            alert('Set b1 fillColour to #FFDDB0.');
        }
    });
}, false);

【问题讨论】:

  • 这篇文章听起来更像是一个编程作业,而不是一个问题。你能解释一下这段代码在做什么吗?到目前为止有多少工作?你现在有什么问题?
  • 如果你在制作游戏(我猜是 id),你可以看看 pixi.js。他们在这里有一个带有 png 和画布的按钮示例:pixijs.github.io/examples/#/demos/interactivity.js
  • 我已经把这个问题变成了一个单一的问题,让它听起来不像是一个编程任务。我正在制作一个游戏,但我希望在没有外部库的情况下做所有事情。

标签: javascript html canvas hover click


【解决方案1】:

一种方法是保留所有元素数据并编写一个 hitTest(x,y) 函数,但是当您有很多复杂的形状时,最好使用辅助画布来渲染带有 ID 而不是颜色的元素,并且第二个画布中 x,y 的颜色是命中元素的 ID,我应该提到第二个画布是不可见的,它只是一个用于获取命中元素的凝胶。

Github 示例:

https://siamandmaroufi.github.io/CanvasElement/

矩形 hitTest 的简单实现:

    var Rectangle = function(id,x,y,width,height,color){
      this.id = id;
      this.x=x;
      this.y=y;
      this.width = width;
      this.height = height;
      this.color = color  || '#7cf';
      this.selected = false;
    }

    Rectangle.prototype.draw = function(ctx){
     ctx.fillStyle = this.color;
     ctx.fillRect(this.x,this.y,this.width,this.height);
     if(this.selected){
       ctx.strokeStyle='red';
       ctx.setLineDash([5,5]);
       ctx.lineWidth = 5;
       ctx.strokeRect(this.x,this.y,this.width,this.height);
     }
    }

    Rectangle.prototype.hitTest=function(x,y){
     return (x >= this.x) && (x <= (this.width+this.x)) &&
            (y >= this.y) && (y <= (this.height+this.y));
    }

    var Paint  = function(el) {
     this.element = el;
     this.shapes = [];
    }

    Paint.prototype.addShape = function(shape){
     this.shapes.push(shape);
    }

    Paint.prototype.render = function(){
     //clear the canvas
     this.element.width = this.element.width;
     var ctx = this.element.getContext('2d');
     for(var i=0;i<this.shapes.length;i++){
      this.shapes[i].draw(ctx);
     }
    }

    Paint.prototype.setSelected = function(shape){
      for(var i=0;i<this.shapes.length;i++){
        this.shapes[i].selected = this.shapes[i]==shape;
      }
      this.render();
    }

    Paint.prototype.select = function(x,y){
     for(var i=this.shapes.length-1;i>=0;i--){
      if(this.shapes[i].hitTest(x,y)){
       return this.shapes[i];
      }
     }
     return null;
    }

    var el = document.getElementById('panel');
    var paint = new Paint(el);
    var rectA = new Rectangle('A',10,10,150,90,'yellow');
    var rectB = new Rectangle('B',150,90,140,100,'green');
    var rectC = new Rectangle('C',70,85,200,70,'rgba(0,0,0,.5)');

    paint.addShape(rectA);
    paint.addShape(rectB);
    paint.addShape(rectC);

    paint.render();


    function panel_mouseUp(evt){
     var p = document.getElementById('panel'); 
     var x = evt.x - p.offsetLeft;
     var y = evt.y - p.offsetTop;
     var shape = paint.select(x,y);
     if(shape){ 
      alert(shape.id);
     }
     //console.log('selected shape :',shape);
    }

    function panel_mouseMove(evt){
      var p = document.getElementById('panel'); 
      var x = evt.x - p.offsetLeft;
      var y = evt.y - p.offsetTop;
      var shape = paint.select(x,y);

      paint.setSelected(shape);
    }


    el.addEventListener('mouseup',panel_mouseUp);
    el.addEventListener('mousemove',panel_mouseMove);
    body {background:#e6e6e6;}
    #panel {
     border:solid thin #ccc;
     background:#fff;
     margin:0 auto;
     display:block;
    }
    <canvas id="panel" width="400px" height="200px"  >
    </canvas>   

只需点击或移动形状

【讨论】:

  • 谢谢。您能否详细说明问题中的代码? hitTest 函数的目的是说'无论何时发生任何事件,都重新绘制画布'​​?
  • 这适用于识别点击,但我正在努力改变悬停时的颜色。见here
  • 只需在 Rectangle 类中添加一个选定的属性,并像我一样在 draw 方法中使用它,我在代码中做了一些更改,再次运行代码 sn-p
  • 谢谢!如果您可以更改代码,以便当您停止将鼠标悬停在对象上时悬停边框消失,我会接受这个答案。
  • 我只是删除了一个条件,它工作正常,在我写的 github 链接上也有一个高级示例。
猜你喜欢
  • 2016-11-10
  • 2014-01-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-08-24
  • 1970-01-01
相关资源
最近更新 更多