2016-09-07 4 views
0

HTML5 캔버스에 텍스트를 배치하고 있습니다. 컨텍스트 textAlign 값을 가운데로 설정하고 textBaseline을 "매달 기"로 설정합니다.브라우저간에 일치하지 않는 HTML5 캔버스에 텍스트 배치

사파리와 크롬, 웹킷 및 텍스트 배치가 모두 동일하기 때문에 혼란 스럽기 때문에 혼란 스럽습니다.

크롬은 올바른 같습니다 파이어 폭스에게 firefox example

그러나 사파리 장소 텍스트 낮은 (같은 코드)를 수행 chrome example

safari example

사파리가 내가 할 수 있도록 다른 위치를 계산하는 방법 어떤 생각 그것을 위해 예외를 만드시겠습니까?

+0

사파리의 어떤 버전? 실제 코드가 없으면 도움을 받기가 정말 어렵습니다. – Dekel

+0

관련 항목 : http://stackoverflow.com/questions/35407614/html5-canvas-textbaseline-top-looks-different-in-firefox-and-chrome. 그래서 크롬은 그것을 고정시키고 FF와 스펙을 조정할 것이지만 다른 것들은 아직 완성하지 않을 것입니다. – Kaiido

답변

1

맞는 텍스트가 아픔입니다. 텍스트를 거의 완벽한 픽셀로 만들 수있는 표준 방법이 없습니다. (너비가 약간 넓어 질 수도 있고, 약간의 글꼴만으로도 측정 할 수 있습니다).

이 메서드는 텍스트를 큰 크기 "100px"로 렌더링 한 다음 다양한 부분을 측정합니다. 데모에서 볼 수있는 네 가지 옵션을 사용하여 직사각형에 맞출 수 있습니다. T, H와 O, G, 둥근 글자보다 약간 짧은 경향이있는 편지는 둥근 비트를 안쪽에 두거나 붙이지 않도록 선택할 수 있습니다. 꼬리 'y, j, g'를 무시하거나 안에 넣을 수 있습니다.

측정 기능은 여러 부분을 찾기 위해 텍스트를 여러 번 렌더링합니다. 더 나은 샘플을 원하거나 특정 글꼴이 아프다면 여러 글자를 겹칠 수 있습니다.

이 솔루션은 훌륭한 캔버스를 지원하는 모든 브라우저에 적합합니다.

이름 지정에 대해 불편을 끼쳐 드려 죄송합니다.하지만이 특별한 문제는 제가 다른 모든 브라우저 API에서 훌륭한 텍스트를 얻습니다. 왜 canvas ????? (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);

+0

모든 것을 읽지는 않았지만'textBaseline = "middle"'을 사용하는 이유는 무엇입니까? 모든 UA가 일치하는 것은 영문자입니까? http://i.stack.imgur.com/mygrV.png – Kaiido

+0

필자는 어떤 기준선을 사용할 수 있었는지, 왜 중간을 사용 했는가는 모든 텍스트가 임시 캔버스에 들어갈 수 있다는 것을 아는 것이 더 간단하다는 것입니다. 꼬리가 많이 매달 리니 나는 그것에 너무 많은 노력을 기울이기를 원하지 않았다. 게다가 목표는 사각형 내부에 맞추는 것입니다. 텍스트 크기가 기본 선이 무엇이든 관계없이 동일하다고 가정하고 더 높은 정밀도가 필요한 경우 텍스트를 200px 또는 400px로 측정 할 수 있습니다 (참고로 1pix kludge factor Chrome Canary 55 추가). 더 많은 글자를 샘플링했습니다 (내부 상단에 기준선을 위해 T 만 사용). – Blindman67

+0

이것은 훌륭한 JS 솔루션입니다. 시험해 봐야 할 것입니다. 결국 나는 AJAX를 호출하여 Imagemagick 스크립트를 호출하여 텍스트를 만든 다음이를 캔버스에 base64 이미지로 반환합니다. – dprogramz

관련 문제