スパンからテキストをキャンバスにレンダリングするだけの場合は、関数 window.getComputedStyle を使用してスタイリング属性にアクセスできます。元のスパンを非表示にするには、そのスタイルを 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>
キャンバスで DOM フォントを一致させますか?
簡単な答えは、"Way to hard!!" です。 「完璧にはならない」
あなたができる最善のことは、回答の下部にある例にある近似値です。これは、表示されるスタイルの一致が表示される品質とは無関係であることも示しています。
単なる CSS ルールからの拡張
フォントをできるだけ要素に一致させたい場合は、Spark Fountain の回答で指摘されているように CSS を取得するだけでなく、いくつかの追加の懸念事項があります。
フォント サイズと CSS ピクセル サイズ
-
フォント サイズは、CSS のピクセル サイズに関連しています。 HTMLCanvasElement
-
CSS のピクセル サイズは、デバイスの表示ピクセルと常に一致するとは限りません。例:HiDPI/Retina ディスプレイ。
devicePixelRatio
経由でデバイスの CSS ピクセル数にアクセスできます -
CSS のピクセル サイズは一定ではなく、さまざまな理由で変化する可能性があります。変更は
MediaQueryListEvent
で監視できますchange
を聞いて イベント -
要素を変換できます。
CanvasRenderingContext2D
3D 変換を実行できないため、要素またはキャンバスに 3D 変換が含まれている場合、キャンバスにレンダリングされたフォントと要素にレンダリングされたフォントを一致させることはできません。 -
キャンバスの解像度と表示サイズは独立しています。
- プロパティ
HTMLCanvasElement.width
を介してキャンバスの解像度を取得できます 、およびHTMLCanvasElement.height
- キャンバスの表示サイズは、幅と高さのスタイル プロパティ、またはその他のさまざまな方法で取得できます。例を参照してください。
- キャンバス ピクセル アスペクトは CSS ピクセル アスペクトと一致しない場合があり、キャンバスにフォントをレンダリングするときに計算する必要があります。
- 小さなフォント サイズでのキャンバス フォントのレンダリングはひどいものです。たとえば、サイズが 16 ピクセルになるようにレンダリングされた 4 ピクセルのフォントは判読できません。
ctx.font = "4px arial"; ctx.scale(4,4); ctx.fillText("Hello pixels");
キャンバスのレンダリング結果の品質が高い固定フォント サイズを使用し、小さいフォントを使用する場合はレンダリングを縮小する必要があります。
- プロパティ
フォントの色
要素の色スタイルは、レンダリングされた色のみを表します。ユーザーが見た実際の色を表すものではありません。
これは、キャンバスと、色を取得する要素、および上または下にある要素の両方に適用されるため、色を視覚的に一致させるために必要な作業量は膨大で、スタック オーバーフローの回答の範囲をはるかに超えています (回答には最大 30K の長さ)
フォント レンダリング
キャンバスのフォント レンダリング エンジンは、DOM のものとは異なります。 DOM は、さまざまなレンダリング技術を使用して、デバイスの物理的な RGB サブピクセルの配置方法を利用して、フォントの見た目の品質を向上させることができます。たとえば、レンダラーが使用する TrueType フォントと関連するヒンティング、ヒンティング レンダリングを使用した派生 ClearType のサブピクセルなどです。
これらのフォント レンダリング メソッドはキャンバス上で照合できますが、リアルタイムの照合には WebGL を使用する必要があります。
問題は、DOM フォントのレンダリングが、ブラウザーの設定を含む多くの要因によって決定されることです。 JavaScript は、フォントのレンダリング方法を決定するために必要な情報にアクセスできません。せいぜい、知識に基づいた推測を行うことができます.
さらなる合併症
フォントに影響を与える他の要因や、CSS フォント スタイル ルールが表示されるフォントの視覚的な結果にどのように関連するかについても影響があります。たとえば、CSS ユニット、アニメーション、配置、方向、フォント変換、および互換モードなどです。
個人的にはレンダリングと色については気にしません。 WebGL を使用してすべてのフォント、フィルタリング、合成、およびレンダリング バリアントに一致する完全なフォント エンジンを作成した場合でも、それらは標準の一部ではないため、予告なしに変更される可能性があります。したがって、プロジェクトは常に開かれており、いつでも読み取り不能な結果のレベルに失敗する可能性があります。努力する価値がありません。
例
この例では、左側にレンダリング キャンバスがあります。テキストとフォントの中央が上部にあります。右側のズーム ビューは、左側のキャンバス キャンバスのズーム イン ビューを示しています
最初に使用されるスタイルは、ページのデフォルトです。キャンバスの解像度は 300x150 ですが、500 x 500 の CSS ピクセルに合わせてスケーリングされています。これにより、キャンバス テキストの品質が非常に低下します。キャンバスの解像度を循環させると、キャンバスの解像度が品質にどのように影響するかがわかります。
関数
-
drawText(text, x, y, fontCSS, sizeCSSpx, colorStyleCSS)
CSS プロパティ値を使用してテキストを描画します。 DOM の視覚的なサイズと縦横比にできるだけ近く一致するようにフォントをスケーリングします。 -
getFontStyle(element)
必要なフォント スタイルをelement
のオブジェクトとして返します
UI の使用法
-
中央のフォントをクリックして、フォント スタイルを切り替えます。
-
左のキャンバスをクリックして、キャンバスの解像度を切り替えます。
-
下部には、キャンバスにテキストをレンダリングするために使用される設定があります。
テキストの品質はキャンバスの解像度に依存することがわかります。
DOM ズームがレンダリングにどのように影響するかを確認するには、ページをズームインまたはズームアウトする必要があります。 HiDPI および Retina ディスプレイでは、キャンバスが 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>