【问题标题】:Hit testing against text shapes针对文本形状的命中测试
【发布时间】:2014-10-20 15:23:19
【问题描述】:

我想知道给定点是在文本形状的内部还是外部。正如您将在下面提供的示例中注意到的那样,hitTest 将在该点位于TextItem 的边界内时立即返回true,而不仅仅是当该点位于字符本身之内时。 (当您将鼠标指针放在# 的中间时,您最能体验到这种行为)

Sample: Hit-testing against TextItem

我还尝试根据路径绘制字符(正如Raphaël 在他们的font samples 中所做的那样)以使用路径本身进行命中测试,但偶然发现了一些非常奇怪的行为,其中(某些)字符未正确绘制. (如果将路径定义复制到Inkscape之类的矢量图像软件中,则可以正确绘制文本形状)

Sample: Drawing text as path

找出给定点是在文本形状内部还是外部最有希望的方法是什么?

【问题讨论】:

  • 我猜可能路径显示错误,因为我认为画布还没有正确支持 SVG 中的 fillRule。不过我可能是错的。你需要使用画布吗?听起来 SVG 可能更适合(或两者都适合)?
  • 我还用Raphaël 中的exact 相同的cufon 字体和按预期呈现的字体做了一个示例。一般来说,我愿意使用其他方法,不幸的是,使用 svg 进行命中测试的结果完全相同:jsfiddle.net/rponudic/6st4Lodc
  • 针对路径的命中测试适用于 SVG:jsfiddle.net/rponudic/fa8v2k6u - 这将是一个可能的解决方案,尽管这是一种解决方法......
  • 基本上我想知道你是否可以用 SVG 覆盖 Canvas 顶部的文本以获取需要精细控制路径的位。
  • 错过了之前的评论。在 Raph/svg 中,不确定为什么要添加一个框并将鼠标移动放在上面。为什么不只在路径上使用鼠标移动?

标签: html5-canvas raphael paperjs


【解决方案1】:

您可以通过发短信通知鼠标下方的像素是否透明来对文本形状(或任何其他数学上不规则的形状)进行命中测试。

您可以使用以下方法获取整个画布的像素数组:

var data=context.getImageData(0,0,canvas.width,canvas.height).data;

然后您可以像这样获取鼠标下方像素的不透明度(alpha)值:

var pixelIsTransparent = data[(mouseY*canvas.width+mouseX)*4+3]==0

如果像素不透明,那么你就在文本形状上。

如果您在画布上有其他非文本绘图,那么这些非文本绘图可能会给您的命中测试带来误报。一种解决方法是使用仅包含您的文本形状的第二个内存画布,然后对第二个画布上的像素进行命中测试。

这是示例代码和演示:

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;

ctx.strokeStyle='gray';
ctx.font='300px verdana';
var wasHit=false;
var isHit=false;

draw();

var data=ctx.getImageData(0,0,canvas.width,canvas.height).data;

$("#canvas").mousemove(function(e){handleMouseMove(e);});


function draw(){
  ctx.fillStyle=(isHit)?'green':'lightgray';
  ctx.fillText("M",25,250);
  ctx.strokeText("M",25,250);
}


function handleMouseMove(e){
  e.preventDefault();
  e.stopPropagation();

  mouseX=parseInt(e.clientX-offsetX);
  mouseY=parseInt(e.clientY-offsetY);

  isHit=(data[(mouseY*cw+mouseX)*4+3]>10)
  if(!isHit==wasHit){
    draw();
    wasHit=isHit;
  }
}
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>
<p>Hit test: Move mouse over letter</p>
<canvas id="canvas" width=300 height=300></canvas>

【讨论】:

    【解决方案2】:

    在花了很长时间调试paper.js 代码后,我终于找到了解决这个问题的方法。

    您应该使用CompoundPath,而不是使用Path

    复合路径包含两个或多个路径,在路径重叠的位置绘制孔。复合路径中的所有路径都采用最后路径的样式,可以通过其 item.children 列表访问。

    我还更新了上面的示例:

    http://jsfiddle.net/64v7s6L9/1/

    【讨论】:

      猜你喜欢
      • 2011-01-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-08-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多