4

我一直在拐弯抹角,所以我会在这里解释我的问题,并希望从整体来看,有人有一些想法。使用以下图像:

带区域的模型

我需要检测她眼睛和嘴巴上的斑点上的鼠标悬停,并以一般形式解决这个问题。模型和斑点位于两个不同的层上,因此我可以生成一张只有斑点的图像,一张只有模型的图像,并以某种方式在斑点上同步虚拟光标,而它实际上悬停在模型上。

我也可以制作 blob 多边形,用于命中测试,但我认为颜色命中测试会容易得多。如果我击中蓝色,我会在她的嘴上显示口红图像;如果我撞到粉红色,我就会在她的眼睛上方,并显示眼妆图像。

这里的学者有什么建议和对话?

4

4 回答 4

2

更简单的方法是将图层图像加载到画布中,然后获取其所有像素数据。当鼠标悬停在模型图像上时,找出当前选择的颜色,如果它与以前的颜色不同,则触发一个事件以指示选择已更改。

这是一个示例,请随意玩弄它;但请注意,它不能处理所有情况:

  • 如果图层和模型图像大小不同怎么办
  • 如果图层和模型图像的宽高比不一样怎么办
  • 如果你想使用一些 alpha 通道怎么办(这个例子没有考虑到它)

$(function() {

  /* we load all the image data first */
  var imageData = null;
  var layerImage = new Image();
  layerImage.onload = function() {
    var canvas = document.createElement("canvas");
    canvas.width = this.width;
    canvas.height = this.height;
    context = canvas.getContext('2d');
    context.drawImage(this, 0, 0, canvas.width, canvas.height);
    imageData = context.getImageData(0, 0, canvas.width, canvas.height).data;
  };

  /* it's easier to set the image data for example as base64 data */
  layerImage.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAOwgAADsIBFShKgAAAABp0RVh0U29mdHdhcmUAUGFpbnQuTkVUIHYzLjUuMTFH80I3AAAA5klEQVR4Xu3WMQ7CMAwF0IxcgCtw/xsGdWApItWHKorLQ8qESe2HbWjNiwABAgQIECBAgAABAgQIECBAgMAUgd4e/ehMSeTMh4wK2j/nqPjt/TNzm3IXgEFb64CdgBG44hKcsmg8pKDAvbf+7SlY7nvK3xb/+lx5BAA/jMCGpwOqC/z9CFT/ApfP/9Z6T87yBaUJJsVvsen9y8cDMAJ2gCWY7IHll1qaYFL84a9AelnF+CFwxYLSnAGMBFLNivE6QAcMBCq2dJqzETACRuCzQDpPFePTv9riCRAgQIAAAQIECBC4nsATagY67TVyuhAAAAAASUVORK5CYII=";

  var pColor = null;

  /* on mouse over the model image */
  $("#model").mousemove(function(event) {
    /* we correct the offset */
    var offset = $(this).offset();
    var relX = event.pageX - offset.left;
    var relY = event.pageY - Math.round(offset.top);

    /* and get the pixel values at this place (note we are not keeping the alpha channel; it's your decision whether or not it is valuable */
    var pixelIndex = relY * layerImage.width + relX;
    var dataIndex = pixelIndex * 4;
    var color = [imageData[dataIndex], imageData[dataIndex + 1], imageData[dataIndex + 2]];

    if (pColor == null) {
      /* we trigger when first entering the image */
      $(this).trigger("newColor", {
        message: "Initial layer color",
        data: color
      });
    } else if (pColor[0] != color[0] || pColor[1] != color[1] || pColor[2] != color[2]) {
      /* we trigger if the new position is a new color in the layer image */
      $(this).trigger("newColor", {
        message: "Changed layer color",
        data: color
      });
    }
    pColor = color;
  });

  /* some small help to convert rgb to css colors */
  function rgb2hex(red, green, blue) {
    var rgb = blue | (green << 8) | (red << 16);
    return '#' + (0x1000000 + rgb).toString(16).slice(1)
  }

  /* there you have the new layer color event management; for the example sake we change the color of some text */
  $("#model").on("newColor", function(event, eventData) {
    $("#selector").css("color", rgb2hex(eventData.data[0], eventData.data[1], eventData.data[2]));
  });
});
img {
  border: 1px solid silver
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>

<body>
  <h4>Model image</h4>

  <img id="model" src="data:image/png;base64, iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAIAAAAlC+aJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjExR/NCNwAAAtdJREFUaEPtmS2PwkAQhhGQEASEBIECFAJBgoCAxpDgsWBxSP4lPwGJRCLv5m4uk6Xt7s5He9dLFst2+z7vfG3bxsc//zX+uf6PBPDXEUwRSBEwOpBSyGig+fIUAbOFxg3KjMD1em3EfqvV6vl8GkW7l5cJsF6vY/p//i8RQwbwer22221U5W63K/T4fr93u1263LdMFB8ZAEc96BsOhwER5WIIAMB+NO92u4lMKlw8m80oFJaMEgCg/ZADdvW0A2GMRiPdtgKAEu13tUJG4c667sQFoBap8yl8FdiPDIqyZgFQ9oerU83mlrV0ExaAJfshdKfTiSMLg8BZKR5k4ewPS4TpNplMOLKqAqD88YngSwxjVAVgyR+O8bSmKoCKumeerVoAkZe6xQnA45vCGH7rfGuIFbVRBUDgvO2bCefzWXEj4I8PDtyXOYzQ0cVi4WMonAmWSR8HIDWbzUZ33grXNKifTqcA3Ov1FNUfB3AdhdOv4h6+S0D6fr/HWLVaLd1jBgsAFNChV5RLAVpKelDfbrd16lk1QCLczLZjoPEW6SiMG4FAdUphXO/tCSkD8GEwi9uVDvZLny6gZg6HQ8YvDYBr23g8DnR9319S6XjH5XIJGw4Gg7fxZwwiFDfsyGfQSQeRvnci1ghE+XXz1d3W7bb5WVF3ABpzvpZVdwDM+8CYqyOAmzPRx6l6ARyPx3w/CNd9jQDgNEHq+RO6LgCgvt/vA8Dlcol2tjLnQPRmhW00n+W4rNPpPB6P6J5cAHofqnhlSffIAIDT8/k8n+jNZhO8l6qPHOYKPxlJ3+Wj1swpqJRzKPc06n6JIOf4GBmz1U778kpWxJmvQ9EjEOQergEX1KegcEnIAHAvPgaIRgDpMwO/jjUA/N1hJT3Hia7iL64c4KtRfP/4mkQrq9r3rVUngEBMUgQYCZtqIGRSSqGUQgwHUgoZTSrrQ3KhjN8oYiN/+PJPqpb83Htu7qcAAAAASUVORK5CYII="
  />

  <p>You are pointing at some <strong><span id="selector">color</span></strong>
  </p>

  <hr/>
  <h4>Layer image (reference only, not displayed in page)</h4>

  <img id="layer" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAOwgAADsIBFShKgAAAABp0RVh0U29mdHdhcmUAUGFpbnQuTkVUIHYzLjUuMTFH80I3AAAA5klEQVR4Xu3WMQ7CMAwF0IxcgCtw/xsGdWApItWHKorLQ8qESe2HbWjNiwABAgQIECBAgAABAgQIECBAgMAUgd4e/ehMSeTMh4wK2j/nqPjt/TNzm3IXgEFb64CdgBG44hKcsmg8pKDAvbf+7SlY7nvK3xb/+lx5BAA/jMCGpwOqC/z9CFT/ApfP/9Z6T87yBaUJJsVvsen9y8cDMAJ2gCWY7IHll1qaYFL84a9AelnF+CFwxYLSnAGMBFLNivE6QAcMBCq2dJqzETACRuCzQDpPFePTv9riCRAgQIAAAQIECBC4nsATagY67TVyuhAAAAAASUVORK5CYII="
  />
</body>

于 2014-10-17T10:30:28.737 回答
2

如果你可以同时拥有两张图片(一张有斑点,一张没有),我认为你可以使用 HTML5 画布做到这一点。

  • 正常绘制图像
  • 在主图像下方绘制 blob 图像,使其不可见
  • 将 blob 复制到 Canvas
  • onMouseOver,在适当的坐标处检索画布的像素数据(R、G、B 和 alpha)
  • 利润

Twist :如果您不需要它用于其他任何事情,您也许可以只使用一个图像及其 alpha 通道来执行此操作 - 除了在 blob 1、2 和 3 中的任何地方,给像素一个完全不透明度 (A=255),它将具有等于 255-(1,2,3...) 的不透明度。你不能有太多不同的斑点,否则透明度会变得很明显。没试过,但应该可以。考虑到“仅 blob”图像可能具有的可压缩性,一对图像(一个不透明,一个也没有透明并且只有 N+1 种颜色,PNG 压缩)应该会产生更好的结果。

带有两个图像的或多或少的伪代码,使用 jQuery(可以不使用):

var image  = document.getElementById('mainImage')
var blobs  = document.getElementById('blobImage');

// Create a canvas
canvas = $('<canvas/>')[0];
canvas.width = image.width;
canvas.height = image.height;
// IMPORTANT: for this to work, this script and blobImage.src must be both
// in the same security domain, or you'll get "this operation is insecure"
canvas.getContext('2d').drawImage(blobs, 0, 0, image.width, image.height);

// Now wait for it.
$('#mainImage').mouseover(function(event) {
    // TO DO: offset clientX, clientY by margin on mainImage
    var ctx = canvas.getContext('2d');
    // Get one pixel
    var pix = ctx.getImageData(event.clientX, event.clientY, 1, 1);
    // Retrieve the red component
    var red = pix.data[0];
    if (red > 128) {
        // ... do something for red
    }
});
于 2014-10-23T10:49:13.110 回答
1

您可以使用 SVG 图形对图像进行分层。我的示例使用了椭圆,但您也可以轻松地使用多边形。您可以使用问题中所述的颜色,或者向 svg 元素添加额外的属性。该示例使用 onclick,但 mouseover 也可以。

示例js:

function svg_clicked(objSVG)
{
    alert(objSVG.style.fill);
    alert(objSVG.getAttribute('data-category'));
}

示例 SVG:

<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
    <ellipse cx="110" cy="80" rx="100" ry="50" style="fill:red;" onclick="svg_clicked(this);" data-category="lipstick" />
</svg>

这是一个小提琴(将鼠标移到图片中的 O 上)

如果您使 svg 元素透明(使用填充:透明),它仍然有效。您可以快速将覆盖更改为颜色或轮廓以进行测试。

于 2014-10-21T20:57:27.667 回答
0

我强烈推荐经过时间考验的方法。

创建 blob 并检测鼠标是否在它们上方的最简单方法是在另一个图像之上使用 svg 图形。<map>SVG 支持鼠标悬停事件并允许矢量形状,这将比使用or提供更高的精度<area>

我发现这个问题也可能对我来自哪里有所帮助:Hover only on non-transparent part of image。阅读第二个答案,因为在您的情况下可能会更喜欢它。

您图像上的 svg 元素将是透明的(或任何您想要的),您可以轻松检测鼠标悬停事件。

该问题的图书馆称为raphael。希望这被证明是有用的。

于 2014-10-24T01:34:47.380 回答