10

I am using using HTML5 Canvas to plot lines. A single line is formed by calling drawLine() on multiple intermediate points. For example:

(0,0) -> (10, 10) -> (10, 5) -> (20, 12)

would show up as one line on the plot.

All the (x,y) co-ordinates of a line are stored in an array.

I want to provide the users with the ability to select a line when they click on it. It becomes difficult to do this in HTML5 Canvas as the line is not represented by an object. The only option that I am left with is to first find that (x,y) coordinate of any line that is closest to the (x,y) of a mousedown event. Once I detect which line the user has selected, then I need to redraw the line with a bold color or put a translucent color around it. But, I am assuming that this would be too time-intensive, as it involves looping over all (x,y) coordinates of all lines.

I am looking for ways that can help me achieve the above in a more time-efficient manner. Should I consider using SVG in HTML5?

Any suggestions would be appreciated.

kkonweb
  • 125
  • 1
  • 7
  • refer to http://stackoverflow.com/questions/27332603/select-and-change-color-of-a-line-in-html5-canvas/27336242#27336242 The answer there is more clearer – Riyafa Abdul Hameed Dec 07 '14 at 05:10

3 Answers3

11

The simplest way to do this in HTML5 canvas is to take a snapshot of the image data for the canvas, and during mousemove look at the alpha color at the pixel under the mouse.

I've put up a working example of this on my site here:
http://phrogz.net/tmp/canvas_detect_mouseover.html

Here's the core code I wrote. Pass it a context and a function, and it will call your function with the RGBA components underneath the pixel.

function pixelOnMouseOver(ctx,callback){
  var canvas = ctx.canvas;
  var w = canvas.width, h=canvas.height;
  var data = ctx.getImageData(0,0,w,h).data;
  canvas.addEventListener('mousemove',function(e){
    var idx = (e.offsetY*w + e.offsetX)*4;
    var parts = Array.prototype.slice.call(data,idx,idx+4);
    callback.apply(ctx,parts);
  },false);
}

And here's how it's used on that test page:

var wasOver;
pixelOnMouseOver(ctx,function(r,g,b,a){
  var isOver = a > 10; // arbitrary threshold
  if (isOver != wasOver){
    can.style.backgroundColor = isOver ? '#ff6' : '';
    wasOver = isOver;
  }
  out.innerHTML = "r:"+r+", g:"+g+", b:"+b+", a:"+a;
});
Phrogz
  • 296,393
  • 112
  • 651
  • 745
  • very cool -- but note that if the mouse is moved quickly, it won't always accurately report whether it's on or off canvas. – ashleedawg Jan 01 '20 at 15:08
6

I think you'd find this much easier in SVG. There each line would be a <polyline> and you could add a onclick handler to do what you want. For example...

<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
    <polyline points="20,20 40,25 60,40 80,120 120,140 200,180" 
              style="fill:none;stroke:black;stroke-width:5"
              onclick="this.style.stroke='red'" />
</svg>
Robert Longson
  • 118,664
  • 26
  • 252
  • 242
  • Thank you for your response, Robert. The SVG approach would work if I know the number of lines to be plotted beforehand. If I have the user dynamically create a new line through some UI button, then wouldn't this approach require modifying the actual SVG file each time a line needs to be created? – kkonweb Nov 18 '11 at 16:20
  • 2
    Yes but that's not difficult, just use document.createElementNS("http://www.w3.org/2000/svg", "polyline") to create a new line. – Robert Longson Nov 18 '11 at 20:00
1

The only way to do this on the canvas is to detect pixel color and follow path or save paths as objects and detect a click on that path.

Phrogz
  • 296,393
  • 112
  • 651
  • 745
austinbv
  • 9,297
  • 6
  • 50
  • 82
  • Note that with filled paths you can also use [`ctx.isPointInPath(x,y)`](http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-ispointinpath). – Phrogz Nov 18 '11 at 20:47
  • Right; that's why I first added my own answer showing a working solution for stroked lines. – Phrogz Nov 19 '11 at 15:13