How Can I draw a Text Along arc path with HTML 5 Canvas?

后端 未结 4 1408
走了就别回头了
走了就别回头了 2020-12-09 07:29

I want to draw a canvas graphic like this flash animation: http://www.cci.com.tr/tr/bizi-taniyin/tarihcemiz/

I drew six arcs and I want to write six words in these

相关标签:
4条回答
  • 2020-12-09 07:36

    You can try the following code to see how to write text along an Arc Path using 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>

    0 讨论(0)
  • 2020-12-09 07:43

    You can't in any built in way. Please note that SVG natively does support text along paths, so you might want to consider SVG instead!

    But you can write custom code in order to achieve the same effect, as some of us did for this question here: HTML5 Canvas Circle Text

    0 讨论(0)
  • 2020-12-09 07:55

    I have a jsFiddle to apply text to any arbitrary Bezier curve definition. Enjoy 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);
    
    }
    
    0 讨论(0)
  • 2020-12-09 07:55

    An old old question... nevertheless, on my blog, I take a fairly close look at creating circular text using HTML5 Canvas:

    html5graphics.blogspot.com

    In the example, options include rounded text alignment (left, center and right) from a given angle, inward and outward facing text, kerning (adjustable gap between characters) and text inside or outside the radius.

    There is also a jsfiddle with a working example.

    It is as follows:

    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);
    }
    
    0 讨论(0)
提交回复
热议问题