Hit testing against text shapes

I want to know if a given point is inside or outside the text form. As you will notice in the example below, hitTest

will return true

as soon as the point is inside the boundaries TextItem

, and not only if the point is inside the symbol itself. (You can experience this behavior best when you place your mouse pointer in the middle #

)

Example: Hit Testing Against TextItem

I also tried drawing a character based on paths (as Raphaël

it does in my font selections ) to use the paths themselves for hitting -testing, but came across some rather strange behavior where (some) characters are drawn incorrectly. (If you copy the path definition into vector image software, for example Inkscape

, text shapes are drawn correctly)

Example: drawing text as a path

What is the most promising way to know if a given point is inside or outside the shape of the text?

+3


source to share


2 answers


After some time debugging the code, paper.js

I finally found a solution to this problem.

Instead of using, Path

you should use CompoundPath

:

A component path contains two or more paths where paths intersect. All paths in a compound path take the style of the backmost path and can be accessed through its item.children list.



I've also updated the example from above:

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

0


source


You can hit a text shape (or any other mathematically irregular shape) by writing whether the pixel is transparent under the mouse or not.

You can get the pixel array for the entire canvas using:

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

      

Then you can get the opacity (alpha) value for the pixel under the mouse like this:

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

      



If the pixel is not transparent, you are above the text shape.

If you have other non-textual drawings on the canvas, then those non-textual drawings may give false positives for your hit tests. A workaround for this is to use a second in-memory canvas containing only your text form, and then test against pixels on that second canvas.

Here's some sample code and demo:

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>
      

Run code


0


source







All Articles