Se vuoi semplicemente rendere il testo dal tuo span in una tela, puoi accedere agli attributi di stile usando la funzione window.getComputedStyle. Per rendere invisibile lo span originale, imposta il suo stile su display: none
.
// get the span element
const span = document.getElementsByClassName('bmcl_evalprompt')[0];
// get the relevant style properties
const font = window.getComputedStyle(span).font;
const color = window.getComputedStyle(span).color;
// get the element's text (if necessary)
const text = span.innerHTML;
// get the canvas element
const canvas = document.getElementById('canvas');
// set the canvas styling
const ctx = canvas.getContext('2d');
ctx.font = font;
ctx.fillStyle = color;
// print the span's content with correct styling
ctx.fillText(text, 35, 110);
#canvas {
width: 300px;
height: 200px;
background: lightgrey;
}
span.bmcl_evalprompt {
display: none; // makes the span invisible
font-family: monospace; // change this value to see the difference
font-size: 32px; // change this value to see the difference
color: rebeccapurple; // change this value to see the difference
}
<span class="bmcl_evalprompt">Hello World!</span>
<canvas id="canvas" width="300" height="200"></canvas>
Carattere DOM corrispondente nell'area di disegno?
La risposta semplice è "Troppo difficile!!" e "Non sarà mai perfetto."
Il meglio che puoi fare è un'approssimazione che è nell'esempio in fondo alla risposta che mostrerà anche che la corrispondenza dello stile visibile non è correlata alla qualità visibile.
Estensione delle sole regole CSS.
Se vuoi che il carattere corrisponda il più possibile all'elemento, ci sono alcune preoccupazioni aggiuntive rispetto al semplice ottenere il CSS come sottolineato nella risposta di Spark Fountain.
Dimensione del carattere e dimensione in pixel CSS
-
La dimensione del carattere è correlata alla dimensione dei pixel CSS. HTMLCanvasElement
-
La dimensione dei pixel CSS non sempre corrisponde ai pixel del display del dispositivo. Ad esempio display HiDPI/Retina. Puoi accedere alla razione di pixel CSS del dispositivo tramite
devicePixelRatio
-
La dimensione dei pixel CSS non è una costante e può cambiare per molte ragioni. Le modifiche possono essere monitorate tramite
MediaQueryListEvent
e ascoltando ilchange
evento -
Gli elementi possono essere trasformati. Il
CanvasRenderingContext2D
non è possibile eseguire trasformazioni 3D quindi è l'elemento o la tela ha una trasformazione 3D non sarai in grado di abbinare il carattere di rendering della tela con il carattere di rendering degli elementi. -
La risoluzione della tela e le dimensioni del display sono indipendenti.
- Puoi ottenere la risoluzione della tela tramite le proprietà
HTMLCanvasElement.width
eHTMLCanvasElement.height
- Puoi ottenere le dimensioni di visualizzazione della tela tramite le proprietà di stile larghezza e altezza o tramite una varietà di altri metodi vedi esempio.
- L'aspetto pixel della tela potrebbe non corrispondere all'aspetto pixel CSS e deve essere calcolato durante il rendering del carattere sulla tela.
- Il rendering dei caratteri Canvas con caratteri di piccole dimensioni è terribile. Ad esempio, un font 4px reso per avere una dimensione di 16px è illeggibile.
ctx.font = "4px arial"; ctx.scale(4,4); ctx.fillText("Hello pixels");
Dovresti utilizzare una dimensione del carattere fissa che abbia risultati di rendering su tela di buona qualità e ridimensionare il rendering quando utilizzi caratteri piccoli.
- Puoi ottenere la risoluzione della tela tramite le proprietà
Colore carattere
Lo stile colore degli elementi rappresenta solo il colore di rendering. Non rappresenta il colore effettivo visto dall'utente.
Poiché questo vale sia per la tela che per l'elemento da cui si ottiene il colore e qualsiasi elemento sopra o sotto la posa, la quantità di lavoro richiesta per abbinare visivamente il colore è enorme e ben oltre lo scopo di una risposta di overflow dello stack (le risposte hanno un lunghezza massima 30K)
Rendering dei caratteri
Il motore di rendering dei caratteri della tela è diverso da quello del DOM. Il DOM può utilizzare una varietà di tecniche di rendering per migliorare la qualità apparente dei caratteri sfruttando il modo in cui sono disposti i sub-pixel RGB fisici del dispositivo. Ad esempio, i caratteri TrueType e i relativi suggerimenti utilizzati dal renderer e il pixel secondario di ClearType derivato con il rendering dei suggerimenti.
Questi metodi di rendering dei caratteri POSSONO essere abbinati nell'area di disegno, anche se per la corrispondenza in tempo reale dovrai utilizzare WebGL.
Il problema è che il rendering dei caratteri DOM è determinato da molti fattori, incluse le impostazioni del browser. JavaScript non può accedere a nessuna delle informazioni necessarie per determinare come viene visualizzato il carattere. Nella migliore delle ipotesi puoi fare un'ipotesi plausibile.
Ulteriori complicazioni
Ci sono anche altri fattori che influenzano il carattere e il modo in cui le regole di stile del carattere CSS si relazionano al risultato visivo del carattere visualizzato. Ad esempio unità CSS, animazione, allineamento, direzione, trasformazioni dei caratteri e modalità quirk.
Personalmente per resa e colore non mi preoccupo. Anche se ho scritto un motore di font completo utilizzando WebGL per abbinare ogni variante di font, filtraggio, composizione e rendering, non fanno parte dello standard e quindi sono soggetti a modifiche senza preavviso. Il progetto sarebbe quindi sempre aperto e potrebbe in qualsiasi momento fallire fino al livello di risultati illeggibili. Semplicemente non ne vale la pena.
Esempio
L'esempio ha una tela di rendering sulla sinistra. Il testo e il carattere al centro in alto. Una vista ingrandita sulla destra, che mostra una vista ingrandita della tela della tela di sinistra
Il primo stile utilizzato è quello predefinito delle pagine. La risoluzione della tela è 300x150 ma ridimensionata per adattarsi a 500x500 pixel CSS. Ciò si traduce in un testo su tela di qualità MOLTO scadente. Il ciclo della risoluzione della tela mostrerà come la risoluzione della tela influisce sulla qualità.
Le funzioni
-
drawText(text, x, y, fontCSS, sizeCSSpx, colorStyleCSS)
disegna il testo utilizzando i valori delle proprietà CSS. Ridimensionare il carattere in modo che corrisponda il più possibile alle dimensioni e alle proporzioni della visualizzazione DOM. -
getFontStyle(element)
restituisce gli stili di carattere necessari come oggetto daelement
Utilizzo dell'interfaccia utente
-
FARE CLIC sul carattere centrale per scorrere gli stili dei caratteri.
-
FAI CLIC sulla tela a sinistra per scorrere le risoluzioni della tela.
-
In basso ci sono le impostazioni utilizzate per rendere il testo nella tela.
Vedrai che la qualità del testo dipende dalla risoluzione della tela.
Per vedere come lo zoom DOM influisce sul rendering, devi ingrandire o ridurre la pagina. I display HiDPI e retina avranno un testo tela di qualità molto inferiore a causa del fatto che la tela ha la metà della risoluzione dei pixel 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>