Pokud chcete jednoduše vykreslit text z vašeho rozsahu na plátno, můžete přistupovat k atributům stylů pomocí funkce window.getComputedStyle. Aby byl původní rozsah neviditelný, nastavte jeho styl na 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>
Odpovídá písmu DOM na plátně?
Jednoduchá odpověď zní:"To je těžké!!" a „Nikdy to nebude dokonalé.“
Nejlepší, co můžete udělat, je přiblížení, které je v příkladu ve spodní části odpovědi, který také ukáže, že shoda viditelného stylu nesouvisí s viditelnou kvalitou.
Rozšiřování pouze z pravidel CSS.
Pokud chcete, aby písmo co nejpřesněji odpovídalo prvku, existují další starosti, než jen získání CSS, jak je uvedeno v odpovědi Spark Fountain.
Velikost písma a velikost pixelů CSS
-
Velikost písma souvisí s velikostí pixelů CSS. HTMLCanvasElement
-
Velikost pixelů CSS nemusí vždy odpovídat pixelům na displeji zařízení. Např. HiDPI/Retina displeje. K poměru pixelů CSS zařízení se dostanete přes
devicePixelRatio
-
Velikost pixelů CSS není konstantní a může se z mnoha důvodů měnit. Změny lze sledovat pomocí
MediaQueryListEvent
a poslechchange
událost -
Prvky lze transformovat.
CanvasRenderingContext2D
nemůže provádět 3D transformace, takže je prvek nebo plátno má 3D transformaci, nebudete moci porovnat písmo vykreslené na plátně s vykresleným písmem prvků. -
Rozlišení plátna a velikost zobrazení jsou nezávislé.
- Rozlišení plátna můžete získat pomocí vlastností
HTMLCanvasElement.width
aHTMLCanvasElement.height
- Velikost zobrazení na plátně můžete získat pomocí vlastností stylu width a height nebo pomocí řady dalších metod, viz příklad.
- Poměr pixelů na plátně se nemusí shodovat s pixely CSS a musí se vypočítat při vykreslování písma na plátně.
- Vykreslování písma na plátně při malých velikostech písma je hrozné. Například 4px písmo vykreslené na velikost 16px je nečitelné.
ctx.font = "4px arial"; ctx.scale(4,4); ctx.fillText("Hello pixels");
Měli byste použít pevnou velikost písma, která poskytuje kvalitní výsledky vykreslování plátna, a při použití malých písem zmenšit vykreslování.
- Rozlišení plátna můžete získat pomocí vlastností
Barva písma
Styl barev prvků představuje pouze vykreslenou barvu. Nepředstavuje skutečnou barvu, jak ji vidí uživatel.
Vzhledem k tomu, že se to týká jak plátna, tak prvku, ze kterého získáváte barvu, a všech prvků nad nebo pod položením, je množství práce potřebné k vizuálnímu sladění barvy obrovské a značně přesahuje rozsah odpovědi přetečení zásobníku (odpovědi mají max. délka 30 kB)
Vykreslování písma
Vykreslovací modul fontu plátna se liší od modulu DOM. DOM může používat různé techniky vykreslování ke zlepšení zdánlivé kvality písem tím, že využije toho, jak jsou fyzické subpixely RGB zařízení uspořádány. Například písma TrueType a související hinting používaný vykreslovacím modulem a odvozený dílčí pixel ClearType s vykreslováním hintingu.
Tyto metody vykreslování písem LZE porovnat na plátně, ačkoli pro shodu v reálném čase budete muset použít WebGL.
Problém je v tom, že vykreslování písem DOM je určeno mnoha faktory, včetně nastavení prohlížeče. JavaScript nemá přístup k žádným informacím potřebným k určení způsobu vykreslení písma. V nejlepším případě můžete kvalifikovaně odhadnout.
Další komplikace
Existují také další faktory, které ovlivňují písmo a jak pravidla stylu písma CSS souvisí s vizuálním výsledkem zobrazeného písma. Například jednotky CSS, animace, zarovnání, směr, transformace písem a režim vtipů.
Osobně si s vykreslováním a barvou hlavu nelámu. Pokud jsem napsal úplný fontový engine pomocí WebGL, aby odpovídal každému fontu, variantě filtrování, skládání a vykreslování, nejsou součástí standardu, a proto se mohou bez upozornění změnit. Projekt by tak byl vždy otevřený a mohl by kdykoli selhat do úrovně nečitelných výsledků. Prostě to nestojí za námahu.
Příklad
V příkladu je vlevo vykreslovací plátno. Text a písmo uprostřed nahoře. Přiblížený pohled vpravo, který ukazuje zvětšený pohled na levé plátno
První použitý styl je výchozí stránka. Rozlišení plátna je 300 x 150, ale je upraveno tak, aby se vešlo na 500 x 500 pixelů CSS. To má za následek VELMI špatnou kvalitu textu na plátně. Cyklováním rozlišení plátna se ukáže, jak rozlišení plátna ovlivňuje kvalitu.
Funkce
-
drawText(text, x, y, fontCSS, sizeCSSpx, colorStyleCSS)
kreslí text pomocí hodnot vlastností CSS. Měřítko písma tak, aby co nejvíce odpovídalo velikosti vizuálu DOM a poměru stran. -
getFontStyle(element)
vrátí potřebné styly písem jako objekt zelement
Použití uživatelského rozhraní
-
KLIKNĚTE na střed písma pro přepínání stylů písma.
-
KLIKNĚTE na levé plátno pro cyklování rozlišení plátna.
-
V dolní části jsou nastavení používaná k vykreslení textu na plátně.
Uvidíte, že kvalita textu závisí na rozlišení plátna.
Chcete-li vidět, jak zoom DOM ovlivňuje vykreslování, musíte stránku přiblížit nebo oddálit. Displeje HiDPI a retina budou mít mnohem nižší kvalitu textu na plátně, protože plátno má poloviční rozlišení než pixely 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>