在HTML5 Canvas上放置文本在浏览器中不一致

时间:2016-09-07 21:36:59

标签: javascript html css html5 canvas

我将文本放在HTML5画布中。我将上下文textAlign值设置为center,textBaseline设置为" hanging"

我很困惑,因为safari和chrome以及webkit和文本放置都是不同的,即使被告知在同一地点。

Chrome看起来正确: chrome example

和Firefox一样 firefox example

然而safari将文本放低(相同的代码) safari example

任何想法safari如何计算不同的位置,以便我可以为它建立一个例外?

1 个答案:

答案 0 :(得分:1)

适合的文字是一种痛苦。这是一种非标准的方法,使文本几乎像素完美。 (宽度可能会进入内部请求一些,一些字体只是在那里测量)。

此方法以大尺寸“100px”呈现文本,然后测量其各个部分。你可以适应一个矩形,你可以在演示中看到四个选项。 T,H和类似的字母往往比O,G和圆形字母略短,你可以选择保持圆形内部与否,你可以忽略尾部'y,j,g'或将它们保持在里面。 / p>

测量功能会多次渲染文本以​​找到各个部分,如果您想要更好的样本或特定字体,则可以重叠多个字母。

此解决方案适合所有具有良好画布支持的浏览器。

对于命名很抱歉但这个特殊问题让我参与其中,我们在其他所有浏览器API中都获得了很好的文字,所以为什么不用画布????? (SVG阵营LOL的秘密阴谋;)我认为

两个功能。首先测量文本,然后返回包含相关信息的对象,第二个渲染文本以​​适合矩形。请参阅底部的代码如何使用它并设置控制悬挂尾部和圆形位的两个标志。

var canvas = document.createElement("canvas");
canvas.width = 800;
canvas.height = 800;
var ctx = canvas.getContext("2d");
document.body.appendChild(canvas);


function superWTFCanvasTextMessure(font){ // just the font name please.....
    var c = document.createElement("canvas"); // make canvas
    var ctx1 = c.getContext("2d"); // get the thing that draw the stuff
    ctx1.font = "100px "+font; // big font
    ctx1.textAlign = "center";  // put it in the middle
    ctx1.textBaseline = "middle";
    // draw text nice and solid...
    ctx1.fillText("lp",Math.floor(c.width/2),Math.floor(c.height/2));
    ctx1.fillText("lp",Math.floor(c.width/2),Math.floor(c.height/2));
    ctx1.fillText("lp",Math.floor(c.width/2),Math.floor(c.height/2));
    ctx1.fillText("lq",Math.floor(c.width/2),Math.floor(c.height/2));
    ctx1.fillText("lg",Math.floor(c.width/2),Math.floor(c.height/2));
    ctx1.fillText("lj",Math.floor(c.width/2),Math.floor(c.height/2));
    // get the pixels as words
    
    var data= new Uint32Array(ctx1.getImageData(0,0,canvas.width,canvas.height).data.buffer);
    var top = 0;
    while(data[top++] === 0);  // find the first pixel on from the top;
    var tail = data.length - 1;
    while(data[tail--] === 0); // find the first pixel on from the bottom;
     
    ctx1.clearRect(0,0,canvas.width,canvas.height); // clear up the mess
    // and now for the Base  draw text nice and solid...
    ctx1.fillText("T",Math.floor(c.width/2),Math.floor(c.height/2));
    ctx1.fillText("T",Math.floor(c.width/2),Math.floor(c.height/2));
    ctx1.fillText("T",Math.floor(c.width/2),Math.floor(c.height/2));
    ctx1.fillText("T",Math.floor(c.width/2),Math.floor(c.height/2));
    ctx1.fillText("T",Math.floor(c.width/2),Math.floor(c.height/2));
    // get the pixels as words
    data= new Uint32Array(ctx1.getImageData(0,0,canvas.width,canvas.height).data.buffer);
    var bum = data.length - 1;
    while(data[bum--] === 0); // find the first pixel on from the bottom;

    // and the round bits the poke out in all the wrong places.
    ctx1.clearRect(0,0,canvas.width,canvas.height); // clear up the mess
    // and now for the Base  draw text nice and solid...
    ctx1.fillText("O",Math.floor(c.width/2),Math.floor(c.height/2));
    ctx1.fillText("J",Math.floor(c.width/2),Math.floor(c.height/2));
    ctx1.fillText("?",Math.floor(c.width/2),Math.floor(c.height/2));
    ctx1.fillText("G",Math.floor(c.width/2),Math.floor(c.height/2));
    ctx1.fillText("O",Math.floor(c.width/2),Math.floor(c.height/2));
    // get the pixels as words
    data= new Uint32Array(ctx1.getImageData(0,0,canvas.width,canvas.height).data.buffer);
    var head = 0;
    while(data[head++] === 0); // find the first pixel on from the bottom;
    var theOtherBit = data.length - 1;
    while(data[theOtherBit--] === 0); // find the first pixel on from the bottom;

    
    return {
        body: Math.floor(bum / canvas.width) - Math.floor(top / canvas.width)+1,
        all : Math.floor(tail / canvas.width) - Math.floor(top / canvas.width)+1,
        offset : Math.floor(c.height/2) - Math.floor(top / canvas.width),
        t2A : Math.floor(theOtherBit / canvas.width) - Math.floor(head / canvas.width)+1,
        t2t : Math.floor(tail / canvas.width) - Math.floor(head / canvas.width)+1,
        offsetHead : Math.floor(c.height/2) - Math.floor(head / canvas.width),
        font : ctx1.font,
    };
}

function drawPixelPerfectTextTheHardWay(text,left,top,width,height,sWTFDesc){
    var sy,offy;
    ctx.font = sWTFDesc.font; // set up the same font as measured. (dont worry it will be scaled and can be any size but if not measure at 100 px this will not work as well)
    ctx.textAlign = "center";
    ctx.textBaseline = "middle";
    var w = ctx.measureText(text).width;  // get the width
    if(sWTFDesc.bumsDown){
        if(sWTFDesc.extrasIn){
            sy =  height/ sWTFDesc.t2A;   // get the height scale
        }else{
            sy =  height/ sWTFDesc.body;   // get the height scale
        }
    }else{
        if(sWTFDesc.extrasIn){
            sy =  height/ sWTFDesc.t2t;   // get the height scale
        }else{
            sy =  height/ sWTFDesc.all;   // get the height scale
        }
        
    }
    var sx = width / w; // get the x scale
    if(sWTFDesc.extrasIn){
        offy = sWTFDesc.offset * sy; // get top offset
    }else{
        offy = sWTFDesc.offset * sy; // get the correct offset
    }
    // set up the tranform
    ctx.setTransform(sx,0,0,sy,left + width / 2, top + offy);
    ctx.fillText(text,0,0);
    ctx.setTransform(1,0,0,1,0,0);  // reset the tranform to the default..
    // all diddly done..
}


ctx.clearRect(0,0,canvas.width,canvas.height)
var superFontDesc = superWTFCanvasTextMessure("arial");


ctx.strokeStyle = "black";
ctx.fillStyle = "red";
ctx.strokeRect(10,200,700,140);
drawPixelPerfectTextTheHardWay("mind p & q's ? j,g",10,200,700,140,superFontDesc);

ctx.fillStyle = "green";
ctx.strokeRect(20,400,700,120);
superFontDesc.bumsDown = true;
drawPixelPerfectTextTheHardWay("test this Jimmy!!",20,400,700,120,superFontDesc);

ctx.fillStyle = "blue";
ctx.strokeRect(20,20,700,140);
superFontDesc.bumsDown = true;
superFontDesc.extrasIn= true;

drawPixelPerfectTextTheHardWay("test this Jimmy!!",20,20,700,140,superFontDesc);

ctx.fillStyle = "#a50";
ctx.strokeRect(20,570,700,140);
superFontDesc.bumsDown = false;
superFontDesc.extrasIn= true;

drawPixelPerfectTextTheHardWay("????&GGhjqgy",20,570,700,140,superFontDesc);
ctx.font = "20px arial";
ctx.textAlign = "left";
ctx.fillStyle = "black";
ctx.fillText("Round bits in and tails hanging.",10,174);
ctx.fillText("Round bits out and tails in.",10,354);
ctx.fillText("Round bits out and tails hanging.",10,540);
ctx.fillText("Round bits out and tails in.",10,724);