检测鼠标悬停在重叠的+透明图像上 [英] detect mouse hover on overlapping + transparent images

查看:136
本文介绍了检测鼠标悬停在重叠的+透明图像上的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在构建一个小游戏,用户可以在其中购买物品来装饰自己的房屋.

I'm building a little game where the user has buy items to furnish his house.

  • 我有很多物品/图片;因此,我决定为每个图像使用一个遮罩"(图像)来定义可悬停区域,而不是为每个图像绘制一个地图区域.
    示例:这是显示的沙发其遮罩.
    我将遮罩转换"为canvas元素,稍后将检查悬停的像素是否透明以检测项目是否悬停.

  • I have a lot of items/images; so I decided to use, for each of them, a "matte" (an image) that would define the hoverable zone, rather than drawing a map area for each image.
    Example : here's the displayed couch, and its matte.
    I "convert" the matte into a canvas element, and will check later if the hovered pixel is transparent to detect if the item is hovered.

第二件事是很多项目重叠,因此我还需要检查哪一层在最上面.

The second thing is that a lot of items are overlapping, so I also need to check which layer is on top.

我在house元素上有 mousemove 事件(jQuery);与功能 getObjectsUnderMouse()绑定.

I have mousemove event (jQuery) on the house element; bound with the function getObjectsUnderMouse().

基本上,这就是getObjectsUnderMouse()的工作方式:

Basically, this is how getObjectsUnderMouse() works :

  1. 获取鼠标坐标
  2. 在房屋中获取活动(显示)的物品
  3. 过滤这些项目以仅保留鼠标击中画布边界的那些项目,知道项目的位置和宽度/高度)
  4. 过滤这些项目以仅保留鼠标不在透明像素上的那些项目(画布)
  5. 过滤这些项目,使其仅顶部(z索引)
  6. 给该物品一个 mouseon
  1. Get the mouse coordinates
  2. Get the active (displayed) items in the house
  3. Filter those items to keep only the ones where the mouse hits the canvas boundaries, knowing the item position and width/height)
  4. Filter those items to keep only the ones where the mouse is NOT on a transparent pixel (canvas)
  5. Filter those items to keep only the one on the top (z-index)
  6. Give that item a mouseon class

我对自己的代码感到非常满意,这是一个很大的挑战,但可以在Chrome上完美运行.

I was quite happy with my code, which was quite a challenge but works perfectly on Chrome.

我的问题是其他地方的速度较慢(没什么大不了的),但是;最重要的是,似乎在ipad上崩溃了;我需要我的游戏才能在ipad上运行...:/

The problem I have is that it is slower elsewhere (not a so big deal), but; above all, seems to crash on ipad; and I need my game to run on ipad... :/

有人知道为什么吗或对此有更好的解决方案吗?

Does anyone knows why or have a better solution for this ?

这里是游戏演示,这里是

Here's a demo of the game, and here's the javascript file where you can have a look at getObjectsUnderMouse().

欢迎任何建议!

推荐答案

尽管一个遮罩画布包含您需要进行命中测试的信息,但是就内存而言,为每个遮罩保留一个完整尺寸的画布是很昂贵的.为每个遮罩保留一个画布可能使用的资源超出了iPad的处理能力.

Although a matte canvas contains the information you need to hit-test, keeping a full sized canvas for each matte is expensive in terms of memory. Keeping a canvas for each matte is likely using more resources than your iPad can handle.

这是一种大大减少内存使用量的方法:

Here's a way to greatly reduce your memory usage:

首先,从每个对象中裁剪出任何多余的透明空间.例如,您的沙发是600x400 = 240000像素,但是裁剪掉空白区域会将图像缩小到612x163 = 99756像素.与原始图像尺寸相比,节省了58%.更少的像素意味着更少的遮罩内存.

First, crop any extra transparent space out of each of your objects. For example, your couch is 600x400=240000 pixels, but cropping away the empty space shrinks the image to 612x163=99756 pixels. That's a savings of 58% over the original image size. Less pixels means less memory for a matte.

与其为每个对象保留完整尺寸的画布,不如为每个对象保留一个数组,该数组仅包含该图像中每个像素的不透明度.数组值为1表示像素是不透明的(并且是对象的一部分).数组值为0表示像素是透明的(对象的任何部分都不在此像素处).

Instead of keeping a full-sized canvas for each object, instead keep an array for each object which only contains the opacity of each pixel in that image. An array value of 1 indicates that pixel is opaque (and is part of the object). An array value of 0 indicates that pixel is transparent (no part of the object is at this pixel).

然后对像素阵列进行命中测试,而不是对磨砂画布进行命中测试.

Then hit-test against the pixel array instead of hit-testing against a matte canvas.

如果以z-index顺序测试数组,甚至可以知道哪个对象在另一个对象之上.

If you test the arrays in z-index order, you can even tell which object is on top of another object.

这是示例代码和演示:

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;

// display which object the mouse is over
var $result=$('#result');

// create an array of target objects
var targets=[];
targets.push({ name:'couch', x:25, y:50, hitArray:[], url:'https://dl.dropboxusercontent.com/u/139992952/multple/couch.png' });
targets.push({ name:'lamp', x:50, y:30, hitArray:[], url:'https://dl.dropboxusercontent.com/u/139992952/multple/lamp.png' });
var imgCount=targets.length;

// load the image associated with each target object
for(var i=0;i<targets.length;i++){
  var t=targets[i];
  t.image=new Image();
  t.image.crossOrigin='anonymous';
  t.image.index=i;
  t.image.onload=start;
  t.image.src=t.url;   
}

// this is called when each image is fully loaded
function start(){

  // return if all target images are not loaded
  if(--imgCount>0){return;}

  // make hit arrays for all targets
  for(var i=0;i<targets.length;i++){
    var t=targets[i];
    t.hitArray=makeHitArray(t.image);
  }

  // resize the canvas back to its original size
  canvas.width=cw;
  canvas.height=ch;   

  // draw all targets on the canvas
  for(var i=0;i<targets.length;i++){
    var t=targets[i];
    t.width=t.image.width;
    t.height=t.image.height;
    ctx.drawImage(t.image,t.x,t.y);
  }

  // listen for events
  $("#canvas").mousemove(function(e){handleMouseMove(e);});

}

// Draw a target image on a canvas
// Get the imageData of that canvas
// Make an array containing the opacity of each pixel on the canvas
// ( 0==pixel is not part of the object, 1==pixel is part of the object)
function makeHitArray(img){
  var a=[];
  canvas.width=img.width;
  canvas.height=img.height;
  ctx.drawImage(img,0,0);
  var data=ctx.getImageData(0,0,canvas.width,canvas.height).data;
  for(var i=0;i<data.length;i+=4){
    // if this pixel is mostly opaque push 1 else push 0
    a.push(data[i+3]>250?1:0);
  }
  return(a);
}


function handleMouseMove(e){

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

  // get the mouse position
  mouseX=parseInt(e.clientX-offsetX);
  mouseY=parseInt(e.clientY-offsetY);

  // Test the mouse position against each object's pixel array
  // Report hitting the topmost object if 2+ objects overlap
  var hit='Not hovering';
  for(var i=0;i<targets.length;i++){
    var t=targets[i];
    var imgX=mouseX-t.x;
    var imgY=mouseY-t.y;
    if(imgX<=t.width && imgY<=t.height){
      var hitArrayIndex=imgY*t.width+imgX;
      if(hitArrayIndex<t.hitArray.length-1){
        if(t.hitArray[hitArrayIndex]>0){
          hit='Hovering over '+t.name;
        }
      }     
    }
  }

  $result.text(hit);

}

body{ background-color: ivory; padding:10px; }
#canvas{border:1px solid red;}

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<h4 id='result'>Move mouse over objects.</h4>
<canvas id="canvas" width=450 height=250></canvas>

这篇关于检测鼠标悬停在重叠的+透明图像上的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆