user5474476
user5474476

Reputation: 141

html canvas draw font exceeds the expected space, why and how to solve?

fontsize = 50px

pink line: y in

ctx.fillText(text, x, y);

blue line: y - 50

khaki line: y + 50

pic1: with

ctx.textBaseline = "bottom";

enter image description here

the letter's top exceeds positionY - fontsize

pic2:with

ctx.textBaseline = "top";

enter image description here

the letter's bottom exceeds positionY + fontsize

It's strange.

Why and how to solve this problem?

My case is I need to blur the text, but Safari don't support ctx.filter

So I write a customBlur filter using getImageData & putImageData.

But when use it to blur the text, I encountered this problem:

I can't get the right y-axis position of my text.

Tried hanging/ideographic/middle/alphabetic, they all get worse result.

Thank you, I didn't know there was a useful code snippet support. I made the code snippet. As follows, looks like it depends on the font.

When I use Verdana, English words works well but Chinese characters and some symbol didn't.

When I switch to PingFang SC, only Chinese characters in hanging mode made it to be in a predictable space.

ctx.font = "50px Verdana, PingFang SC" works the best with bottom mode.

let radios = Array.from(document.querySelectorAll("[type='radio']"))
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
let cx = canvas.width/2;
let cy = canvas.height/2;
ctx.font="50px Verdana, PingFang SC";
ctx.fillStyle = "blue";
ctx.textAlign="center";

ctx.textBaseline="top";
//ctx.textBaseline="middle";
//ctx.textBaseline="bottom";
//ctx.textBaseline="alphabetic";
//ctx.textBaseline="hanging";

radios.forEach(r=>{
  r.addEventListener("input",()=>{
    if(r.checked){
      ctx.textBaseline=r.value;
      ctx.clearRect(0, 0, canvas.width,canvas.height);
      draw()
    }
  })
})

draw();

function draw(){
ctx.fillText("abpf-+=|{}[]中文",cx,cy);
ctx.strokeStyle="red";
ctx.moveTo(5,cy);
ctx.lineTo(445,cy);
ctx.stroke();
ctx.moveTo(5,cy - 50);
ctx.lineTo(445,cy - 50);
ctx.stroke();
ctx.moveTo(5,cy + 50);
ctx.lineTo(445,cy + 50);
ctx.stroke();
}
canvas{border:1px solid;}
<canvas width="450" height="150" id="canvas"> :( </canvas>
<p>
  <label>top: <input name="textBaseline" type="radio" value="top" checked /></label><br>
  <label>middle: <input name="textBaseline" type="radio" value="middle" /></label><br>
  <label>bottom: <input name="textBaseline" type="radio" value="bottom" /></label><br>
  <label>alphabetic: <input name="textBaseline" type="radio" value="alphabetic" /></label><br>
  <label>hanging: <input name="textBaseline" type="radio" value="hanging" /></label>
</p>

Upvotes: 1

Views: 122

Answers (1)

enxaneta
enxaneta

Reputation: 33024

I doubt that this will solve your problem. However: it's always helpful to make yourself a little demo like this.

let radios = Array.from(document.querySelectorAll("[type='radio']"))
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
let cx = canvas.width/2;
let cy = canvas.height/2;
ctx.font="50px Verdana";
ctx.fillStyle = "blue";
ctx.textAlign="center";

ctx.textBaseline="top";
//ctx.textBaseline="middle";
//ctx.textBaseline="bottom";
//ctx.textBaseline="alphabetic";
//ctx.textBaseline="hanging";

radios.forEach(r=>{
  r.addEventListener("input",()=>{
    if(r.checked){
      ctx.textBaseline=r.value;
      ctx.clearRect(0, 0, canvas.width,canvas.height);
      draw()
    }
  })
})

draw();

function draw(){
ctx.fillText("abpf",cx,cy);
ctx.strokeStyle="red";
ctx.moveTo(5,cy);
ctx.lineTo(245,cy);
ctx.stroke();
}
canvas{border:1px solid;}
<canvas width="250" height="150" id="canvas"> :( </canvas>
<p>
  <label>top: <input name="textBaseline" type="radio" value="top" checked /></label><br>
  <label>middle: <input name="textBaseline" type="radio" value="middle" /></label><br>
  <label>bottom: <input name="textBaseline" type="radio" value="bottom" /></label><br>
  <label>alphabetic: <input name="textBaseline" type="radio" value="alphabetic" /></label><br>
  <label>hanging: <input name="textBaseline" type="radio" value="hanging" /></label>
</p>

Upvotes: 1

Related Questions