Passende DOMs-Schriftart auf Leinwand?
Die einfache Antwort lautet: "Viel zu schwer !!" und "Es wird niemals perfekt sein."
Das Beste, was Sie tun können, ist eine Annäherung, die im Beispiel unten in der Antwort angegeben ist und die auch zeigt, dass die Übereinstimmung mit dem sichtbaren Stil nicht mit der sichtbaren Qualität zusammenhängt.
Erweiterung nur von CSS-Regeln.
Wenn Sie möchten, dass die Schriftart so genau wie möglich mit dem Element übereinstimmt, gibt es einige zusätzliche Bedenken, als nur das CSS zu erhalten, wie in der Antwort von Spark Fountain dargelegt .
Schriftgröße und CSS-Pixelgröße
- Die Schriftgröße hängt mit der CSS-Pixelgröße zusammen. Das HTMLCanvasElement
- Die CSS-Pixelgröße stimmt nicht immer mit den Anzeigepixeln des Geräts überein. ZB HiDPI / Retina-Displays. Sie können über auf die CSS-Pixelration des Geräts zugreifen
devicePixelRatio
- Die CSS-Pixelgröße ist keine Konstante und kann sich aus vielen Gründen ändern. Änderungen können über
MediaQueryListEvent
das change
Ereignis überwacht und abgehört werden
Elemente können transformiert werden. Das CanvasRenderingContext2D
kann keine 3D-Transformationen durchführen, daher ist das Element oder die Leinwand hat eine 3D-Transformation. Sie können die auf der Leinwand gerenderte Schriftart nicht mit der auf den Elementen gerenderten Schriftart abgleichen.
Die Leinwandauflösung und die Anzeigegröße sind unabhängig voneinander.
- Sie können die Leinwandauflösung über die Eigenschaften
HTMLCanvasElement.width
und erhaltenHTMLCanvasElement.height
- Sie können die Größe der Leinwandanzeige über die Stileigenschaften width und height oder über eine Vielzahl anderer Methoden abrufen (siehe Beispiel).
- Der Canvas-Pixelaspekt stimmt möglicherweise nicht mit dem CSS-Pixelaspekt überein und muss beim Rendern der Schriftart auf der Canvas berechnet werden.
- Das Rendern von Canvas-Schriftarten bei kleinen Schriftgrößen ist schrecklich. Beispielsweise ist eine 4px-Schriftart mit einer Größe von 16px nicht lesbar.
ctx.font = "4px arial"; ctx.scale(4,4); ctx.fillText("Hello pixels");
Sie sollten eine feste Schriftgröße verwenden, die gute Canvas-Rendering-Ergebnisse liefert, und das Rendering verkleinern, wenn Sie kleine Schriftarten verwenden.
Schriftfarbe
Der Farbstil der Elemente repräsentiert nur die gerenderte Farbe. Es repräsentiert nicht die tatsächliche Farbe, wie sie vom Benutzer gesehen wird.
Da dies sowohl für die Leinwand als auch für das Element gilt, von dem Sie die Farbe erhalten, sowie für alle Über- oder Unterlegelemente, ist der Arbeitsaufwand für die visuelle Anpassung der Farbe enorm und geht weit über den Rahmen einer Stapelüberlaufantwort hinaus (Antworten haben a max 30K Länge)
Schriftwiedergabe
Die Font-Rendering-Engine der Zeichenfläche unterscheidet sich von der des DOM. Das DOM kann eine Vielzahl von Rendering-Techniken verwenden, um die scheinbare Qualität der Schriftarten zu verbessern, indem es ausnutzt, wie die physischen RGB-Subpixel der Geräte angeordnet sind. Zum Beispiel TrueType- Schriftarten und zugehörige Hinweise, die vom Renderer verwendet werden, sowie das abgeleitete ClearType- Subpixel mit Hinweis-Rendering.
Diese Methoden zum Rendern von Schriftarten können auf der Leinwand abgeglichen werden. Für die Echtzeitübereinstimmung müssen Sie jedoch WebGL verwenden.
Das Problem ist, dass das Rendern von DOMs-Schriftarten von vielen Faktoren bestimmt wird, einschließlich der Browsereinstellungen. JavaScript kann nicht auf die Informationen zugreifen, die zum Ermitteln der Schriftart erforderlich sind. Bestenfalls können Sie eine fundierte Vermutung anstellen.
Weitere Komplikationen
Es gibt auch andere Faktoren, die sich auf die Schriftart auswirken und wie sich die Regeln für den CSS-Schriftstil auf das visuelle Ergebnis der angezeigten Schriftart beziehen. Zum Beispiel CSS-Einheiten, Animation, Ausrichtung, Richtung, Schrifttransformationen und Mackenmodus.
Persönlich für Rendering und Farbe störe ich nicht. Wenn ich eine vollständige Schriftarten-Engine mit WebGL geschrieben habe, die zu jeder Schriftart-, Filter-, Compositing- und Rendering-Variante passt, sind sie nicht Teil des Standards und können daher ohne vorherige Ankündigung geändert werden. Das Projekt wäre somit immer offen und könnte jederzeit zu unlesbaren Ergebnissen führen. Nur die Mühe nicht wert.
Beispiel
Das Beispiel hat links eine Renderfläche. Die Text- und Schriftartmitte oben. Eine vergrößerte Ansicht rechts, die eine vergrößerte Ansicht der linken Leinwand zeigt
Der erste verwendete Stil ist der Seitenstandard. Die Canvas-Auflösung beträgt 300 x 150, ist jedoch auf 500 x 500 CSS-Pixel skaliert. Dies führt zu SEHR schlechter Leinwandqualität. Wenn Sie die Leinwandauflösung durchlaufen, wird angezeigt, wie sich die Leinwandauflösung auf die Qualität auswirkt.
Die Funktionen
drawText(text, x, y, fontCSS, sizeCSSpx, colorStyleCSS)
Zeichnet den Text mithilfe von CSS-Eigenschaftswerten. Skalieren Sie die Schriftart so nah wie möglich an die visuelle Größe und das Seitenverhältnis des DOM.
getFontStyle(element)
Gibt die erforderlichen Schriftstile als Objekt von zurück element
Verwendung der Benutzeroberfläche
KLICKEN Sie auf die mittlere Schriftart, um die Schriftstile zu wechseln.
KLICKEN Sie auf die linke Leinwand, um die Leinwandauflösungen zu wechseln.
Unten finden Sie die Einstellung, mit der der Text auf der Leinwand gerendert wird.
Sie werden sehen, dass die Qualität des Textes von der Auflösung der Leinwand abhängt.
Um zu sehen, wie sich der DOM-Zoom auf das Rendering auswirkt, müssen Sie die Seite vergrößern oder verkleinern. HiDPI- und Retina-Displays weisen eine weitaus geringere Leinwandqualität auf, da die Leinwand die Hälfte der Auflösung der CSS-Pixel aufweist.
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>