Reputation: 3527
I'm trying to draw speech buble with dragable handler. That's what I have:
Here is the picture for better understanding:
I know how to draw it in canvas when all coordinates are known. It's pretty simple. Tutorial
function drawBubble(ctx, x, y, w, h, radius)
{
var r = x + w;
var b = y + h;
ctx.beginPath();
ctx.strokeStyle="black";
ctx.lineWidth="2";
ctx.moveTo(x+radius, y);
ctx.lineTo(x+radius/2, y-10);
ctx.lineTo(x+radius * 2, y);
ctx.lineTo(r-radius, y);
ctx.quadraticCurveTo(r, y, r, y+radius);
ctx.lineTo(r, y+h-radius);
ctx.quadraticCurveTo(r, b, r-radius, b);
ctx.lineTo(x+radius, b);
ctx.quadraticCurveTo(x, b, x, b-radius);
ctx.lineTo(x, y+radius);
ctx.quadraticCurveTo(x, y, x+radius, y);
ctx.stroke();
}
But the trouble is - how to find coordinates of red dots shown on the picture. Both (x,y) and (x1,y1) are known but change when user drags buble or handler. And in all cases handler should look pretty.
Whould be great if anyone could share the code, it's kinda complicated for me. Thanks in advance!
Upvotes: 2
Views: 3456
Reputation: 4048
You can preserve the corners and draw the pointing bit fixed to a given point. You just need to calculate the correct connection points.
// This is an example with the connection points 20px apart.
// The px and py variables here come from the onmousemove event.
// Finally, this part here is only for the top part of the bubble,
// you have watch for 4 different scenarios, depending on where
// the mouse is and thus what the pointing bit should aim for.
...
var con1 = Math.min(Math.max(x+radius,px-10),r-radius-20);
var con2 = Math.min(Math.max(x+radius+20,px+10),r-radius);
...
if(py < y) dir = 2;
...
ctx.moveTo(x+radius,y);
if(dir==2){
ctx.lineTo(con1,y);
ctx.lineTo(px,py);
ctx.lineTo(con2,y);
ctx.lineTo(r-radius,y);
}
else ctx.lineTo(r-radius,y);
ctx.quadraticCurveTo(r,y,r,y+radius);
...
Like this:
Try clicking on the bubble to drag the pointer.
Upvotes: 2
Reputation:
The handle is already calculated for you so it's simply a matter of preserving its coordinates by doing for example this modification:
function drawBubble(ctx, x, y, w, h, radius) {
...snipped...
var handle = {
x1: x + radius,
y1: y,
x2: x + radius / 2,
y2: y - 10,
x3: x + radius * 2,
y3: y
}
ctx.moveTo(handle.x1, handle.y1);
ctx.lineTo(handle.x2, handle.y2);
ctx.lineTo(handle.x3, handle.y3);
...snipped...
return handle;
}
This is one way to get the coordinates for the handle.
To take it one step further we can modify the above function to also take a handle
parameter.
This way you can choose to feed a handle setting or use a default calculated one:
function drawBubble(ctx, x, y, w, h, radius, handle) {
...snipped...
/// use given handle settings or calculate a default one:
handle = handle || {
x1: x + radius,
y1: y,
x2: x + radius / 2,
y2: y - 10,
x3: x + radius * 2,
y3: y
}
ctx.moveTo(handle.x1, handle.y1);
ctx.lineTo(handle.x2, handle.y2);
ctx.lineTo(handle.x3, handle.y3);
...snipped...
return handle;
}
In order to use this you first obtain a default calculate handle by passing in for example null or false to the function.
Then use those coordinates to draw the positions around. For each move clear and redraw the canvas but this time feed the modified handle parameters to the function:
/// first time:
var handle = null, /// first time use default handle
...;
handle = drawBubble(ctx, x, y, w, h, radius, handle);
Then in your mouse operations:
/// modify and update bubble:
handle = drawBubble(ctx, x, y, w, h, radius, handle);
Hope this helps!
Upvotes: 1