Jika Anda hanya ingin merender teks dari rentang Anda di kanvas, Anda dapat mengakses atribut gaya menggunakan fungsi window.getComputedStyle. Untuk membuat span asli tidak terlihat, setel gayanya ke 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>
Mencocokkan font DOM di kanvas?
Jawaban sederhananya adalah, "Sangat sulit!!" dan "Itu tidak akan pernah sempurna."
Yang terbaik yang dapat Anda lakukan adalah perkiraan yang ada pada contoh di bagian bawah jawaban yang juga akan menunjukkan kecocokan gaya yang terlihat tidak terkait dengan kualitas yang terlihat.
Memperluas dari hanya aturan CSS.
Jika Anda ingin font cocok sedekat mungkin dengan elemen, ada beberapa masalah tambahan selain mendapatkan CSS seperti yang ditunjukkan dalam jawaban Spark Fountain.
Ukuran font &ukuran piksel CSS
-
Ukuran font terkait dengan ukuran piksel CSS. HTMLCanvasElement
-
Ukuran piksel CSS tidak selalu cocok dengan piksel tampilan perangkat. Misalnya tampilan HiDPI/Retina. Anda dapat mengakses rasio piksel CSS perangkat melalui
devicePixelRatio
-
Ukuran piksel CSS tidak konstan dan dapat berubah karena berbagai alasan. Perubahan dapat dipantau melalui
MediaQueryListEvent
dan mendengarkanchange
acara -
Elemen dapat diubah.
CanvasRenderingContext2D
tidak dapat melakukan transformasi 3D sehingga elemen atau kanvas memiliki transformasi 3D, Anda tidak akan dapat mencocokkan font yang dirender kanvas dengan font yang dirender elemen. -
Resolusi kanvas dan ukuran tampilan bersifat independen.
- Anda bisa mendapatkan resolusi kanvas melalui properti
HTMLCanvasElement.width
, danHTMLCanvasElement.height
- Anda bisa mendapatkan ukuran tampilan kanvas melalui lebar dan tinggi properti gaya, atau melalui berbagai metode lain, lihat contoh.
- Aspek piksel kanvas mungkin tidak cocok dengan aspek piksel CSS dan harus dihitung saat merender font di kanvas.
- Rendering font kanvas pada ukuran font kecil sangat buruk. Misalnya font 4px yang dirender menjadi ukuran 16px tidak dapat dibaca.
ctx.font = "4px arial"; ctx.scale(4,4); ctx.fillText("Hello pixels");
Anda harus menggunakan ukuran font tetap yang memiliki hasil rendering kanvas berkualitas baik dan menurunkan skala rendering saat menggunakan font kecil.
- Anda bisa mendapatkan resolusi kanvas melalui properti
Warna Huruf
Gaya warna elemen hanya mewakili warna yang diberikan. Itu tidak mewakili warna sebenarnya seperti yang dilihat oleh pengguna.
Karena ini berlaku untuk kanvas dan elemen dari mana Anda mendapatkan warna dan elemen peletakan di atas atau di bawah, jumlah pekerjaan yang diperlukan untuk mencocokkan warna secara visual sangat besar dan jauh di luar cakupan jawaban stack overflow (jawaban memiliki panjang maksimal 30K)
Perenderan Fon
Mesin rendering font kanvas berbeda dari DOM. DOM dapat menggunakan berbagai teknik rendering untuk meningkatkan kualitas font yang tampak dengan memanfaatkan bagaimana sub-piksel RGB fisik perangkat diatur. Misalnya font TrueType dan petunjuk terkait yang digunakan oleh perender, dan subpiksel ClearType turunan dengan perenderan petunjuk.
Metode perenderan font ini DAPAT dicocokkan di kanvas, meskipun untuk pencocokan waktu nyata Anda harus menggunakan WebGL.
Masalahnya adalah rendering font DOM ditentukan oleh banyak faktor, termasuk pengaturan browser. JavaScript tidak dapat mengakses informasi apa pun yang diperlukan untuk menentukan bagaimana font dirender. Paling-paling Anda bisa membuat tebakan yang cerdas.
Kerumitan lebih lanjut
Ada juga faktor lain yang mempengaruhi font dan bagaimana aturan gaya font CSS berhubungan dengan hasil visual dari font yang ditampilkan. Misalnya unit CSS, animasi, perataan, arah, transformasi font, dan mode quirks.
Secara pribadi untuk rendering dan warna saya tidak repot. Bahkan jika saya menulis mesin font lengkap menggunakan WebGL untuk mencocokkan setiap font, memfilter, menggabungkan, dan merender varian, mereka bukan bagian dari standar dan karenanya dapat berubah tanpa pemberitahuan. Proyek dengan demikian akan selalu terbuka dan kapan saja dapat gagal ke tingkat hasil yang tidak dapat dibaca. Tidak sepadan dengan usaha.
Contoh
Contoh memiliki kanvas render di sebelah kiri.Teks dan font di tengah atas.Tampilan yang diperbesar di sebelah kanan, yang menunjukkan tampilan yang diperbesar dari kanvas kanvas kiri
Gaya pertama yang digunakan adalah halaman default. Resolusi kanvas adalah 300 kali 150 tetapi diskalakan agar sesuai dengan 500 kali 500 piksel CSS. Ini menghasilkan teks kanvas berkualitas SANGAT buruk. Mengubah resolusi kanvas akan menunjukkan bagaimana resolusi kanvas memengaruhi kualitas.
Fungsinya
-
drawText(text, x, y, fontCSS, sizeCSSpx, colorStyleCSS)
menggambar teks menggunakan nilai properti CSS. Menskalakan font agar sesuai dengan ukuran visual DOM dan rasio tinggi lebar sedekat mungkin. -
getFontStyle(element)
mengembalikan gaya font yang diperlukan sebagai objek darielement
Penggunaan UI
-
KLIK font tengah untuk mengubah gaya font.
-
KLIK kanvas kiri untuk menggilir resolusi kanvas.
-
Di bagian bawah adalah setelan yang digunakan untuk merender teks di kanvas.
Anda akan melihat bahwa kualitas teks bergantung pada resolusi kanvas.
Untuk melihat bagaimana DOM memperbesar efek rendering, Anda harus memperbesar atau memperkecil halaman. Layar HiDPI dan retina akan memiliki kualitas teks kanvas yang jauh lebih rendah karena fakta bahwa kanvas adalah setengah dari resolusi piksel 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>