Text With Arc Direction In Canvas, But Letters Should Keep Vertical

- 1 answer

This is what I'm trying to make:

enter image description hereenter image description here

Closer I got was bridge text like this example, but it uses a Top baseline for the bridge and I'm not sure how to addapt it to my needs:

Bridge Text Effect in HTML5 Canvas


/// (c) Ken Fyrstenberg Nilsen, Abidas Software .com
/// License: CC-Attribute

var ctx = demo.getContext('2d'),
    font = '64px impact',
    w = demo.width,
    h = demo.height,
    isTri = false,
    angleSteps = 180 / w,
    i = w,
    os = document.createElement('canvas'),
    octx = os.getContext('2d');

os.width = w;
os.height = h;

octx.font = font;
octx.textBaseline = 'top';
octx.textAlign = 'center';

function renderBridgeText() {

    curve = parseInt(iCurve.value, 10);
    offsetY = parseInt(iOffset.value, 10);
    textHeight = parseInt(iHeight.value, 10);
    bottom = parseInt(iBottom.value, 10);
    isTri = iTriangle.checked;

    vCurve.innerHTML = curve;
    vOffset.innerHTML = offsetY;
    vHeight.innerHTML = textHeight;
    vBottom.innerHTML = bottom;

    octx.clearRect(0, 0, w, h);
    ctx.clearRect(0, 0, w, h);

    octx.fillText(iText.value.toUpperCase(), w * 0.5, 0);

    /// slide and dice
    i = w;
    dltY = curve / textHeight;
    y = 0;
    while (i--) {
        if (isTri) {
            y += dltY;
            if (i === (w * 0.5)|0) dltY = -dltY;
        } else {
            y = bottom - curve * Math.sin(i * angleSteps * Math.PI / 180);
        ctx.drawImage(os, i, 0, 1, textHeight,
        i, h * 0.5 - offsetY / textHeight * y, 1, y);

iCurve.onchange = iOffset.onchange = iHeight.onchange = iBottom.onchange = iText.onkeyup = iTriangle.onchange = renderBridgeText;

span {
    font:12px sans-serif;
<canvas id=demo width=400 height=300></canvas>
<input id="iCurve" type="range" min=0 max=200 value=110>
<span id="vCurve">110</span>
<input id="iOffset" type="range" min=0 max=100 value=4>
<span id="vOffset">0</span>
    <br><span>Text height:</span>
<input id="iHeight" type="range" min=0 max=200 value=64>
<span id="vHeight">64</span>
<input id="iBottom" type="range" min=0 max=200 value=200>
<span id="vBottom">200</span>
    <input id="iTriangle" type="checkbox">
<input id="iText" type="text" value="BRIDGE TEXT">

Is there any way to arc text while keeping each letter vertical like in the images?



The tool works by drawing the text normally, slicing it into 1px wide vertical strips, then redrawing those strips with vertical scaling and offset. Knowing how .drawImage() functions helps a lot in making this possible. Changing this algorithm to your needs is a matter of changing how the scaling and offset are calculated.

Two parameters are calculated for each strip: Offset and height. y controls the arc but is used directly to set the target height. h * 0.5 - offsetY / textHeight * y scales and offsets y to produce the offset.

Since y is derived almost directly from sin(), we can use that as the offset instead. The height would remain fixed so we use textHeight for that.


ctx.drawImage(os, i, 0, 1, textHeight,
                  i, h * 0.5 - offsetY / textHeight * y, 1, y);


ctx.drawImage(os, i, 0, 1, textHeight,
                  i, y, 1, textHeight);

does the trick.

This is the minimum change necessary. I'll leave it as an exercise for yourself as to how to further clean up the code.