简体   繁体   中英

Why the text in canvas can't be centered vertically

    CanvasRenderingContext2D.prototype.roundRect = function (
        x,
        y,
        width,
        height,
        radius
    ) {
        if (width < 2 * radius) radius = width / 2;
        if (height < 2 * radius) radius = height / 2;
        this.beginPath();
        this.moveTo(x + radius, y);
        this.arcTo(x + width, y, x + width, y + height, radius);
        this.arcTo(x + width, y + height, x, y + height, radius);
        this.arcTo(x, y + height, x, y, radius);
        this.arcTo(x, y, x + width, y, radius);
        this.closePath();
        return this;
    };

let generateCanvas = function (config) {
        var tmpCanvas = document.createElement("canvas");
        var tmpContext = tmpCanvas.getContext("2d");
        tmpContext.font = tmpContext.font.replace(/\d+px/, config.fontSize);
        var textWidth = tmpContext.measureText(config.text).width || 10;
        tmpCanvas = null;
        var textCanvas = document.createElement("canvas");
        var textContext = textCanvas.getContext("2d");
        textCanvas.height = config.height;
        textCanvas.width = textWidth;
        textCanvas.width = textWidth + 2 * config.padding;
        textContext.lineWidth = config.lineWidth;
        textContext.strokeStyle = config.lineColor;
        textContext.fillStyle = config.backgroundColor;
        textContext.roundRect(
                0,
                0,
                textCanvas.width,
                textCanvas.height,
                config.rounded
            );
            textContext.stroke();
            textContext.fill();
        textContext.fillStyle = config.color;
        textContext.font = textContext.font.replace(/\d+px/, config.fontSize);
        textContext.textAlign = config.textAlign;
        textContext.textBaseline = config.textBaseline;
        textContext.fillText(
            config.text,
            textCanvas.width / 2,
            config.height / 2
        );
        return textCanvas;
    };

The config I passed to the generateCanvas function is:

{
                            text: "测试名称测试名称测试名称测试名称",
                            height: 40,
                            fontSize: "24px",
                            textAlign: "center",
                            textBaseline: "middle",
                            color: "#F9A403",
                            backgroundColor: "white",
                            lineColor: "transparent",
                            rounded: 25,
                            padding: 10,
                        }

Why is the text in the image generated by the above function not centered vertically? roundRect is a custom function that implements drawing a rectangle with rounded corners using canvas.Please help me if there are any gods to look at this problem.

The "middle" value of textBaseline is based on the em-square . This means it will work well for latin glyphs, but not so well for ideographic glyphs, which would need an ideographic centered baseline value, that the Canvas API doesn't have, yet.

Here is a graphic representation from the specs :

em 方格的顶部大致位于字体中字形的顶部,悬挂基线是一些像 आ 这样的字形锚定的位置,中间是 em 方格顶部和 em 方格底部之间的中间位置, 字母基线是 Á、ÿ、f 和 Ω 等字符的锚定位置,表意下基线是诸如私和达之类的字形锚定的位置,而 em 正方形的底部大致位于一种字体。由于字形延伸到 em 方格之外,边界框的顶部和底部可能远离这些基线。

But all hope is not lost, during the last years, all browsers have made some progress in their implementations of the TextMetrics interface, and we finally have access to enough information from all major browsers to measure the height of the text being measured.
This means that we can implement a truly centered vertical-align, based on the glyphs being rendered:

 const text_input = document.querySelector("input[type='text']"); const padding_input = document.querySelector("input[type='range']"); const canvas = document.querySelector("canvas"); const ctx = canvas.getContext("2d");; const font = "60px sans-serif"; let padding_x = 10; let padding_y = 10; text_input.oninput = padding_input.oninput = (evt) => drawText(text_input.value); drawText(text_input.value); // returns an easier to use BBox like object // from a TextMetrics object function getTextBBox( ctx, text ) { const metrics = ctx.measureText( text ); const left = metrics.actualBoundingBoxLeft * -1; const top = metrics.actualBoundingBoxAscent * -1; const right = metrics.actualBoundingBoxRight; const bottom = metrics.actualBoundingBoxDescent; const width = right - left; const height = bottom - top; return { left, top, right, bottom, width, height }; } function drawText( text ) { // we set only the font // other values like textBaseline and textAlign // are left to their default ctx.font = font; const bbox = getTextBBox(ctx, text); const padding = +padding_input.value; canvas.width = bbox.width + padding; canvas.height = bbox.height + padding; ctx.font = font; ctx.fillStyle = "#F9A403"; const middle_x = bbox.left + (bbox.width / 2); const middle_y = bbox.top + (bbox.height / 2); const left = canvas.width / 2 - middle_x; const top = canvas.height / 2 - middle_y; ctx.fillText(text, left, top); // draw the middle line for reference ctx.fillStyle = "red"; ctx.fillRect(0, canvas.height/2-1, canvas.width, 2); }
 canvas { border: 1px solid; }
 <label>Text content: <input type="text" value="测试名称测试名称测试名称测试名称"></label><br> <label>Padding: <input type="range" min="0" max="100"></label><br> <canvas></canvas>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM