How can I draw a curve where the curve does not go over the text?

64 Views Asked by At

I want to add a text to the center of quadratic curve or line like this:

text on curve

I tried to add a text with ctx.fillText but it overlaps the curve. text on curve but it overlaps

how can I draw a curve where the curve does not go over the text?

My code:

// draw the curve
const startPoints = this._getStartPoints(); // returns start points [x, y]
const endPoints = this._getEndPoints(); // returns end points [x, y]

ctx.moveTo(...startPoints);

ctx.quadraticCurveTo(...this.getControlPoints(), ...endPoints);
ctx.stroke();

// draw the text
const textMeasurement = ctx.measureText('text on curve');
const middlePoints = this._getControlPoints(); // returns the middle point of the curve [x, y]
const textXPoint = middlePoints[0] - textMeasurement.width / 2;
ctx.fillText('text on curve', textXPoint, middlePoints[1]);
1

There are 1 best solutions below

1
On

Clipping

Use ctx.clip to prevent drawing on parts of the canvas.

In this case you want to draw only outside a bounding area. To do this create two clipping rectangle. The first bounding the whole canvas, the second bounding the text bounding area.

Then clip using the "evenodd" winding (fill) rule.

Example code

Example of clipping outside a bounding box using two rectangle as described above.

const ctx = canvas.getContext("2d");
const w = canvas.width;
const h = canvas.height;
const someText = {
    str: "Clip outside...",
    x: w / 2,
    y: h / 2,
    style: {    
        font: "24px arial",
        textAlign: "center",
        textBaseline: "middle",
    }
};

//-------------------------------------------------------------
// Draw text and get text bounding rect
drawText(someText);
const tBWidth = ctx.measureText(someText.str).width;
const textBounds = {
    x: someText.x - tBWidth * 0.6,
    y: someText.y - 15,
    w: tBWidth * 1.2,
    h: 30,
};

//-------------------------------------------------------------
// Clip and draw lines
ctx.save(); // must have to turn off clip

clipOutside(textBounds);

// Draw some lines outside clip bounds 
ctx.lineWidth = 2;
ctx.beginPath();
ctx.moveTo(-10, -10);
ctx.lineTo(w + 10, h + 10);
ctx.moveTo(w, 0);
ctx.quadraticCurveTo(w * 0.5, h * 0.8, 0, h * 0.3);
ctx.stroke();

ctx.restore(); // Removes the clip

// all done...................
//-------------------------------------------------------------

function drawText(textObj) {
    Object.assign(ctx, textObj.style);
    ctx.fillText(textObj.str, textObj.x, textObj.y);
}
function clipOutside(rect) {
    ctx.beginPath();
    ctx.rect(-1, -1, w + 2, h + 2);
    ctx.rect(rect.x, rect.y, rect.w, rect.h);
    ctx.clip("evenodd");
}
canvas {
   border: 1px solid black;
}
<canvas id="canvas" height="160" width="400"></canvas>