Как нарисовать текст вдоль пути дуги с помощью HTML 5 Canvas? - PullRequest
2 голосов
/ 23 января 2012

Я хочу нарисовать холст, как эта флэш-анимация: http://www.cci.com.tr/tr/bizi-taniyin/tarihcemiz/

Я нарисовал шесть дуг и хочу написать шесть слов в этих дугах. Есть идеи?

Ответы [ 4 ]

19 голосов
/ 10 июля 2015

У меня есть jsFiddle, чтобы применить текст к любому произвольному определению кривой Безье. Наслаждайтесь http://jsfiddle.net/Makallus/hyyvpp8g/

<table>
    <TR>
        <TH>Bezier Curve</TH>
        <TD>
            <input size="80" type="text" id="curve" name="curve" value="99.2,177.2,130.02,60.0,300.5,276.2,300.7,176.2">
        </TD>
    </TR>
    <TR>
        <TH>Text</TH>
        <TD>
            <input size="80" type="text" id="text" name="text" value="testing 1234567890">
        </TD>
    </TR>
    <TR>
        <TD colspan=2>
            <div id="canvasDiv"></div>
        </TD>
    </TR>
</table>

var first = true;
startIt();


function startIt() {
    canvasDiv = document.getElementById('canvasDiv');
    canvasDiv.innerHTML = '<canvas id="layer0" width="300" height="300"></canvas>'; //for IE
    canvas = document.getElementById('layer0');
    ctx = canvas.getContext('2d');
    ctx.fillStyle = "black";
    ctx.font = "18px arial black";
    curve = document.getElementById('curve');
    curveText = document.getElementById('text');
    $(curve).keyup(function (e) {
        changeCurve();
    });
    $(curveText).keyup(function (e) {
        changeCurve();
    });




    if (first) {
        changeCurve();
        first = false;
    }

}

function changeCurve() {
    points = curve.value.split(',');
    if (points.length == 8) drawStack();

}

function drawStack() {
    Ribbon = {
        maxChar: 50,
        startX: points[0],
        startY: points[1],
        control1X: points[2],
        control1Y: points[3],
        control2X: points[4],
        control2Y: points[5],
        endX: points[6],
        endY: points[7]
    };

    ctx.clearRect(0, 0, canvas.width, canvas.height);
    ctx.save();
    ctx.beginPath();

    ctx.moveTo(Ribbon.startX, Ribbon.startY);
    ctx.bezierCurveTo(Ribbon.control1X, Ribbon.control1Y,
    Ribbon.control2X, Ribbon.control2Y,
    Ribbon.endX, Ribbon.endY);

    ctx.stroke();
    ctx.restore();

    FillRibbon(curveText.value, Ribbon);
}

function FillRibbon(text, Ribbon) {

    var textCurve = [];
    var ribbon = text.substring(0, Ribbon.maxChar);
    var curveSample = 1000;


    xDist = 0;
    var i = 0;
    for (i = 0; i < curveSample; i++) {
        a = new bezier2(i / curveSample, Ribbon.startX, Ribbon.startY, Ribbon.control1X, Ribbon.control1Y, Ribbon.control2X, Ribbon.control2Y, Ribbon.endX, Ribbon.endY);
        b = new bezier2((i + 1) / curveSample, Ribbon.startX, Ribbon.startY, Ribbon.control1X, Ribbon.control1Y, Ribbon.control2X, Ribbon.control2Y, Ribbon.endX, Ribbon.endY);
        c = new bezier(a, b);
        textCurve.push({
            bezier: a,
            curve: c.curve
        });
    }

    letterPadding = ctx.measureText(" ").width / 4;
    w = ribbon.length;
    ww = Math.round(ctx.measureText(ribbon).width);


    totalPadding = (w - 1) * letterPadding;
    totalLength = ww + totalPadding;
    p = 0;

    cDist = textCurve[curveSample - 1].curve.cDist;

    z = (cDist / 2) - (totalLength / 2);

    for (i = 0; i < curveSample; i++) {
        if (textCurve[i].curve.cDist >= z) {
            p = i;
            break;
        }
    }

    for (i = 0; i < w; i++) {
        ctx.save();
        ctx.translate(textCurve[p].bezier.point.x, textCurve[p].bezier.point.y);
        ctx.rotate(textCurve[p].curve.rad);
        ctx.fillText(ribbon[i], 0, 0);
        ctx.restore();

        x1 = ctx.measureText(ribbon[i]).width + letterPadding;
        x2 = 0;
        for (j = p; j < curveSample; j++) {
            x2 = x2 + textCurve[j].curve.dist;
            if (x2 >= x1) {
                p = j;
                break;
            }
        }




    }
} //end FillRibon

function bezier(b1, b2) {
    //Final stage which takes p, p+1 and calculates the rotation, distance on the path and accumulates the total distance
    this.rad = Math.atan(b1.point.mY / b1.point.mX);
    this.b2 = b2;
    this.b1 = b1;
    dx = (b2.x - b1.x);
    dx2 = (b2.x - b1.x) * (b2.x - b1.x);
    this.dist = Math.sqrt(((b2.x - b1.x) * (b2.x - b1.x)) + ((b2.y - b1.y) * (b2.y - b1.y)));
    xDist = xDist + this.dist;
    this.curve = {
        rad: this.rad,
        dist: this.dist,
        cDist: xDist
    };
}

function bezierT(t, startX, startY, control1X, control1Y, control2X, control2Y, endX, endY) {
    //calculates the tangent line to a point in the curve; later used to calculate the degrees of rotation at this point.
    this.mx = (3 * (1 - t) * (1 - t) * (control1X - startX)) + ((6 * (1 - t) * t) * (control2X - control1X)) + (3 * t * t * (endX - control2X));
    this.my = (3 * (1 - t) * (1 - t) * (control1Y - startY)) + ((6 * (1 - t) * t) * (control2Y - control1Y)) + (3 * t * t * (endY - control2Y));
}

function bezier2(t, startX, startY, control1X, control1Y, control2X, control2Y, endX, endY) {
    //Quadratic bezier curve plotter
    this.Bezier1 = new bezier1(t, startX, startY, control1X, control1Y, control2X, control2Y);
    this.Bezier2 = new bezier1(t, control1X, control1Y, control2X, control2Y, endX, endY);
    this.x = ((1 - t) * this.Bezier1.x) + (t * this.Bezier2.x);
    this.y = ((1 - t) * this.Bezier1.y) + (t * this.Bezier2.y);
    this.slope = new bezierT(t, startX, startY, control1X, control1Y, control2X, control2Y, endX, endY);

    this.point = {
        t: t,
        x: this.x,
        y: this.y,
        mX: this.slope.mx,
        mY: this.slope.my
    };
}

function bezier1(t, startX, startY, control1X, control1Y, control2X, control2Y) {
    //linear bezier curve plotter; used recursivly in the quadratic bezier curve calculation
    this.x = ((1 - t) * (1 - t) * startX) + (2 * (1 - t) * t * control1X) + (t * t * control2X);
    this.y = ((1 - t) * (1 - t) * startY) + (2 * (1 - t) * t * control1Y) + (t * t * control2Y);

}
8 голосов
/ 12 марта 2015

Старый старый вопрос ... тем не менее, в своем блоге я довольно внимательно смотрю на создание кругового текста с использованием HTML5 Canvas:

blog.graphicsgen.com

В этом примере параметры включают выравнивание текста по кругу (слева, по центру и справа) под заданным углом, текст, обращенный внутрь и наружу, кернинг (регулируемый зазор между символами) и текст внутри или за пределами радиуса.

Существует также jsfiddle с рабочим примером.

Это выглядит следующим образом:

document.body.appendChild(getCircularText("ROUNDED TEXT LOOKS BEST IN CAPS!", 250, 0, "center", true, true, "Arial", "18pt", 0));

function getCircularText(text, diameter, startAngle, align, textInside, inwardFacing, fName, fSize, kerning) {
    // text:         The text to be displayed in circular fashion
    // diameter:     The diameter of the circle around which the text will
    //               be displayed (inside or outside)
    // startAngle:   In degrees, Where the text will be shown. 0 degrees
    //               if the top of the circle
    // align:        Positions text to left right or center of startAngle
    // textInside:   true to show inside the diameter. False draws outside
    // inwardFacing: true for base of text facing inward. false for outward
    // fName:        name of font family. Make sure it is loaded
    // fSize:        size of font family. Don't forget to include units
    // kearning:     0 for normal gap between letters. positive or
    //               negative number to expand/compact gap in pixels
 //------------------------------------------------------------------------

    // declare and intialize canvas, reference, and useful variables
    align = align.toLowerCase();
    var mainCanvas = document.createElement('canvas');
    var ctxRef = mainCanvas.getContext('2d');
    var clockwise = align == "right" ? 1 : -1; // draw clockwise for aligned right. Else Anticlockwise
    startAngle = startAngle * (Math.PI / 180); // convert to radians

    // calculate height of the font. Many ways to do this
    // you can replace with your own!
    var div = document.createElement("div");
    div.innerHTML = text;
    div.style.position = 'absolute';
    div.style.top = '-10000px';
    div.style.left = '-10000px';
    div.style.fontFamily = fName;
    div.style.fontSize = fSize;
    document.body.appendChild(div);
    var textHeight = div.offsetHeight;
    document.body.removeChild(div);

    // in cases where we are drawing outside diameter,
    // expand diameter to handle it
    if (!textInside) diameter += textHeight * 2;

    mainCanvas.width = diameter;
    mainCanvas.height = diameter;
    // omit next line for transparent background
    mainCanvas.style.backgroundColor = 'lightgray'; 
    ctxRef.font = fSize + ' ' + fName;

    // Reverse letter order for align Left inward, align right outward 
    // and align center inward.
    if (((["left", "center"].indexOf(align) > -1) && inwardFacing) || (align == "right" && !inwardFacing)) text = text.split("").reverse().join(""); 

    // Setup letters and positioning
    ctxRef.translate(diameter / 2, diameter / 2); // Move to center
    startAngle += (Math.PI * !inwardFacing); // Rotate 180 if outward
    ctxRef.textBaseline = 'middle'; // Ensure we draw in exact center
    ctxRef.textAlign = 'center'; // Ensure we draw in exact center

    // rotate 50% of total angle for center alignment
    if (align == "center") {
        for (var j = 0; j < text.length; j++) {
            var charWid = ctxRef.measureText(text[j]).width;
            startAngle += ((charWid + (j == text.length-1 ? 0 : kerning)) / (diameter / 2 - textHeight)) / 2 * -clockwise;
        }
    }

    // Phew... now rotate into final start position
    ctxRef.rotate(startAngle);

    // Now for the fun bit: draw, rotate, and repeat
    for (var j = 0; j < text.length; j++) {
        var charWid = ctxRef.measureText(text[j]).width; // half letter

        ctxRef.rotate((charWid/2) / (diameter / 2 - textHeight) * clockwise);  // rotate half letter

        // draw char at "top" if inward facing or "bottom" if outward
        ctxRef.fillText(text[j], 0, (inwardFacing ? 1 : -1) * (0 - diameter / 2 + textHeight / 2));

        ctxRef.rotate((charWid/2 + kerning) / (diameter / 2 - textHeight) * clockwise); // rotate half letter
    }

    // Return it
    return (mainCanvas);
}
3 голосов
/ 23 января 2012

Вы не можете ни в каком встроенном способе. Обратите внимание, что SVG изначально поддерживает текст вдоль путей, поэтому вы можете рассмотреть SVG вместо этого!

Но вы можете написать собственный код для достижения того же эффекта, который некоторые из нас сделали для этого вопроса здесь: HTML5 Canvas Circle Text

2 голосов
/ 14 октября 2014

Вы можете попробовать следующий код, чтобы увидеть, как писать текст вдоль пути дуги с помощью HTML5 Canvas

function drawTextAlongArc(context, str, centerX, centerY, radius, angle) {
  var len = str.length,
    s;
  context.save();
  context.translate(centerX, centerY);
  context.rotate(-1 * angle / 2);
  context.rotate(-1 * (angle / len) / 2);
  for (var n = 0; n < len; n++) {
    context.rotate(angle / len);
    context.save();
    context.translate(0, -1 * radius);
    s = str[n];
    context.fillText(s, 0, 0);
    context.restore();
  }
  context.restore();
}
var canvas = document.getElementById('myCanvas'),
  context = canvas.getContext('2d'),
  centerX = canvas.width / 2,
  centerY = canvas.height - 30,
  angle = Math.PI * 0.8,
  radius = 150;

context.font = '30pt Calibri';
context.textAlign = 'center';
context.fillStyle = 'blue';
context.strokeStyle = 'blue';
context.lineWidth = 4;
drawTextAlongArc(context, 'Text along arc path', centerX, centerY, radius, angle);

// draw circle underneath text
context.arc(centerX, centerY, radius - 10, 0, 2 * Math.PI, false);
context.stroke();
<!DOCTYPE HTML>
<html>

<head>
  <style>
    body {
      margin: 0px;
      padding: 0px;
    }
  </style>
</head>

<body>
  <canvas id="myCanvas" width="578" height="250"></canvas>
</body>

</html>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...