Fonte DOMs correspondente na tela?
Resposta simples é: "Muito difícil !!" e "Isso nunca será perfeito".
O melhor que você pode fazer é uma aproximação, que está no exemplo na parte inferior da resposta, que também mostrará que a correspondência do estilo visível não está relacionada à qualidade visível.
Estendendo-se apenas de regras CSS.
Se você deseja que a fonte corresponda o mais próximo possível do elemento, existem algumas preocupações adicionais, além de obter o CSS, conforme indicado na resposta da Spark Fountain .
Tamanho da fonte e tamanho dos pixels CSS
- O tamanho da fonte está relacionado ao tamanho do pixel CSS. O HTMLCanvasElement
- O tamanho do pixel CSS nem sempre corresponde aos pixels de exibição do dispositivo. Por exemplo, displays HiDPI / Retina. Você pode acessar a proporção de pixels em CSS do dispositivo via
devicePixelRatio
- O tamanho do pixel CSS não é constante e pode ser alterado por vários motivos. As alterações podem ser monitoradas via
MediaQueryListEvent
e ouvindo o change
evento
Elementos podem ser transformados. oCanvasRenderingContext2D
não é possível fazer transformações em 3D, é o elemento ou a tela tem uma transformação em 3D. Você não poderá corresponder a fonte renderizada da tela com a fonte renderizada dos elementos.
A resolução da tela e o tamanho da tela são independentes.
- Você pode obter a resolução da tela através das propriedades
HTMLCanvasElement.width
eHTMLCanvasElement.height
- Você pode obter o tamanho da tela de exibição através das propriedades de estilo width e height, ou através de uma variedade de outros métodos, veja o exemplo.
- O aspecto de pixel da tela pode não corresponder ao aspecto do pixel CSS e deve ser calculado ao renderizar a fonte na tela.
- A renderização de fontes Canvas em tamanhos pequenos é terrível. Por exemplo, uma fonte 4px renderizada com 16px de tamanho é ilegível.
ctx.font = "4px arial"; ctx.scale(4,4); ctx.fillText("Hello pixels");
Você deve usar um tamanho de fonte fixo que tenha resultados de renderização de tela de boa qualidade e reduza a renderização ao usar fontes pequenas.
Cor da fonte
O estilo de cor dos elementos representa apenas a cor renderizada. Não representa a cor real como vista pelo usuário.
Como isso se aplica à tela e ao elemento do qual você está obtendo a cor e a todos os elementos de sobre ou abaixo da camada, a quantidade de trabalho necessária para combinar visualmente a cor é enorme e está muito além do escopo de uma resposta de estouro de pilha (as respostas têm uma comprimento máximo de 30K)
Renderização de fontes
O mecanismo de renderização da fonte da tela é diferente do do DOM. O DOM pode usar uma variedade de técnicas de renderização para melhorar a qualidade aparente das fontes, aproveitando a forma como os sub-pixels RGB físicos dos dispositivos são organizados. Por exemplo, fontes TrueType e dicas relacionadas usadas pelo renderizador e as definições derivadas do ClearType sub pixel secundário com renderização de dicas.
Esses métodos de renderização de fonte PODEM ser combinados na tela , embora, para correspondência em tempo real, você precise usar o WebGL.
O problema é que a renderização da fonte DOMs é determinada por vários fatores, incluindo as configurações do navegador. O JavaScript não pode acessar nenhuma das informações necessárias para determinar como a fonte é renderizada. Na melhor das hipóteses, você pode adivinhar.
Complicações adicionais
Também existem outros fatores que afetam a fonte e como as regras de estilo de fonte CSS se relacionam com o resultado visual da fonte exibida. Por exemplo, unidades CSS, modo de animação, alinhamento, direção, transformações de fonte e peculiaridades.
Pessoalmente, para renderização e cores, não me incomodo. Evento, se eu escrevi um mecanismo de fonte completo usando o WebGL para corresponder a todas as variantes de fonte, filtragem, composição e renderização, elas não fazem parte do padrão e, portanto, estão sujeitas a alterações sem aviso prévio. O projeto seria, portanto, sempre aberto e poderia a qualquer momento falhar no nível de resultados ilegíveis. Só não vale o esforço.
Exemplo
O exemplo tem uma tela de renderização à esquerda. A parte superior central do texto e da fonte. Uma visualização ampliada à direita, que mostra uma visualização ampliada da tela da tela esquerda
O primeiro estilo usado é o padrão das páginas. A resolução da tela é 300by150, mas dimensionada para caber 500 a 500 pixels em CSS. Isso resulta em um texto de tela com MUITO baixa qualidade. O ciclo da resolução da tela mostrará como a tela afeta a qualidade.
As funções
drawText(text, x, y, fontCSS, sizeCSSpx, colorStyleCSS)
desenha o texto usando valores de propriedade CSS. Dimensionar a fonte para corresponder ao tamanho visual do DOM e à proporção, o mais próximo possível.
getFontStyle(element)
retorna os estilos de fonte necessários como um objeto de element
Uso da interface do usuário
CLIQUE na fonte central para alternar entre estilos de fonte.
Clique em tela esquerda para alternar entre as resoluções da tela.
Na parte inferior, está a configuração usada para renderizar o texto na tela.
Você verá que a qualidade do texto depende da resolução da tela.
Para ver como o zoom DOM afeta a renderização, você deve ampliar ou reduzir a página. Os monitores HiDPI e retina terão um texto de tela de qualidade muito mais baixa devido ao fato de a tela ter metade dos res dos pixels CSS.
const ZOOM_SIZE = 16;
canvas1.width = ZOOM_SIZE;
canvas1.height = ZOOM_SIZE;
const ctx = canvas.getContext("2d");
const ctx1 = canvas1.getContext("2d");
const mouse = {x:0, y:0};
const CANVAS_FONT_BASE_SIZE = 32; // the size used to render the canvas font.
const TEXT_ROWS = 12;
var currentFontClass = 0;
const fontClasses = "fontA,fontB,fontC,fontD".split(",");
const canvasResolutions = [[canvas.scrollWidth, canvas.scrollHeight],[300,150],[200,600],[600,600],[1200,1200],[canvas.scrollWidth * devicePixelRatio, canvas.scrollHeight * devicePixelRatio]];
var currentCanvasRes = canvasResolutions.length - 1;
var updateText = true;
var updating = false;
setTimeout(updateDisplay, 0, true);
function drawText(text, x, y, fontCSS, sizeCSSpx, colorStyleCSS) { // Using px as the CSS size unit
ctx.save();
// Set canvas state to default
ctx.globalAlpha = 1;
ctx.filter = "none";
ctx.globalCompositeOperation = "source-over";
const pxSize = Number(sizeCSSpx.toString().trim().replace(/[a-z]/gi,"")) * devicePixelRatio;
const canvasDisplayWidthCSSpx = ctx.canvas.scrollWidth; // these are integers
const canvasDisplayHeightCSSpx = ctx.canvas.scrollHeight;
const canvasResWidth = ctx.canvas.width;
const canvasResHeight = ctx.canvas.height;
const scaleX = canvasResWidth / (canvasDisplayWidthCSSpx * devicePixelRatio);
const scaleY = canvasResHeight / (canvasDisplayHeightCSSpx * devicePixelRatio);
const fontScale = pxSize / CANVAS_FONT_BASE_SIZE
ctx.setTransform(scaleX * fontScale, 0, 0, scaleY * fontScale, x, y); // scale and position rendering
ctx.font = CANVAS_FONT_BASE_SIZE + "px " + fontCSS;
ctx.textBaseline = "hanging";
ctx.fillStyle = colorStyleCSS;
ctx.fillText(text, 0, 0);
ctx.restore();
}
function getFontStyle(element) {
const style = getComputedStyle(element);
const color = style.color;
const family = style.fontFamily;
const size = style.fontSize;
styleView.textContent = `Family: ${family} Size: ${size} Color: ${color} Canvas Resolution: ${canvas.width}px by ${canvas.height}px Canvas CSS size 500px by 500px CSS pixel: ${devicePixelRatio} to 1 device pixels`
return {color, family, size};
}
function drawZoomView(x, y) {
ctx1.clearRect(0, 0, ctx1.canvas.width, ctx1.canvas.height);
//x -= ZOOM_SIZE / 2;
//y -= ZOOM_SIZE / 2;
const canvasDisplayWidthCSSpx = ctx.canvas.scrollWidth; // these are integers
const canvasDisplayHeightCSSpx = ctx.canvas.scrollHeight;
const canvasResWidth = ctx.canvas.width;
const canvasResHeight = ctx.canvas.height;
const scaleX = canvasResWidth / (canvasDisplayWidthCSSpx * devicePixelRatio);
const scaleY = canvasResHeight / (canvasDisplayHeightCSSpx * devicePixelRatio);
x *= scaleX;
y *= scaleY;
x -= ZOOM_SIZE / 2;
y -= ZOOM_SIZE / 2;
ctx1.drawImage(ctx.canvas, -x, -y);
}
displayFont.addEventListener("click", changeFontClass);
function changeFontClass() {
currentFontClass ++;
myFontText.className = fontClasses[currentFontClass % fontClasses.length];
updateDisplay(true);
}
canvas.addEventListener("click", changeCanvasRes);
function changeCanvasRes() {
currentCanvasRes ++;
if (devicePixelRatio === 1 && currentCanvasRes === canvasResolutions.length - 1) {
currentCanvasRes ++;
}
updateDisplay(true);
}
addEventListener("mousemove", mouseEvent);
function mouseEvent(event) {
const bounds = canvas.getBoundingClientRect();
mouse.x = event.pageX - scrollX - bounds.left;
mouse.y = event.pageY - scrollY - bounds.top;
updateDisplay();
}
function updateDisplay(andRender = false) {
if(updating === false) {
updating = true;
requestAnimationFrame(render);
}
updateText = andRender;
}
function drawTextExamples(text, textStyle) {
var i = TEXT_ROWS;
const yStep = ctx.canvas.height / (i + 2);
while (i--) {
drawText(text, 20, 4 + i * yStep, textStyle.family, textStyle.size, textStyle.color);
}
}
function render() {
updating = false;
const res = canvasResolutions[currentCanvasRes % canvasResolutions.length];
if (res[0] !== canvas.width || res[1] !== canvas.height) {
canvas.width = res[0];
canvas.height = res[1];
updateText = true;
}
if (updateText) {
ctx.setTransform(1,0,0,1,0,0);
ctx.clearRect(0,0,ctx.canvas.width, ctx.canvas.height);
updateText = false;
const textStyle = getFontStyle(myFontText);
const text = myFontText.textContent;
drawTextExamples(text, textStyle);
}
drawZoomView(mouse.x, mouse.y)
}
.fontContainer {
position: absolute;
top: 8px;
left: 35%;
background: white;
border: 1px solid black;
width: 30%;
cursor: pointer;
text-align: center;
}
#styleView {
}
.fontA {}
.fontB {
font-family: arial;
font-size: 12px;
color: #F008;
}
.fontC {
font-family: cursive;
font-size: 32px;
color: #0808;
}
.fontD {
font-family: monospace;
font-size: 26px;
color: #000;
}
.layout {
display: flex;
width: 100%;
height: 128px;
}
#container {
border: 1px solid black;
width: 49%;
height: 100%;
overflow-y: scroll;
}
#container canvas {
width: 500px;
height: 500px;
}
#magViewContainer {
border: 1px solid black;
display: flex;
width: 49%;
height: 100%;
}
#magViewContainer canvas {
width: 100%;
height: 100%;
image-rendering: pixelated;
}
<div class="fontContainer" id="displayFont">
<span class="fontA" id="myFontText" title="Click to cycle font styles">Hello Pixels</span>
</div>
<div class="layout">
<div id="container">
<canvas id="canvas" title="Click to cycle canvas resolution"></canvas>
</div>
<div id="magViewContainer">
<canvas id="canvas1"></canvas>
</div>
</div>
<code id="styleView"></code>