按 alpha 通道绘制的图像映射


<img src="circle.png" onclick="alert('clicked')"/>

假设 circle.png 是一个 400x400 像素的透明背景图像,中间有一个圆圈。

我现在得到的是整个图像区域(400x400px)是可点击的。我想要的是只有圆圈(非透明像素)是可点击的。

当然,我知道在这个例子中我可以使用<map>标签和一个圆形区域,但我正在寻找一种通用解决方案,该解决方案将考虑实际图像透明度并适用于任何类型的图像(即非规则形状)。

我能看到的最复杂的方法是根据每个像素 alpha 跟踪图像的轮廓,转换为路径(可能简化)并应用为地图。

有没有更有效/更直接的方法?

使用 canvas 标签,您可以确定给定点下像素的颜色值。您可以使用事件数据来确定坐标,然后检查透明度。剩下的就是将图像加载到画布中。

首先,我们将处理这个问题:

  var ctx = document.getElementById('canvas').getContext('2d');
  var img = new Image();
  img.onload = function(){
    ctx.drawImage(img,0,0);
  };
  img.src = [YOUR_URL_HERE];

第一个位获取 canvas 元素,然后创建一个 Image 对象。加载图像时,将在画布上绘制图像。很简单!除了。。。如果映像与代码不在同一域中,则会遇到同一域策略安全性。为了获取映像的数据,我们需要在本地托管映像。您还可以对图像进行base64编码,这超出了本答案的范围。(有关执行此操作的工具,请参阅此 URL)。

接下来,将点击事件附加到画布。当点击进入时,我们将检查透明度,并仅对不透明的点击区域采取行动:

    if (isTransparentUnderMouse(this, e))
        return;
    // do whatever you need to do
    alert('will do something!');

魔术发生在函数isTransparentUnderMouse中,它需要两个参数:目标画布元素(this在点击处理程序的作用域中)和事件数据(在本例中为 e)。现在我们来谈谈肉:

var isTransparentUnderMouse = function (target, evnt) {
    var l = 0, t = 0;
    if (target.offsetParent) {
        var ele = target;
        do {
            l += ele.offsetLeft;
            t += ele.offsetTop;
        } while (ele = ele.offsetParent);
    }
    var x = evnt.page.x - l;
    var y = evnt.page.y - t;
    var imgdata = target.getContext('2d').getImageData(x, y, 1, 1).data;
    if (
        imgdata[0] == 0 &&
        imgdata[1] == 0 &&
        imgdata[2] == 0 &&
        imgdata[3] == 0
    ){
        return true;
    }
    return false;
};

首先,我们做一些跳舞来跳去,以获得所讨论元素的精确位置。我们将使用该信息传递给 canvas 元素。除其他事项外,getImageData将为我们提供一个包含我们指定位置的 RGBA 的data对象。

如果所有这些值都是 0,那么我们关注的是透明度。如果没有,则存在一些颜色。-edit- 如注释中所述,我们真正需要查看的唯一值是上面示例中imgdata[3]的最后一个值。值为 r(0)g(1)b(2)a(3),透明度由 a, alpha 决定。您可以使用相同的方法来查找您知道 rgba 数据的任何不透明度的任何颜色。

在这里尝试一下:http://jsfiddle.net/pJ3MD/1/

注意:在我的示例中,由于我提到的域安全性,我使用了 base64 编码的图像。您可以忽略代码的这一部分,除非您还打算使用 base64 编码)

同样的例子,为了好玩而对鼠标光标进行了更改:http://jsfiddle.net/pJ3MD/2/

文档

  • Image MDN 上的对象 - https://developer.mozilla.org/en/DOM/Image
  • 在 MDN 上使用带有canvas的图像的教程 - https://developer.mozilla.org/en/Canvas_tutorial/Using_images
  • MDN 上的画布门户 - https://developer.mozilla.org/en/HTML/Canvas
  • MDN 上的 HTML canvas 元素 ( getContext ) - https://developer.mozilla.org/en/DOM/HTMLCanvasElement/
  • CanvasRenderingContext2D 在 MDN ( getImageData ) - https://developer.mozilla.org/en/DOM/CanvasRenderingContext2D
  • MDN 上的像素操作 - https://developer.mozilla.org/En/HTML/Canvas/Pixel_manipulation_with_canvas/
您可以使用

HTML5画布执行此操作。将图像绘制到画布中,将单击处理程序附加到画布,然后在处理程序中检查单击的像素是否透明。

谢谢克里斯的这个伟大的答案

我在代码中添加了几行以处理缩放画布

所以我现在要做的是创建一个画布,该画布的像素大小与图像绘制在其上的完全相同。 喜欢(对于图像 220px*120px):

<canvas width="220" height="120" id="mainMenu_item"></canvas>

并使用 CSS 缩放画布:

#mainMenu_item{ width:110px; }

调整后的 isTransparentUnderMouse 函数如下所示:

    var isTransparentUnderMouse = function (target, evnt) {
    var l = 0, t = 0;
    if (target.offsetParent) {
        var ele = target;
        do {
            l += ele.offsetLeft;
            t += ele.offsetTop;
        } while (ele = ele.offsetParent);
    }
    var x = evnt.pageX - l;
    var y = evnt.pageY - t;
    var initialWidth = $(evnt.target).attr('width');
    var clientWidth = evnt.target.clientWidth;
    x = x * (initialWidth/clientWidth);
    var initialHeight = $(evnt.target).attr('height');;
    var clientHeight = evnt.target.clientHeight;
    y = y * (initialHeight/clientHeight);
    var imgdata = target.getContext('2d').getImageData(x, y, 1, 1).data;
    if (
        imgdata[0] == 0 &&
        imgdata[1] == 0 &&
        imgdata[2] == 0 &&
        imgdata[3] == 0
    ){
        return true;
    }
    return false;
};

最新更新