Zoeken…


Tekst tekenen

Tekenen op canvas beperkt zich niet alleen tot vormen en afbeeldingen. U kunt ook tekst naar het canvas tekenen.

Als u tekst op het canvas wilt tekenen, fillText u een verwijzing naar het canvas en roept u de methode fillText aan in de context.

var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
ctx.fillText("My text", 0, 0);

De drie vereiste argumenten die worden doorgegeven aan fillText zijn:

  1. De tekst die u wilt weergeven
  2. De horizontale (x-as) positie
  3. De verticale (y-as) positie

Bovendien is er een vierde optioneel argument, dat u kunt gebruiken om de maximale breedte van uw tekst in pixels op te geven. In het onderstaande voorbeeld beperkt de waarde van 200 de maximale breedte van de tekst tot 200 px:

ctx.fillText("My text", 0, 0, 200);

Resultaat:

Voorbeeldresultaat van het gebruik van de methode fillText op canvas

U kunt ook tekst tekenen zonder vulling, en in plaats daarvan alleen een omtrek, met behulp van de methode strokeText :

ctx.strokeText("My text", 0, 0);

Resultaat:

Voorbeeldresultaat van het gebruik van de streekmethode tekst op canvas

Zonder toegepaste eigenschappen voor lettertype-opmaak, geeft het canvas tekst standaard 10px in sans-serif weer, waardoor het moeilijk is om het verschil te zien tussen het resultaat van de methoden fillText en strokeText . Zie het voorbeeld Tekst opmaken voor meer informatie over het vergroten van de tekstgrootte en het toepassen van andere esthetische wijzigingen op tekst.

Tekst opmaken

De standaard lettertype-opmaak van de methoden fillText en strokeText is niet erg esthetisch aantrekkelijk. Gelukkig biedt de canvas-API eigenschappen voor het opmaken van tekst.

Met de eigenschap font kunt u het volgende opgeven:

  • lettertype
  • tekenvariabele
  • lettertype dikte
  • lettergrootte / regelhoogte
  • font-family

Bijvoorbeeld:

ctx.font = "italic small-caps bold 40px Helvetica, Arial, sans-serif";
ctx.fillText("My text", 20, 50);

Resultaat:

Voorbeeldresultaat van het opgeven van lettertype-eigenschappen

Met de eigenschap textAlign kunt u de textAlign ook wijzigen in:

  • links
  • centrum
  • Rechtsaf
  • einde (hetzelfde als rechts)
  • start (hetzelfde als links)

Bijvoorbeeld:

ctx.textAlign = "center";

Tekst in alinea's verpakken

Native Canvas API heeft geen methode om tekst op de volgende regel te plaatsen wanneer een gewenste maximale breedte is bereikt. Dit voorbeeld verpakt tekst in alinea's.

function wrapText(text, x, y, maxWidth, fontSize, fontFace){
  var firstY=y;
  var words = text.split(' ');
  var line = '';
  var lineHeight=fontSize*1.286; // a good approx for 10-18px sizes

  ctx.font=fontSize+" "+fontFace;
  ctx.textBaseline='top';

  for(var n = 0; n < words.length; n++) {
    var testLine = line + words[n] + ' ';
    var metrics = ctx.measureText(testLine);
    var testWidth = metrics.width;
    if(testWidth > maxWidth) {
      ctx.fillText(line, x, y);
      if(n<words.length-1){
          line = words[n] + ' ';
          y += lineHeight;
      }
    }
    else {
      line = testLine;
    }
  }
  ctx.fillText(line, x, y);
}

Teken tekstalinea's in onregelmatige vormen

In dit voorbeeld worden tekstalinea's in alle delen van het canvas getekend met dekkende pixels.

Het werkt door het volgende blok dekkende pixels te vinden dat groot genoeg is om het volgende opgegeven woord te bevatten en dat blok met het opgegeven woord te vullen.

De ondoorzichtige pixels kunnen uit elke bron komen: opdrachten voor padtekening en / of afbeeldingen.

voer hier de afbeeldingsbeschrijving in

<!doctype html>
<html>
<head>
<style>
    body{ background-color:white; padding:10px; }
    #canvas{border:1px solid red;}
</style>
<script>
window.onload=(function(){

    var canvas=document.getElementById("canvas");
    var ctx=canvas.getContext("2d");
    var cw=canvas.width;
    var ch=canvas.height;

    var fontsize=12;
    var fontface='verdana';
    var lineHeight=parseInt(fontsize*1.286);

    var text='It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way; in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.';
    var words=text.split(' ');
    var wordWidths=[];
    ctx.font=fontsize+'px '+fontface;
    for(var i=0;i<words.length;i++){ wordWidths.push(ctx.measureText(words[i]).width); }
    var spaceWidth=ctx.measureText(' ').width;
    var wordIndex=0
    var data=[];

    // Demo: draw Heart 
    // Note: the shape can be ANY opaque drawing -- even an image
    ctx.scale(3,3);
    ctx.beginPath();
    ctx.moveTo(75,40);
    ctx.bezierCurveTo(75,37,70,25,50,25);
    ctx.bezierCurveTo(20,25,20,62.5,20,62.5);
    ctx.bezierCurveTo(20,80,40,102,75,120);
    ctx.bezierCurveTo(110,102,130,80,130,62.5);
    ctx.bezierCurveTo(130,62.5,130,25,100,25);
    ctx.bezierCurveTo(85,25,75,37,75,40);
    ctx.fillStyle='red';
    ctx.fill();
    ctx.setTransform(1,0,0,1,0,0);

    // fill heart with text
    ctx.fillStyle='white';
    var imgDataData=ctx.getImageData(0,0,cw,ch).data;
    for(var i=0;i<imgDataData.length;i+=4){
        data.push(imgDataData[i+3]);
    }
    placeWords();   

    // draw words sequentially into next available block of 
    //    available opaque pixels 
    function placeWords(){
        var sx=0;
        var sy=0;
        var y=0;
        var wordIndex=0;
        ctx.textBaseline='top';
        while(y<ch && wordIndex<words.length){
            sx=0;
            sy=y;
            var startingIndex=wordIndex;
            while(sx<cw && wordIndex<words.length){
                var x=getRect(sx,sy,lineHeight);
                var available=x-sx;
                var spacer=spaceWidth;  // spacer=0 to have no left margin
                var w=spacer+wordWidths[wordIndex];
                while(available>=w){
                    ctx.fillText(words[wordIndex],spacer+sx,sy);
                    sx+=w;
                    available-=w;
                    spacer=spaceWidth;
                    wordIndex++;
                    w=spacer+wordWidths[wordIndex];
                }
                sx=x+1;
            }
            y=(wordIndex>startingIndex)?y+lineHeight:y+1;
        }
    }

    // find a rectangular block of opaque pixels
    function getRect(sx,sy,height){
        var x=sx;
        var y=sy;
        var ok=true;
        while(ok){
            if(data[y*cw+x]<250){ok=false;}
            y++;
            if(y>=sy+height){
                y=sy;
                x++;
                if(x>=cw){ok=false;}
            }
        }
        return(x);
    }

}); // end $(function(){});
</script>
</head>
<body>
    <h4>Note: the shape must be closed and alpha>=250 inside</h4>
    <canvas id="canvas" width=400 height=400></canvas>
</body>
</html>

Vul tekst met een afbeelding in

In dit voorbeeld wordt tekst gevuld met een opgegeven afbeelding.

Belangrijk! De opgegeven afbeelding moet volledig zijn geladen voordat deze functie wordt aangeroepen, anders mislukt de tekening. Gebruik image.onload om te controleren of de afbeelding volledig is geladen.

voer hier de afbeeldingsbeschrijving in

function drawImageInsideText(canvas,x,y,img,text,font){
    var c=canvas.cloneNode();
    var ctx=c.getContext('2d');
    ctx.font=font;
    ctx.fillText(text,x,y);
    ctx.globalCompositeOperation='source-atop';
    ctx.drawImage(img,0,0);
    canvas.getContext('2d').drawImage(c,0,0);
}

Tekst langs een boog renderen.

Dit voorbeeld laat zien hoe tekst langs een boog kan worden weergegeven. Het omvat hoe u functionaliteit kunt toevoegen aan de CanvasRenderingContext2D door het prototype uit te breiden.

Dit voorbeeld is afgeleid van het antwoord Circular Text .


Voorbeeldweergave

Voorbeeld van cirkel tekst


Voorbeeld code

Het voorbeeld voegt 3 nieuwe functies voor tekstweergave toe aan het 2D-contextprototype.

  • ctx.fillCircleText (tekst, x, y, straal, start, einde, vooruit);
  • ctx.strokeCircleText (tekst, x, y, straal, start, einde, vooruit);
  • ctx.measureCircleText (tekst, straal);
(function(){
    const FILL = 0;        // const to indicate filltext render
    const STROKE = 1;
    var renderType = FILL; // used internal to set fill or stroke text
    const multiplyCurrentTransform = true; // if true Use current transform when rendering
                                           // if false use absolute coordinates which is a little quicker
                                           // after render the currentTransform is restored to default transform
                                           
      

    // measure circle text
    // ctx: canvas context
    // text: string of text to measure
    // r: radius in pixels
    //
    // returns the size metrics of the text
    //
    // width: Pixel width of text
    // angularWidth : angular width of text in radians
    // pixelAngularSize : angular width of a pixel in radians
    var measure = function(ctx, text, radius){        
        var textWidth = ctx.measureText(text).width; // get the width of all the text
        return {
            width               : textWidth,
            angularWidth        : (1 / radius) * textWidth,
            pixelAngularSize    : 1 / radius
        };
    }

    // displays text along a circle
    // ctx: canvas context
    // text: string of text to measure
    // x,y: position of circle center
    // r: radius of circle in pixels
    // start: angle in radians to start. 
    // [end]: optional. If included text align is ignored and the text is 
    //        scaled to fit between start and end;
    // [forward]: optional default true. if true text direction is forwards, if false  direction is backward
    var circleText = function (ctx, text, x, y, radius, start, end, forward) {
        var i, textWidth, pA, pAS, a, aw, wScale, aligned, dir, fontSize;
        if(text.trim() === "" || ctx.globalAlpha === 0){ // dont render empty string or transparent
            return;
        }
        if(isNaN(x) || isNaN(y) || isNaN(radius) || isNaN(start) || (end !== undefined && end !== null && isNaN(end))){ // 
            throw TypeError("circle text arguments requires a number for x,y, radius, start, and end.")
        }
        aligned = ctx.textAlign;        // save the current textAlign so that it can be restored at end
        dir = forward ? 1 : forward === false ? -1 : 1;  // set dir if not true or false set forward as true  
        pAS = 1 / radius;               // get the angular size of a pixel in radians
        textWidth = ctx.measureText(text).width; // get the width of all the text
        if (end !== undefined && end !== null) { // if end is supplied then fit text between start and end
            pA = ((end - start) / textWidth) * dir;
            wScale = (pA / pAS) * dir;
        } else {                 // if no end is supplied correct start and end for alignment
            // if forward is not given then swap top of circle text to read the correct direction
            if(forward === null || forward === undefined){
                if(((start % (Math.PI * 2)) + Math.PI * 2) % (Math.PI * 2) > Math.PI){
                    dir = -1;
                }
            }
            pA = -pAS * dir ;
            wScale = -1 * dir;
            switch (aligned) {
            case "center":       // if centered move around half width
                start -= (pA * textWidth )/2;
                end = start + pA * textWidth;
                break;
            case "right":// intentionally falls through to case "end"
            case "end":
                end = start;
                start -= pA * textWidth;
                break;
            case "left":  // intentionally falls through to case "start"
            case "start":
                end = start + pA * textWidth;
            }
        }

        ctx.textAlign = "center";                     // align for rendering
        a = start;                                    // set the start angle
        for (var i = 0; i < text.length; i += 1) {    // for each character
            aw = ctx.measureText(text[i]).width * pA; // get the angular width of the text
            var xDx = Math.cos(a + aw / 2);           // get the yAxies vector from the center x,y out
            var xDy = Math.sin(a + aw / 2);
            if(multiplyCurrentTransform){ // transform multiplying current transform
                ctx.save();
                if (xDy < 0) { // is the text upside down. If it is flip it
                    ctx.transform(-xDy * wScale, xDx * wScale, -xDx, -xDy, xDx * radius + x, xDy * radius + y);
                } else {
                    ctx.transform(-xDy * wScale, xDx * wScale, xDx, xDy, xDx * radius + x, xDy * radius + y);
                }
            }else{
                if (xDy < 0) { // is the text upside down. If it is flip it
                    ctx.setTransform(-xDy * wScale, xDx * wScale, -xDx, -xDy, xDx * radius + x, xDy * radius + y);
                } else {
                    ctx.setTransform(-xDy * wScale, xDx * wScale, xDx, xDy, xDx * radius + x, xDy * radius + y);
                }
            }
            if(renderType === FILL){
                ctx.fillText(text[i], 0, 0);    // render the character
            }else{                    
                ctx.strokeText(text[i], 0, 0);  // render the character
            }
            if(multiplyCurrentTransform){  // restore current transform
                ctx.restore();
            }
            a += aw;                     // step to the next angle
        }
        // all done clean up.
        if(!multiplyCurrentTransform){
            ctx.setTransform(1, 0, 0, 1, 0, 0); // restore the transform
        }
        ctx.textAlign = aligned;            // restore the text alignment
    }
    // define fill text
    var fillCircleText = function(text, x, y, radius, start, end, forward){
        renderType = FILL;
        circleText(this, text, x, y, radius, start, end, forward);
    }
    // define stroke text
    var strokeCircleText = function(text, x, y, radius, start, end, forward){
        renderType = STROKE;
        circleText(this, text, x, y, radius, start, end, forward);
    }
    // define measure text
    var measureCircleTextExt = function(text,radius){
        return measure(this, text, radius);
    }
    // set the prototypes
    CanvasRenderingContext2D.prototype.fillCircleText = fillCircleText;
    CanvasRenderingContext2D.prototype.strokeCircleText = strokeCircleText;
    CanvasRenderingContext2D.prototype.measureCircleText = measureCircleTextExt;  
})();

Functie beschrijvingen

Dit voorbeeld voegt 3 functies toe aan het CanvasRenderingContext2D prototype . fillCircleText , strokeCircleText en measureCircleText

CanvasRenderingContext2D.fillCircleText (tekst, x, y, radius, start, [end, [forward]]);

CanvasRenderingContext2D.strokeCircleText (tekst, x, y, radius, start, [end, [forward]]);

  • tekst: tekst die als tekenreeks moet worden weergegeven.
  • x , y : positie van het middelpunt van de cirkel als getallen.
  • radius: cirkelradius in pixels
  • start: hoek in radialen om te starten.
  • [einde]: optioneel. Indien opgenomen wordt ctx.textAlign genegeerd en wordt de tekst geschaald zodat deze tussen begin en einde past.
  • [vooruit]: optionele standaard 'true'. als ware tekstrichting vooruit is, als 'valse' richting achteruit is.

Beide functies gebruiken de textBaseline om de tekst verticaal rond de straal te plaatsen. Gebruik ctx.TextBaseline voor de beste resultaten.

Functies geven een TypeError is een van de numerieke argumenten als NaN.

Als de text argument trims op een lege tekenreeks of ctx.globalAlpha = 0 de functie alleen maar daalt door en doet niets.

CanvasRenderingContext2D.measureCircleText (tekst, straal);

 - **text:** String of text to measure.
 - **radius:** radius of circle in pixels.

Retourneert een object met verschillende groottemetingen voor het weergeven van circulaire tekst

   - **width:** Pixel width of text as it would normaly be rendered
   - **angularWidth:** angular width of text in radians.
   - **pixelAngularSize:** angular width of a pixel in radians.

Gebruik voorbeelden

const rad = canvas.height * 0.4;
const text = "Hello circle TEXT!";
const fontSize = 40;
const centX = canvas.width / 2;
const centY = canvas.height / 2;
ctx.clearRect(0,0,canvas.width,canvas.height)

ctx.font = fontSize + "px verdana";
ctx.textAlign = "center";
ctx.textBaseline = "bottom";
ctx.fillStyle = "#000";
ctx.strokeStyle = "#666";

// Text under stretched from Math.PI to 0 (180 - 0 deg)
ctx.fillCircleText(text, centX, centY, rad, Math.PI, 0);

// text over top centered at Math.PI * 1.5 ( 270 deg)
ctx.fillCircleText(text, centX, centY, rad, Math.PI * 1.5);

// text under top centered at Math.PI * 1.5 ( 270 deg)
ctx.textBaseline = "top";
ctx.fillCircleText(text, centX, centY, rad, Math.PI * 1.5);


// text over top centered at Math.PI * 1.5 ( 270 deg)
ctx.textBaseline = "middle";
ctx.fillCircleText(text, centX, centY, rad, Math.PI * 1.5);


// Use measureCircleText to get angular size
var circleTextMetric = ctx.measureCircleText("Text to measure", rad);
console.log(circleTextMetric.width);            // width of text if rendered normally
console.log(circleTextMetric.angularWidth);     // angular width of text
console.log(circleTextMetric.pixelAngularSize); // angular size of a pixel    



// Use measure text to draw a arc around the text
ctx.textBaseline = "middle";
var width = ctx.measureCircleText(text, rad).angularWidth;    
ctx.fillCircleText(text, centX, centY, rad, Math.PI * 1.5);

// render the arc around the text
ctx.strokeStyle= "red";
ctx.lineWidth = 3;
ctx.beginPath();
ctx.arc(centX, centY, rad + fontSize / 2,Math.PI * 1.5 - width/2,Math.PI*1.5 + width/2);
ctx.arc(centX, centY, rad - fontSize / 2,Math.PI * 1.5 + width/2,Math.PI*1.5 - width/2,true);
ctx.closePath();
ctx.stroke();


OPMERKING: de weergegeven tekst is slechts een benadering van cirkelvormige tekst. Als bijvoorbeeld twee l's worden weergegeven, zijn de twee lijnen niet parallel, maar als u een "H" geeft, zijn de twee randen evenwijdig. Dit komt omdat elk teken zo dicht mogelijk bij de vereiste richting wordt weergegeven, in plaats van dat elke pixel correct wordt getransformeerd om cirkelvormige tekst te maken.

OPMERKING: const multiplyCurrentTransform = true; gedefinieerd in dit voorbeeld wordt gebruikt om de gebruikte transformatiemethode in te stellen. Indien false de transformatie voor circulaire tekstweergave absoluut en niet afhankelijk van de huidige transformatiestatus. De tekst wordt niet beïnvloed door eerdere transformaties op schaal, roteren of vertalen. Dit verhoogt de prestaties van de setTransform(1,0,0,1,0,0) , nadat de functie wordt aangeroepen, wordt de transformatie ingesteld op de standaard setTransform(1,0,0,1,0,0)

Als multiplyCurrentTransform = true (in dit voorbeeld als standaard ingesteld), gebruikt de tekst de huidige transformatie zodat de tekst kan worden vertaald, vertaald, scheefgetrokken, geroteerd, enz. Maar de huidige transformatie wordt gewijzigd voordat de functies fillCircleText en strokeCircleText . Afhankelijk van de huidige status van de 2D-context kan dit iets langzamer zijn dan multiplyCurrentTransform = false

Tekst op curve, kubieke en kwadratische beziers

voer hier de afbeeldingsbeschrijving in

textOnCurve (tekst, offset, x1, y1, x2, y2, x3, y3, x4, y4)

Geeft tekst weer op kwadratische en kubieke curven.

  • text is de tekst die moet worden weergegeven
  • offset afstand vanaf het begin van de curve naar tekst> = 0
  • x1,y1 - x3,y3 punten van kwadratische curve of
  • x1,y1 - x4,y4 punten van kubieke curve of

Voorbeeld gebruik:

textOnCurve("Hello world!",50,100,100,200,200,300,100); // draws text on quadratic curve
                                                        // 50 pixels from start of curve


textOnCurve("Hello world!",50,100,100,200,200,300,100,400,200); 
                                                        // draws text on cubic curve
                                                        // 50 pixels from start of curve

De functie en curver helperfunctie

// pass 8 values for cubic bezier
// pass 6 values for quadratic
// Renders text from start of curve
var textOnCurve = function(text,offset,x1,y1,x2,y2,x3,y3,x4,y4){
    ctx.save();
    ctx.textAlign = "center";
    var widths = [];
    for(var i = 0; i < text.length; i ++){
        widths[widths.length] = ctx.measureText(text[i]).width;
    }
    var ch = curveHelper(x1,y1,x2,y2,x3,y3,x4,y4);
    var pos = offset;
    var cpos = 0;

    for(var i = 0; i < text.length; i ++){
        pos += widths[i] / 2;
        cpos = ch.forward(pos);
        ch.tangent(cpos);
        ctx.setTransform(ch.vect.x, ch.vect.y, -ch.vect.y, ch.vect.x, ch.vec.x, ch.vec.y);
        ctx.fillText(text[i],0,0);     

        pos += widths[i] / 2;
    }
    ctx.restore();
}

De curve helper-functie is ontworpen om de prestaties van het vinden van punten op de bezier te verbeteren.

// helper function locates points on bezier curves.
function curveHelper(x1, y1, x2, y2, x3, y3, x4, y4){
    var tx1, ty1, tx2, ty2, tx3, ty3, tx4, ty4;
    var a,b,c,u;
    var vec,currentPos,vec1,vect;
    vec = {x:0,y:0};
    vec1 = {x:0,y:0};
    vect = {x:0,y:0};
    quad = false;
    currentPos = 0;
    currentDist = 0;
    if(x4 === undefined || x4 === null){
        quad = true;
        x4 = x3;
        y4 = y3;
    }
    var estLen = Math.sqrt((x4 - x1) * (x4 - x1) + (y4 - y1) * (y4 - y1));
    var onePix = 1 / estLen;
    function posAtC(c){
        tx1 = x1; ty1 = y1;
        tx2 = x2; ty2 = y2;
        tx3 = x3; ty3 = y3;
        tx1 += (tx2 - tx1) * c;
        ty1 += (ty2 - ty1) * c;
        tx2 += (tx3 - tx2) * c;
        ty2 += (ty3 - ty2) * c;
        tx3 += (x4 - tx3) * c;
        ty3 += (y4 - ty3) * c;
        tx1 += (tx2 - tx1) * c;
        ty1 += (ty2 - ty1) * c;
        tx2 += (tx3 - tx2) * c;
        ty2 += (ty3 - ty2) * c;
        vec.x = tx1 + (tx2 - tx1) * c;
        vec.y = ty1 + (ty2 - ty1) * c;    
        return vec;
    }
    function posAtQ(c){
        tx1 = x1; ty1 = y1;
        tx2 = x2; ty2 = y2;
        tx1 += (tx2 - tx1) * c;
        ty1 += (ty2 - ty1) * c;
        tx2 += (x3 - tx2) * c;
        ty2 += (y3 - ty2) * c;
        vec.x = tx1 + (tx2 - tx1) * c;
        vec.y = ty1 + (ty2 - ty1) * c;
        return vec;
    }    
    function forward(dist){
        var step;
        helper.posAt(currentPos);

        while(currentDist < dist){
            vec1.x = vec.x;
            vec1.y = vec.y;            
            currentPos += onePix;
            helper.posAt(currentPos);
            currentDist += step = Math.sqrt((vec.x - vec1.x) * (vec.x - vec1.x) + (vec.y - vec1.y) * (vec.y - vec1.y));

        }
        currentPos -= ((currentDist - dist) / step) * onePix
        currentDist -= step;
        helper.posAt(currentPos);
        currentDist += Math.sqrt((vec.x - vec1.x) * (vec.x - vec1.x) + (vec.y - vec1.y) * (vec.y - vec1.y));
        return currentPos;
    }
    
    function tangentQ(pos){
        a = (1-pos) * 2;
        b = pos * 2;
        vect.x = a * (x2 - x1) + b * (x3 - x2);
        vect.y = a * (y2 - y1) + b * (y3 - y2);       
        u = Math.sqrt(vect.x * vect.x + vect.y * vect.y);
        vect.x /= u;
        vect.y /= u;        
    }
    function tangentC(pos){
        a  = (1-pos)
        b  = 6 * a * pos;       
        a *= 3 * a;                  
        c  = 3 * pos * pos; 
        vect.x  = -x1 * a + x2 * (a - b) + x3 * (b - c) + x4 * c;
        vect.y  = -y1 * a + y2 * (a - b) + y3 * (b - c) + y4 * c;
        u = Math.sqrt(vect.x * vect.x + vect.y * vect.y);
        vect.x /= u;
        vect.y /= u;
    }  
    var helper = {
        vec : vec,
        vect : vect,
        forward : forward,
    }
    if(quad){
        helper.posAt = posAtQ;
        helper.tangent = tangentQ;
    }else{
        helper.posAt = posAtC;
        helper.tangent = tangentC;
    }
    return helper
}

Gerechtvaardigde tekst

In dit voorbeeld wordt gerechtvaardigde tekst weergegeven. Het voegt extra functionaliteit toe aan de CanvasRenderingContext2D door het prototype uit te breiden of als een globaal object justifiedText (optioneel zie opmerking A).


Voorbeeldweergave.

voer hier de afbeeldingsbeschrijving in
Code om deze afbeelding weer te geven staat in de gebruiksvoorbeelden onderaan .


Het voorbeeld

De functie als een anonieme onmiddellijk opgeroepen functie.

(function(){
    const FILL = 0;        // const to indicate filltext render
    const STROKE = 1;
    const MEASURE = 2;
    var renderType = FILL; // used internal to set fill or stroke text
    
    var maxSpaceSize = 3; // Multiplier for max space size. If greater then no justificatoin applied
    var minSpaceSize = 0.5; // Multiplier for minimum space size
    var renderTextJustified = function(ctx,text,x,y,width){
        var words, wordsWidth, count, spaces, spaceWidth, adjSpace, renderer, i, textAlign, useSize, totalWidth;
        textAlign = ctx.textAlign; // get current align settings
        ctx.textAlign = "left";
        wordsWidth = 0;
        words = text.split(" ").map(word => {
            var w = ctx.measureText(word).width;                
            wordsWidth += w;
            return {
                width : w,
                word : word,
            };
        });
        // count = num words, spaces = number spaces, spaceWidth normal space size
        // adjSpace new space size >= min size. useSize Resulting space size used to render
        count = words.length;
        spaces = count - 1;
        spaceWidth = ctx.measureText(" ").width;
        adjSpace = Math.max(spaceWidth * minSpaceSize, (width - wordsWidth) / spaces);
        useSize = adjSpace > spaceWidth * maxSpaceSize ? spaceWidth : adjSpace;
        totalWidth = wordsWidth + useSize * spaces
        if(renderType === MEASURE){ // if measuring return size
            ctx.textAlign = textAlign;
            return totalWidth;
        }
        renderer = renderType === FILL ? ctx.fillText.bind(ctx) : ctx.strokeText.bind(ctx); // fill or stroke
        switch(textAlign){
            case "right":
                x -= totalWidth;
                break;
            case "end":
                x += width - totalWidth;
                break;
            case "center": // intentional fall through to default
                x -= totalWidth / 2;                     
            default:
        }
        if(useSize === spaceWidth){ // if space size unchanged
            renderer(text,x,y);
        } else {
            for(i = 0; i < count; i += 1){
                renderer(words[i].word,x,y);
                x += words[i].width;
                x += useSize;
            }
        }
        ctx.textAlign = textAlign;
    }
    // Parse vet and set settings object.
    var justifiedTextSettings = function(settings){
        var min,max;
        var vetNumber = (num, defaultNum) => {
            num = num !== null && num !== null && !isNaN(num) ? num : defaultNum;
            if(num < 0){
                num = defaultNum;
            }
            return num;
        }
        if(settings === undefined || settings === null){
            return;
        }
        max = vetNumber(settings.maxSpaceSize, maxSpaceSize);
        min = vetNumber(settings.minSpaceSize, minSpaceSize);
        if(min > max){
            return;
        }
        minSpaceSize = min;
        maxSpaceSize = max;
    }
    // define fill text
    var fillJustifyText = function(text, x, y, width, settings){
        justifiedTextSettings(settings);
        renderType = FILL;
        renderTextJustified(this, text, x, y, width);
    }
    // define stroke text
    var strokeJustifyText = function(text, x, y, width, settings){
        justifiedTextSettings(settings);
        renderType = STROKE;
        renderTextJustified(this, text, x, y, width);
    }
    // define measure text
    var measureJustifiedText = function(text, width, settings){
        justifiedTextSettings(settings);
        renderType = MEASURE;
        return renderTextJustified(this, text, 0, 0, width);
    }
    // code point A
    // set the prototypes
    CanvasRenderingContext2D.prototype.fillJustifyText = fillJustifyText;
    CanvasRenderingContext2D.prototype.strokeJustifyText = strokeJustifyText;
    CanvasRenderingContext2D.prototype.measureJustifiedText = measureJustifiedText;  
    // code point B
    
    // optional code if you do not wish to extend the CanvasRenderingContext2D prototype
    /* Uncomment from here to the closing comment
    window.justifiedText = {
        fill : function(ctx, text, x, y, width, settings){
            justifiedTextSettings(settings);
            renderType = FILL;
            renderTextJustified(ctx, text, x, y, width);
        },
        stroke : function(ctx, text, x, y, width, settings){
            justifiedTextSettings(settings);
            renderType = STROKE;
            renderTextJustified(ctx, text, x, y, width);
        },
        measure : function(ctx, text, width, settings){
            justifiedTextSettings(settings);
            renderType = MEASURE;
            return renderTextJustified(ctx, text, 0, 0, width);
        }
    }
    to here*/
})();

Opmerking A: Als u het CanvasRenderingContext2D prototype niet wilt uitbreiden, verwijdert u uit het voorbeeld alle code tussen // code point A en // code point B en geeft u de code met de markering /* Uncomment from here to the closing comment Uncomment /* Uncomment from here to the closing comment


Hoe te gebruiken

Drie functies zijn toegevoegd aan de CanvasRenderingContext2D en zijn beschikbaar voor alle gemaakte 2D- CanvasRenderingContext2D .

  • ctx.fillJustifyText (tekst, x, y, breedte, [instellingen]);
  • ctx.strokeJustifyText (tekst, x, y, breedte, [instellingen]);
  • ctx.measureJustifiedText (tekst, breedte, [instellingen]);

Tekst vullen en lijnfunctie vul tekst of lijn en gebruik dezelfde argumenten. measureJustifiedText retourneert de werkelijke breedte waarin tekst zou worden weergegeven. Dit kan gelijk, kleiner of groter dan het argument width afhankelijk van de huidige instellingen.

Opmerking: argumenten binnen [ en ] zijn optioneel.

Functie argumenten

  • text: String die de weer te geven tekst bevat.

  • x, y: Coördinaten om de tekst weer te geven op.

  • breedte: breedte van de uitgevulde tekst. Tekst vergroot / verkleint de spaties tussen woorden zodat ze in de breedte passen. Als de ruimte tussen woorden groter is dan maxSpaceSize (standaard = 6), wordt de normale afstand gebruikt en vult de tekst niet de vereiste breedte. Als de afstand kleiner is dan minSpaceSize (standaard = 0,5) in tijd normale afstand, wordt de minimale ruimtegrootte gebruikt en overschrijdt de tekst de gevraagde breedte

  • instellingen: optioneel. Object met minimale en maximale ruimtegroottes.

Het settings is optioneel en als het niet wordt opgenomen, wordt de laatst gedefinieerde instelling of de standaardinstelling (hieronder weergegeven) gebruikt.

Zowel min als max zijn de min en max grootte voor het [spatie] karakter dat woorden scheidt. De standaard maxSpaceSize = 6 betekent dat wanneer de spatie tussen tekens> 63 * ctx.measureText ("") is. Breedte-tekst niet gerechtvaardigd is. Als de te rechtvaardigen tekst spaties heeft kleiner dan minSpaceSize = 0.5 (standaardwaarde 0,5) * ctx.measureText(" ").width de afstand ingesteld op minSpaceSize * ctx.measureText(" ").width en de resulterende tekst wordt overschreden de rechtvaardigende breedte.

De volgende regels zijn van toepassing, min en max moeten getallen zijn. Zo niet, dan worden de bijbehorende waarden niet gewijzigd. Als minSpaceSize groter is dan maxSpaceSize beide invoerinstellingen ongeldig en wordt min max niet gewijzigd.

Voorbeeld van een object met standaardinstellingen

settings = { 
    maxSpaceSize : 6;   // Multiplier for max space size. 
    minSpaceSize : 0.5; // Multiplier for minimum space size
};

OPMERKING: Deze tekstfuncties introduceren een subtiele gedragsverandering voor de eigenschap textAlign van de 2D-context. 'Links', 'rechts', 'midden' en 'start' gedragen zich zoals verwacht, maar 'einde' zal niet uitlijnen rechts van het functieargument x maar eerder rechts van x + width

Opmerking: instellingen (minimale en maximale ruimtegrootte) zijn algemeen voor alle 2D contextobjecten.


GEBRUIK Voorbeelden

var i = 0;
text[i++] = "This text is aligned from the left of the canvas."; 
text[i++] = "This text is near the max spacing size"; 
text[i++] = "This text is way too short."; 
text[i++] = "This text is too long for the space provied and will overflow#";
text[i++] = "This text is aligned using 'end' and starts at x + width"; 
text[i++] = "This text is near the max spacing size"; 
text[i++] = "This text is way too short."; 
text[i++] = "#This text is too long for the space provied and will overflow";
text[i++] = "This is aligned with 'center' and is placed from the center"; 
text[i++] = "This text is near the max spacing size"; 
text[i++] = "This text is way too short."; 
text[i++] = "This text is just too long for the space provied and will overflow";

// ctx is the 2d context
// canvas is the canvas

ctx.clearRect(0,0,w,h);
ctx.font = "25px arial";
ctx.textAlign = "center"
var left = 20;
var center = canvas.width / 2;
var width = canvas.width-left*2;
var y = 40;
var size = 16;
var i = 0;
ctx.fillText("Justified text examples.",center,y);
y+= 40;
ctx.font = "14px arial";
ctx.textAlign = "left"
var ww = ctx.measureJustifiedText(text[0], width);
var setting = {
    maxSpaceSize : 6,
    minSpaceSize : 0.5
}
ctx.strokeStyle = "red"
ctx.beginPath();
ctx.moveTo(left,y - size * 2);
ctx.lineTo(left, y + size * 15);
ctx.moveTo(canvas.width - left,y - size * 2);
ctx.lineTo(canvas.width - left, y + size * 15);
ctx.stroke();
ctx.textAlign = "left";
ctx.fillStyle = "red";
ctx.fillText("< 'left' aligned",left,y - size)
ctx.fillStyle = "black";
ctx.fillJustifyText(text[i++], left, y, width, setting);  // settings is remembered
ctx.fillJustifyText(text[i++], left, y+=size, width);
ctx.fillJustifyText(text[i++], left, y+=size, width);
ctx.fillJustifyText(text[i++], left, y+=size, width);
y += 2.3*size;
ctx.fillStyle = "red";
ctx.fillText("< 'end' aligned from x plus the width -------------------->",left,y - size)
ctx.fillStyle = "black";
ctx.textAlign = "end";
ctx.fillJustifyText(text[i++], left, y, width);
ctx.fillJustifyText(text[i++], left, y+=size, width);
ctx.fillJustifyText(text[i++], left, y+=size, width);
ctx.fillJustifyText(text[i++], left, y+=size, width);

y += 40;
ctx.strokeStyle = "red"
ctx.beginPath();
ctx.moveTo(center,y - size * 2);
ctx.lineTo(center, y + size * 5);
ctx.stroke();
ctx.textAlign = "center";
ctx.fillStyle = "red";
ctx.fillText("'center' aligned",center,y - size)
ctx.fillStyle = "black";
ctx.fillJustifyText(text[i++], center, y, width);
ctx.fillJustifyText(text[i++], center, y+=size, width);
ctx.fillJustifyText(text[i++], center, y+=size, width);
ctx.fillJustifyText(text[i++], center, y+=size, width);

Gerechtvaardigde paragrafen.

Geeft tekst weer als gerechtvaardigde alinea's. VEREIST het voorbeeld Uitgevulde tekst


Voorbeeld render

voer hier de afbeeldingsbeschrijving in
De bovenste alinea heeft setting.compact = true en de onderkant false en de regelafstand is 1,2 in plaats van de standaard 1.5 . Weergegeven door voorbeeld van codegebruik onderaan dit voorbeeld.


Voorbeeld code

 // Requires justified text extensions 
(function(){
    // code point A
    if(typeof CanvasRenderingContext2D.prototype.fillJustifyText !== "function"){
        throw new ReferenceError("Justified Paragraph extension missing requiered CanvasRenderingContext2D justified text extension");
    }
    var maxSpaceSize = 3; // Multiplier for max space size. If greater then no justificatoin applied
    var minSpaceSize = 0.5; // Multiplier for minimum space size   
    var compact = true; // if true then try and fit as many words as possible. If false then try to get the spacing as close as possible to normal
    var lineSpacing = 1.5; // space between lines
    const noJustifySetting = {  // This setting forces justified text off. Used to render last line of paragraph.
        minSpaceSize : 1,
        maxSpaceSize : 1,
    }

    // Parse vet and set settings object.
    var justifiedTextSettings = function(settings){
        var min, max;
        var vetNumber = (num, defaultNum) => {
            num = num !== null && num !== null && !isNaN(num) ? num : defaultNum;
            return num < 0 ? defaultNum : num;
        }
        if(settings === undefined || settings === null){ return; }
        compact = settings.compact === true ? true : settings.compact === false ? false : compact;
        max = vetNumber(settings.maxSpaceSize, maxSpaceSize);
        min = vetNumber(settings.minSpaceSize, minSpaceSize);
        lineSpacing = vetNumber(settings.lineSpacing, lineSpacing);
        if(min > max){ return; }
        minSpaceSize = min;
        maxSpaceSize = max;
    }        
    var getFontSize = function(font){  // get the font size. 
        var numFind = /[0-9]+/;
        var number = numFind.exec(font)[0];
        if(isNaN(number)){
            throw new ReferenceError("justifiedPar Cant find font size");
        }
        return Number(number);
    }
    function justifiedPar(ctx, text, x, y, width, settings, stroke){
        var spaceWidth, minS, maxS, words, count, lines, lineWidth, lastLineWidth, lastSize, i, renderer, fontSize, adjSpace, spaces, word, lineWords, lineFound;
        spaceWidth = ctx.measureText(" ").width;
        minS = spaceWidth * minSpaceSize;
        maxS = spaceWidth * maxSpaceSize;
        words = text.split(" ").map(word => {  // measure all words.
            var w = ctx.measureText(word).width;                
            return {
                width : w,
                word : word,
            };
        });
        // count = num words, spaces = number spaces, spaceWidth normal space size
        // adjSpace new space size >= min size. useSize Resulting space size used to render
        count = 0;
        lines = [];
        // create lines by shifting words from the words array until the spacing is optimal. If compact
        // true then will true and fit as many words as possible. Else it will try and get the spacing as
        // close as possible to the normal spacing
        while(words.length > 0){
            lastLineWidth = 0;
            lastSize = -1;
            lineFound = false;
            // each line must have at least one word.
            word = words.shift();
            lineWidth = word.width;
            lineWords = [word.word];
            count = 0;
            while(lineWidth < width && words.length > 0){ // Add words to line
                word = words.shift();
                lineWidth += word.width;
                lineWords.push(word.word);
                count += 1;
                spaces = count - 1;
                adjSpace =  (width - lineWidth) / spaces;
                if(minS > adjSpace){  // if spacing less than min remove last word and finish line
                    lineFound = true;
                    words.unshift(word);
                    lineWords.pop();
                }else{
                    if(!compact){ // if compact mode 
                        if(adjSpace < spaceWidth){ // if less than normal space width
                            if(lastSize === -1){
                                lastSize = adjSpace;
                            }
                            // check if with last word on if its closer to space width
                            if(Math.abs(spaceWidth - adjSpace) < Math.abs(spaceWidth - lastSize)){
                                lineFound = true; // yes keep it
                            }else{
                                words.unshift(word);  // no better fit if last word removes
                                lineWords.pop();
                                lineFound = true;
                            }
                        }
                    }
                }
                lastSize = adjSpace; // remember spacing 
            }
            lines.push(lineWords.join(" ")); // and the line
        }
        // lines have been worked out get font size, render, and render all the lines. last
        // line may need to be rendered as normal so it is outside the loop.
        fontSize = getFontSize(ctx.font);
        renderer = stroke === true ? ctx.strokeJustifyText.bind(ctx) : ctx.fillJustifyText.bind(ctx);
        for(i = 0; i < lines.length - 1; i ++){
            renderer(lines[i], x, y, width, settings);
            y += lineSpacing * fontSize;
        }
        if(lines.length > 0){ // last line if left or start aligned for no justify
            if(ctx.textAlign === "left" || ctx.textAlign === "start"){
                renderer(lines[lines.length - 1], x, y, width, noJustifySetting);
                ctx.measureJustifiedText("", width, settings);
            }else{
                renderer(lines[lines.length - 1], x, y, width);
            }
        }
        // return details about the paragraph.
        y += lineSpacing * fontSize;
        return {
            nextLine : y,
            fontSize : fontSize,
            lineHeight : lineSpacing * fontSize,
        };
    }
    // define fill
    var fillParagraphText = function(text, x, y, width, settings){
        justifiedTextSettings(settings);
        settings = {
            minSpaceSize : minSpaceSize,
            maxSpaceSize : maxSpaceSize,
        };
        return justifiedPar(this, text, x, y, width, settings);
    }
    // define stroke
    var strokeParagraphText = function(text, x, y, width, settings){
        justifiedTextSettings(settings);
        settings = {
            minSpaceSize : minSpaceSize,
            maxSpaceSize : maxSpaceSize,
        };
        return justifiedPar(this, text, x, y, width, settings,true);
    }
    CanvasRenderingContext2D.prototype.fillParaText = fillParagraphText;
    CanvasRenderingContext2D.prototype.strokeParaText = strokeParagraphText;
})();

LET OP hiermee wordt het CanvasRenderingContext2D prototype uitgebreid. Als u niet wilt dat dit gebeurt, gebruikt u de voorbeeld- uitgevulde tekst om uit te werken hoe u dit voorbeeld kunt wijzigen om deel uit te maken van de globale naamruimte.

OPMERKING Gooit een ReferenceError als dit voorbeeld de functie CanvasRenderingContext2D.prototype.fillJustifyText niet kan vinden


Hoe te gebruiken

ctx.fillParaText(text, x, y, width, [settings]);
ctx.strokeParaText(text, x, y, width, [settings]);

Zie gerechtvaardigde tekst voor details over argumenten. Argumenten tussen [ en ] zijn optioneel.

Het argument settings heeft twee extra eigenschappen.

  • compact: standaard true . Als true probeert zoveel mogelijk woorden per regel in te pakken. Indien onwaar probeert het woordafstand zo dicht mogelijk bij de normale afstand te krijgen.
  • lineSpacing Standaard 1.5 . Ruimte per regel standaard 1.5 de afstand van online naar de volgende in termen van lettergrootte

Eigenschappen die ontbreken in het instellingenobject zullen standaard hun standaardwaarden of de laatste geldige waarden zijn. De eigenschappen worden alleen gewijzigd als de nieuwe waarden geldig zijn. Voor compact geldige waarden zijn alleen Booleans true of false Waarheden worden niet als geldig beschouwd.

Retourneer object

De twee functies retourneren een object met informatie om u te helpen de volgende alinea te plaatsen. Het object bevat de volgende eigenschappen.

  • nextLine Positie van de volgende regel na de alineapixels .
  • fontSize Grootte van het lettertype. (let op: gebruik alleen lettertypen die zijn gedefinieerd in pixels, bijvoorbeeld 14 14px arial )
  • lineHeight Afstand in pixels van de ene regel naar de volgende

In dit voorbeeld wordt een eenvoudig algoritme gebruikt dat één regel tegelijk werkt om de beste pasvorm voor een alinea te vinden. Dit betekent niet dat het het beste past (liever het beste van het algoritme). Misschien wilt u het algoritme verbeteren door een multi-pass lijnalgoritme over de gegenereerde lijnen te maken. Woorden verplaatsen van het einde van de ene regel naar het begin van de volgende, of van het begin terug naar het einde. De beste look wordt bereikt wanneer de afstand over de hele alinea de kleinste variatie heeft en het dichtst bij de normale tekstafstand ligt.

Omdat dit voorbeeld afhankelijk is van het voorbeeld van de rechtvaardigde tekst, lijkt de code erg op elkaar. Misschien wilt u de twee naar één functie verplaatsen. Vervang in het andere voorbeeld de functie justifiedTextSettings Tekstextensies door die in dit voorbeeld. Kopieer vervolgens de rest van de code uit dit voorbeeld naar de anonieme functie van het voorbeeld van de tekst Justified . U hoeft niet langer te testen op afhankelijkheden gevonden op // Code point A Het kan worden verwijderd.


Gebruiksvoorbeeld

ctx.font = "25px arial";
ctx.textAlign = "center"

var left = 10;
var center = canvas.width / 2;
var width = canvas.width-left*2;
var y = 20;
var size = 16;
var i = 0;
ctx.fillText("Justified paragraph examples.",center,y);
y+= 30;
ctx.font = "14px arial";
ctx.textAlign = "left"
// set para settings
var setting = {
    maxSpaceSize : 6,
    minSpaceSize : 0.5,
    lineSpacing : 1.2,
    compact : true,
}
// Show the left and right bounds.
ctx.strokeStyle = "red"
ctx.beginPath();
ctx.moveTo(left,y - size * 2);
ctx.lineTo(left, y + size * 15);
ctx.moveTo(canvas.width - left,y - size * 2);
ctx.lineTo(canvas.width - left, y + size * 15);
ctx.stroke();
ctx.textAlign = "left";
ctx.fillStyle = "black";

// Draw paragraph
var line = ctx.fillParaText(para, left, y, width, setting);  // settings is remembered    

// Next paragraph
y = line.nextLine + line.lineHeight;
setting.compact = false;
ctx.fillParaText(para, left, y, width, setting);

Opmerking: voor tekst left uitgelijnd of start de laatste regel van de alinea altijd een normale afstand hebben. Voor alle andere uitlijningen wordt de laatste regel behandeld als alle andere.

Opmerking: u kunt het begin van de alinea met spaties invoegen. Hoewel dit mogelijk niet consistent is van paragraaf tot paragraaf. Het is altijd goed om te weten wat een functie doet en deze te wijzigen. Een oefening zou zijn om een instelling toe te voegen aan de instellingen die de eerste regel inspringt met een vast bedrag. Hint dat de while-lus het eerste woord tijdelijk groter moet laten lijken (+ inspringen) words[0].width += ? en dan bij het renderen van lijnen de eerste regel inspringen.



Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow